FreeWRL/FreeX3D  3.0.0
plugin_main.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 
3 
4  FreeWRL plugin for Mozilla compatible browsers.
5  Works in Firefox 1.x - 3.0 on Linux.
6 
7 */
8 
9 /****************************************************************************
10  This file is part of the FreeWRL/FreeX3D Distribution.
11 
12  Copyright 2009 CRC Canada. (http://www.crc.gc.ca)
13 
14  FreeWRL/FreeX3D is free software: you can redistribute it and/or modify
15  it under the terms of the GNU Lesser Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  FreeWRL/FreeX3D is distributed in the hope that it will be useful,
20  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  GNU General Public License for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with FreeWRL/FreeX3D. If not, see <http://www.gnu.org/licenses/>.
26 ****************************************************************************/
27 
28 /*******************************************************************************
29  * Simple LiveConnect Sample Plugin
30  * Copyright (c) 1996 Netscape Communications. All rights reserved.
31  *
32  * Modified by John Stewart - CRC Canada to provide for plugin capabilities
33  * for FreeWRL - an open source VRML and X3D browser.
34  *
35  * Operation:
36  *
37  * In the NPP_Initialize routine, a pipe is created and sent as the window
38  * title to FreeWRL. FreeWRL (OpenGL/OpenGL.xs) looks at this "wintitle",
39  * and if it starts with "pipe:", then treats the number following as
40  * a pipe id to send the window id back through. The pipe is then closed.
41  *
42  * The Plugin uses this window id to rehost the window.
43  *
44  * John Stewart, Alya Khan, Sarah Dumoulin - CRC Canada 2002 - 2006.
45  * Michel Briand - 2009.
46  ******************************************************************************/
47 
48 #include <config.h>
49 #include <system.h>
50 
51 #if defined(HAVE_STDARG_H)
52 # include <stdarg.h>
53 #endif
54 
55 #include <plugin_utils.h>
56 #include <npapi.h>
57 
58 #include <X11/Xlib.h>
59 #include <X11/Intrinsic.h>
60 #include <X11/StringDefs.h>
61 #include <X11/Xatom.h>
62 
63 #ifdef HAVE_NSPR /* for PRBool */
64 #include <prtypes.h>
65 #else
66 /* in nspr as of 2011-07-15, PRBool was just an int; if no nspr then define explicitly */
67 # define PR_TRUE 1
68 # define PR_FALSE 0
69 typedef int PRBool
70 #endif
71 
72 #define PLUGIN_NAME "FreeWRL X3D/VRML"
73 
74 #define BOOL_STR(b) (b ? "TRUE" : "FALSE")
75 
76 #define RECORD_FILE_NAME_IF_NULL \
77  if (me->fName == NULL) { \
78  /* Get the base file name for FreeWRL to run */ \
79  me->fName = (char *) NPN_MemAlloc((strlen(stream->url) +1) *sizeof(char *)); \
80  strcpy(me->fName,stream->url); \
81  PRINT("Can record filename now, name is %s\n", me->fName); \
82  }
83 
84 /* used in init. Don't write to the socket until a request has been received */
85 int gotRequestFromFreeWRL = FALSE;
86 
87 char *paramline[20]; /* parameter line */
88 static void *seqNo = 0;
89 
90 static int PluginVerbose = 1; /* CHECK LOG FILE PATH BEFORE SETTING THIS TO 1 */
91 
92 /*******************************************************************************
93  * Instance state information about the plugin.
94  ******************************************************************************/
95 
96 typedef struct _FW_PluginInstance
97 {
98  int interfaceFile[2];
99  Display *display;
100  int32 x, y;
101  uint32 width, height;
102  Window mozwindow;
103  Window fwwindow;
104  pid_t childPID;
105  char *fName;
106  int freewrl_running;
107  int interfacePipe[2]; /* pipe plugin FROM freewrl */
108  char *cacheFileName;
109  int cacheFileNameLen;
110  FILE *logFile;
111  char *logFileName;
113 
114 typedef void (* Sigfunc) (int);
115 
116 static int np_fileDescriptor;
117 
118 /* Socket file descriptors */
119 #define SOCKET_2 0
120 #define SOCKET_1 1
121 
122 #define PIPE_PLUGINSIDE 0
123 #define PIPE_FREEWRLSIDE 1
124 
125 #if 0
126 static void signalHandler (int);
127 /* Sigfunc signal (int, Sigfunc func); */
128 void freewrlReceive(int fileDescriptor);
129 #endif
130 
131 /* libFreeWRL defines those macros ...
132  we will redefine them here for our own purpose
133 */
134 #undef MALLOC
135 #define MALLOC NPN_MemAlloc
136 #undef FREE
137 #define FREE NPN_MemFree
138 
139 struct timeval mytime;
140 struct timezone tz; /* unused see man gettimeofday */
141 NPStream *currentStream = NULL;
142 
147 static void create_log_file(FW_PluginInstance *me)
148 {
149  FILE *tty;
150  char *logfilename, *hostname, *username;
151  static const char log_file_pat[] = "/tmp/npfreewrl_%s-%s.log";
152 
153  hostname = MALLOC(4096);
154  if (gethostname(hostname, 4096) < 0) {
155  int err = errno;
156  fprintf(stderr, "system error: %s\n", strerror(err));
157  sprintf(hostname, "unknown-host");
158  }
159  username = getenv("LOGNAME");
160  if (!username) {
161  username = getlogin();
162  }
163  if (!username) {
164  int err = errno;
165  fprintf(stderr, "system error: %s\n", strerror(err));
166  username = "unknown-user";
167  }
168 
169  // -4 %s%s
170  // +1 \n
171  logfilename = MALLOC(strlen(log_file_pat)
172  + strlen(hostname) + strlen(username) -4 +1 +4 );
173  sprintf(logfilename, log_file_pat, hostname, username);
174  FREE(hostname);
175  /* do not free username */
176 
177  // Open log file (create it if doesn't exist
178  tty = fopen(logfilename, "a");
179 
180  if (tty == NULL) {
181  fprintf (stderr, "FreeWRL plugin ERROR: plugin could not open log file: %s. Will output to stderr.\n", logfilename);
182  FREE(logfilename);
183  logfilename = NULL;
184  tty = stderr;
185  }
186 
187  me->logFile = tty;
188  me->logFileName = logfilename;
189 }
190 
196 static void print(FW_PluginInstance *me, const char *format, ...)
197 {
198  va_list ap;
199  va_start(ap, format);
200 
201  FILE *tty;
202  double TickTime;
203 
204  if (!PluginVerbose) return;
205 
206  /* Set the timestamp */
207  gettimeofday (&mytime,&tz);
208  TickTime = (double) mytime.tv_sec + (double)mytime.tv_usec/1000000.0;
209 
210  if (!me)
211  tty = stderr;
212  else
213  tty = me->logFile;
214 
215  fprintf(tty, "%f: FreeWRL plugin: ", TickTime);
216  vfprintf(tty, format, ap);
217  fflush(tty);
218 
219  va_end(ap);
220 }
221 
222 #define PRINT(_formargs...) print(me, ##_formargs)
223 
224 #define PRINT_PERROR(_msg) print(me, "system error: %s failed: %s (%d)\n", \
225  _msg, strerror(errno), errno)
226 
227 
228 #if 0
229 Sigfunc signal(int signo, Sigfunc func)
230 {
231  struct sigaction action, old_action;
232 
233  action.sa_handler = func;
234  /*
235  * Initialize action's signal set as empty set
236  * (see man page sigsetops(3)).
237  */
238  sigemptyset(&action.sa_mask);
239 
240  action.sa_flags = 0; /* Is this a good idea??? */
241 
242  /* Add option flags for handling signal: */
243  action.sa_flags |= SA_NOCLDSTOP;
244 #ifdef SA_NOCLDWAIT
245  action.sa_flags |= SA_NOCLDWAIT;
246 #endif
247 
248  if (sigaction(signo, &action, &old_action) < 0) {
249  /* print_here("Call to sigaction failed"); */
250  return(SIG_ERR);
251  }
252  /* Return the old action for the signal or SIG_ERR. */
253  return(old_action.sa_handler);
254 }
255 #endif
256 
257 #if 0
258 void signalHandler(int signo) {
259  /* sprintf(debs, "ACTION signalHandler %d", signo); */
260  /* print_here(debs); */
261 
262  if (signo == SIGIO) {
263  freewrlReceive(np_fileDescriptor);
264 
265  } else {
266  /* Should handle all except the uncatchable ones. */
267  /* print_here("\nClosing plugin log.\n"); */
268  }
269 }
270 
271 void freewrlReceive(int fileDescriptor)
272 {
273  FW_PluginInstance *me = NULL;
274  sigset_t newmask, oldmask;
275 
276  urlRequest request;
277  size_t request_size = 0;
278  NPError rv = 0;
279 
280  sprintf(debs, "Call to freewrlReceive fileDescriptor %d.", fileDescriptor);
281  print_here(debs);
282 
283  bzero(request.url, FILENAME_MAX);
284  request.instance = 0;
285  request.notifyCode = 0; /* not currently used */
286 
287  request_size = sizeof(request);
288 
289  /*
290  * The signal handling code is based on the work of
291  * W. Richard Stevens from Unix Network Programming,
292  * Networking APIs: Sockets and XTI.
293  */
294 
295  /* Init. the signal sets as empty sets. */
296  if (sigemptyset(&newmask) < 0) {
297  print_here("Call to sigemptyset with arg newmask failed");
298  return;
299  }
300 
301  if (sigemptyset(&oldmask) < 0) {
302  print_here("Call to sigemptyset with arg oldmask failed");
303  return;
304  }
305 
306  if (sigaddset(&newmask, SIGIO) < 0) {
307  print_here("Call to sigaddset failed");
308  return;
309  }
310 
311  /* Code to block SIGIO while saving the old signal set. */
312  if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
313  print_here("Call to sigprocmask failed");
314  return;
315  }
316 
317  /* If blocked or interrupted, be silent. */
318  if (read(fileDescriptor, (urlRequest *) &request, request_size) < 0) {
319  if (errno != EINTR && errno != EAGAIN) {
320  print_here("Call to read failed");
321  }
322  /* FreeWRL has died, or THIS IS US WRITING CREATING THAT SIG. */
323  print_here ("freewrlReceive, quick return; either this is us writing or freewrl croaked");
324  return;
325  } else {
326  sprintf (debs, "notifyCode = %d url = %s", request.notifyCode, request.url);
327  print_here(debs);
328 
329  /* signal that we have now received a file request from FreeWRL */
330  gotRequestFromFreeWRL = TRUE;
331 
332  /* is this a getUrl, or a "open new window for url" */
333  if (request.notifyCode == 0) {
334  /* get Url and return it to FreeWRL */
335 
336  seqNo++;
337  /* printf ("request seq %d, url %s\n",seqNo, request.url); */
338 
339  if ((rv = NPN_GetURLNotify(request.instance,
340  request.url, NULL,
341  (void *)(seqNo))) != NPERR_NO_ERROR) {
342  sprintf(debs, "Call to NPN_GetURLNotify failed with error %d.", rv);
343  print_here(debs);
344  }
345 
346 
347  sprintf (debs, "step 2a, NPN_GetURLNotify with request.url %s",request.url);
348  print_here(debs);
349 
350  } else if (request.notifyCode == -99) {
351  /* Firefox, etc took too long. we have timed out. */
352  sprintf (debs,"notifyCode = -99, we have timed out for %s",request.url);
353  print_here(debs);
354  if (currentStream != NULL) {
355  NPN_DestroyStream(request.instance, currentStream, NPRES_USER_BREAK);
356  sprintf (debs, "FreeWRL can not find: %s",request.url);
357  print_here(debs);
358  NPN_Status (request.instance, debs);
359  currentStream = NULL;
360  }
361 
362  } else {
363  /* request.notifyCode must be 1 */
364  sprintf (debs,"NPN_GetStream...");
365  print_here(debs);
366 
367  NPStream* stream;
368  NPError err = NPERR_NO_ERROR;
369  char* myData = "<HTML><B>This is a message from my plug-in!</b></html>";
370  int32 myLength = strlen(myData) + 1;
371  err = NPN_NewStream(request.instance,
372  "text/html",
373  "_AnchorFailsinFreeWRL",
374  &stream);
375  print_here ("NewStream made");
376 
377  err = NPN_Write(request.instance,
378  stream,
379  myLength,
380  myData);
381  print_here ("NPN_Write made");
382  }
383 
384  /* now, put a status line on bottom of browser */
385  sprintf (debs, "FreeWRL loading: %s",request.url);
386  print_here(debs);
387  NPN_Status (request.instance, debs);
388  }
389 
390  /* Restore old signal set, which unblocks SIGIO. */
391  if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
392  print_here("Call to sigprocmask failed");
393  return;
394  }
395 
396  print_here("returning from freewrl_receive");
397  return;
398 }
399 #endif
400 
401 static int init_socket(FW_PluginInstance *me, int fileDescriptor, Boolean nonblock)
402 {
403  int io_flags;
404 
405  if (fcntl(fileDescriptor, F_SETOWN, getpid()) < 0) {
406  PRINT("Call to fcntl with command F_SETOWN failed\n");
407  return(NPERR_GENERIC_ERROR);
408  }
409 
410  if ( (io_flags = fcntl(fileDescriptor, F_GETFL, 0)) < 0 ) {
411  PRINT("Call to fcntl with command F_GETFL failed\n");
412  return(NPERR_GENERIC_ERROR);
413  }
414 
415  /*
416  * O_ASYNC is specific to BSD and Linux.
417  * Use ioctl with FIOASYNC for others.
418  */
419 #ifndef __sgi
420  io_flags |= O_ASYNC;
421 #endif
422 
423  if (nonblock) { io_flags |= O_NONBLOCK; }
424 
425  if ( (io_flags = fcntl(fileDescriptor, F_SETFL, io_flags)) < 0 ) {
426  PRINT("Call to fcntl with command F_SETFL failed\n");
427  return(NPERR_GENERIC_ERROR);
428  }
429  return(NPERR_NO_ERROR);
430 }
431 
432 /* actually run FreeWRL and swallow it, if enough information has been found */
433 int Run (NPP instance)
434 {
435  FW_PluginInstance* me = (FW_PluginInstance *) instance->pdata;
436 
437  char pipetome[25];
438  char childFd[25];
439  char instanceStr[25];
440  int carg = 0;
441 
442  XWindowAttributes mywin;
443  Window child_window = 0;
444 
445  pid_t child;
446  pid_t mine;
447 
448  int secpipe[2];
449 #define clean_pipe { close(secpipe[0]); close(secpipe[1]); }
450 
451  int err;
452  int count;
453  int nbytes;
454 
455  PRINT("Run starts... Checking if can run; disp %u win %u fname %s\n",
456  me->mozwindow, me->display, me->fName);
457 
458  /* Return if we do not have all of the required parameters. */
459  if (me->mozwindow == 0) return FALSE;
460 
461  if (me->fName == NULL) return FALSE;
462 
463  if (me->display == 0) return FALSE;
464 
465  PRINT("Run ... ok\n");
466 
467  /* start FreeWRL, if it is not running already. */
468  if (me->freewrl_running) {
469  PRINT("Run ... FreeWRL already running, returning.\n");
470  return TRUE;
471  }
472 
473  if (pipe(secpipe) < 0) {
474  PRINT_PERROR("pipe");
475  return FALSE;
476  }
477 
478  if (fcntl(secpipe[1], F_SETFD, fcntl(secpipe[1], F_GETFD) | FD_CLOEXEC)) {
479  PRINT_PERROR("fcntl");
480  clean_pipe;
481  return FALSE;
482  }
483 
484  switch ((child = fork())) {
485  case -1:
486  PRINT_PERROR("fork");
487  clean_pipe;
488  return FALSE;
489  case 0:
490  mine = getpid();
491  if (setpgid(mine, mine) < 0) {
492  PRINT_PERROR("setpgid");
493  }
494 
495  /* create pipe string */
496  sprintf(pipetome, "pipe:%d",
497  me->interfacePipe[PIPE_FREEWRLSIDE]);
498 
499  /* child file descriptor - to send requests back here */
500  sprintf(childFd, "%d", me->interfaceFile[SOCKET_2]);
501 
502  /* Instance, so that FreeWRL knows its us... */
503  sprintf(instanceStr, "%lu",
504  (unsigned long int) (uintptr_t) instance);
505 
506  /* Nice FreeWRL to a lower priority */
507  paramline[carg++] = "nice";
508  paramline[carg++] = "freewrl";
509  paramline[carg++] = "--logfile";
510 
511  if (me->logFileName) {
512  paramline[carg++] = me->logFileName;
513  } else {
514  paramline[carg++] = "-"; // no log file (stderr)
515 
516  /* this is usefull to build a test case
517  for child death... because FreeWRL
518  will exit with this param... */
519  /* paramline[carg++] = NULL; */
520  }
521 
522  /* We have the file name, so include it */
523  paramline[carg++] = me->fName;
524 
525  /* Pass in the pipe number so FreeWRL can return the
526  window id */
527  paramline[carg++] = "--plugin";
528  paramline[carg++] = pipetome;
529 
530  /* EAI connection */
531  paramline [carg++] = "--eai";
532 
533  /* File descriptor and instance - allows FreeWRL to
534  request files from browser's cache */
535  paramline[carg++] = "--fd";
536  paramline[carg++] = childFd;
537  paramline[carg++] = "--instance";
538  paramline[carg++] = instanceStr;
539  paramline[carg] = NULL;
540  /* End of arguments */
541 
542  PRINT("exec param line is %s %s %s %s %s %s %s %s %s %s %s\n",
543  paramline[0],paramline[1],paramline[2],paramline[3],
544  paramline[4],paramline[5],paramline[6],paramline[7],
545  paramline[8],paramline[9],paramline[10]);
546 
547  close(secpipe[0]);
548  execvp(paramline[0], paramline);
549  write(secpipe[1], &errno, sizeof(int));
550  _exit(0);
551  break;
552 
553  default:
554  close(secpipe[1]);
555  while ((count = read(secpipe[0], &err, sizeof(errno))) == -1)
556  if (errno != EAGAIN && errno != EINTR) break;
557 
558  if (count) {
559  PRINT_PERROR("execvp");
560  clean_pipe;
561  return FALSE;
562  }
563 
564  close(secpipe[0]);
565 #if 0
566  PRINT("waiting for child...\n");
567  while (waitpid(child, &err, 0) == -1)
568  if (errno != EINTR) {
569  PRINT_PERROR("waitpid");
570  return FALSE;
571  }
572 
573  if (WIFEXITED(err))
574  PRINT("child exited with %d\n", WEXITSTATUS(err));
575 
576  else if (WIFSIGNALED(err))
577  PRINT("child killed by %d\n", WTERMSIG(err));
578 #endif
579  }
580 
581  me->childPID = child;
582  PRINT("CHILD %d\n", me->childPID);
583 
584  PRINT("after FW_Plugin->freewrl_running call - waiting on pipe\n");
585 
586  usleep(1500);
587 
588  nbytes = read(me->interfacePipe[PIPE_PLUGINSIDE], &child_window, sizeof(Window));
589  if ((nbytes < 0) || (nbytes == 0)) {
590  int status = 0;
591  // error reading pipe: child died
592  PRINT("ERROR: child %d FreeWRL program died (%d), waiting...\n",
593  me->childPID, nbytes);
594 
595  switch (waitpid(me->childPID, &status, WNOHANG)) {
596  case 0: PRINT("child is gone (nothing to wait), exit code: %d\n", status);
597  break;
598  case -1: PRINT_PERROR("waitpid");
599  break;
600  default: PRINT("child passed away, exit code: %d\n", status);
601  break;
602  }
603 
604  me->childPID = 0;
605  return FALSE;
606  }
607 
608  PRINT("After exec, and after read from pipe, FW window is %u\n",
609  child_window);
610 
611  me->fwwindow = child_window;
612 
613  PRINT("disp mozwindow height width %u %u %u %u\n",
614  me->display, me->mozwindow, me->width, me->height);
615 
616  /*reparent the window */
617 
618  XGetWindowAttributes(me->display,me->fwwindow, &mywin);
619 
620  PRINT("Plugin: mapped_state %d, IsUnmapped %d, isUnviewable %d isViewable %d\n"
621  "x %d y %d wid %d height %d\n",
622  mywin.map_state, IsUnmapped, IsUnviewable, IsViewable,
623  mywin.x,mywin.y,mywin.width,mywin.height);
624 
625  /* print_here ("going to XFlush"); */
626  /* XFlush(me->display); */
627 
628  /* print_here ("going to XSync"); */
629  /* XSync (me->display, FALSE); */
630 
631  PRINT("Going to resize FreeWRL: %d x %d -> %d x %d\n",
632  mywin.width, mywin.height, me->width, me->height);
633 
634  /* MB 28-12-2009 : added this sync to prevent the plugin from "loosing" the resize event ... */
635  /* XSync (me->display, FALSE); */
636 
637  /* here the two next calls seems to be operating well in any order... hum... */
638  {
639  XSizeHints size_hints;
640  memset(&size_hints, 0, sizeof(size_hints));
641  size_hints.min_width = size_hints.max_width = me->width;
642  size_hints.min_height = size_hints.max_height = me->height;
643  XSetWMNormalHints(me->display, me->fwwindow, &size_hints);
644  }
645  XResizeWindow(me->display, me->fwwindow, me->width, me->height);
646 
647  PRINT("Going to reparent\n");
648  XReparentWindow(me->display, me->fwwindow, me->mozwindow, 0,0);
649 
650  PRINT("Going to remap\n");
651  XMapWindow(me->display,me->fwwindow);
652 
653  XGetWindowAttributes(me->display,me->fwwindow, &mywin);
654 
655  PRINT("Plugin, after reparenting, mapped_state %d, "
656  "IsUnmapped %d, isUnviewable %d isViewable %d\n"
657  "x %d y %d wid %d height %d\n",
658  mywin.map_state, IsUnmapped, IsUnviewable, IsViewable,
659  mywin.x,mywin.y,mywin.width,mywin.height);
660 
661  me->freewrl_running = TRUE;
662 
663  PRINT("Run function finished\n");
664  return TRUE;
665 }
666 
667 
668 /*******************************************************************************
669  ******************************************************************************/
670 #ifdef LEGACY_NPAPI
671 char *
672 #else
673 const char *
674 #endif
675 NPP_GetMIMEDescription(void)
676 {
677  static const char mime_types[] =
678  "x-world/x-vrml:wrl:FreeWRL VRML Browser;"
679  "model/vrml:wrl:FreeWRL VRML Browser;"
680  "model/x3d:x3d:FreeWRL X3D Browser;"
681  "model/x3d+xml:x3d:FreeWRL X3D Browser;"
682  "model/x3d+vrml:x3dv:FreeWRL X3D Browser;"
683  "model/x3d+binary:x3db:FreeWRL X3D Browser"
684  ;
685 
686  print (NULL, "NPP_GetMIMEDescription: %s\n", mime_types);
687  return (char *) mime_types;
688 }
689 
690 NPError
691 NPP_GetValue(NPP instance, NPPVariable variable, void *value)
692 {
693 #define VERSION_DESCRIPTION_SIZE 1024
694  static char version_description[VERSION_DESCRIPTION_SIZE];
695  FW_PluginInstance* me = NULL; /* instance may be NULL */
696  NPError err = NPERR_NO_ERROR;
697 
698  if (!value) return NPERR_GENERIC_ERROR;
699 
700  if (instance)
701  me = (FW_PluginInstance *) instance->pdata;
702 
703  PRINT("NPP_GetValue %u\n", variable);
704 
705  switch (variable) {
706  case NPPVpluginNameString:
707  *((char **)value) = PLUGIN_NAME;
708  break;
709 
710  case NPPVpluginNeedsXEmbed:
711  *((PRBool *)value) = PR_TRUE;
712  break;
713 
714  case NPPVpluginDescriptionString:
715  snprintf(version_description, VERSION_DESCRIPTION_SIZE,
716  "<b>FreeWRL is a VRML/X3D plugin.</b><br>"
717  "Visit us at <a href=\"http://freewrl.sourceforge.net/\">"
718  "http://freewrl.sourceforge.net/</a>.<br>"
719  "Plugin version: <b>%s</b>.<br>"
720  "Build timestamp: <b>%s</b>.<br>",
721  freewrl_plugin_get_version(),
722  BUILD_TIMESTAMP);
723  *((char **)value) = version_description;
724  break;
725 
726  default:
727  err = NPERR_INVALID_PARAM;
728  }
729  return err;
730 }
731 
732 /*******************************************************************************
733  * General Plug-in Calls
734  ******************************************************************************/
735 
736 /*
737 ** NPP_Initialize is called when your DLL is being loaded to do any
738 ** DLL-specific initialization.
739 */
740 NPError NPP_Initialize(void) {
741  /* print_here ("NPP_Initialize"); */
742  return NPERR_NO_ERROR;
743 }
744 
745 #ifdef OJI
746 jref NPP_GetJavaClass( void )
747 {
748  return NULL;
749 }
750 #endif
751 
752 /*
753 ** NPP_Shutdown is called when your DLL is being unloaded to do any
754 ** DLL-specific shut-down. You should be a good citizen and declare that
755 ** you're not using your java class any more. FW_Plugin allows java to unload
756 ** it, freeing up memory.
757 */
758 void NPP_Shutdown(void) {
759  /* print_here ("NPP_Shutdown"); */
760 }
761 
762 /*
763 ** NPP_New is called when your plugin is instantiated (i.e. when an EMBED
764 ** tag appears on a page).
765 */
766 NPError
767 NPP_New(NPMIMEType pluginType,
768  NPP instance,
769  uint16 mode,
770  int16 argc,
771  char* argn[],
772  char* argv[],
773  NPSavedData* saved) {
774 
775  FW_PluginInstance* me = NULL; /* only case */
776  unsigned int err;
777  void *tmp;
778 
779  if( instance == NULL ) {
780  return NPERR_INVALID_INSTANCE_ERROR;
781  }
782 
783  /* Create plugin instance structure */
784  tmp = NPN_MemAlloc(sizeof(FW_PluginInstance));
785  if (!tmp)
786  return NPERR_OUT_OF_MEMORY_ERROR;
787 
788  instance->pdata = tmp;
789  me = (FW_PluginInstance*) tmp;
790  memset(me, 0, sizeof(FW_PluginInstance));
791 
792  /* Create log file */
793  create_log_file(me);
794  PRINT("FreeWRL plugin log restarted. Version: %s. Build: %s\n",
795  freewrl_plugin_get_version(), BUILD_TIMESTAMP);
796 
797  PRINT("NPP_New, argc %d argn %s argv %s\n", argc, argn[0], argv[0]);
798 
799  /* mode is NP_EMBED, NP_FULL, or NP_BACKGROUND (see npapi.h) */
800  switch (mode) {
801  case NP_EMBED: PRINT("NPP_New, mode NP_EMBED\n"); break;
802  case NP_FULL: PRINT("NPP_New, mode NP_FULL\n"); break;
803  default: PRINT("NPP_New, mode UNKNOWN MODE\n"); break;
804  }
805 
806  seqNo = 0;
807  gotRequestFromFreeWRL = FALSE;
808 
809  if (pipe(me->interfacePipe) < 0) {
810  PRINT("Pipe connection to FW_Plugin->interfacePipe failed: %d,%s [%s:%d]\n",
811  errno, strerror(errno), __FILE__,__LINE__);
812  }
813 
814  PRINT("Pipe created, PIPE_FREEWRLSIDE %d PIPE_PLUGINSIDE %d\n",
815  me->interfacePipe[PIPE_FREEWRLSIDE], me->interfacePipe[PIPE_PLUGINSIDE]);
816 
817  /* Assume plugin and FreeWRL child process run on the same machine,
818  then we can use UDP and have incredibly close to 100.00% reliability */
819 
820  if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, me->interfaceFile) < 0) {
821  PRINT("Call to socketpair failed\n");
822  return (NPERR_GENERIC_ERROR);
823  }
824  PRINT("file pair created, SOCKET_1 %d SOCKET_2 %d\n",
825  me->interfaceFile[SOCKET_1], me->interfaceFile[SOCKET_2]);
826 
827  np_fileDescriptor = me->interfaceFile[SOCKET_1];
828 
829 #if 0
830  if (signal(SIGIO, signalHandler) == SIG_ERR) return (NPERR_GENERIC_ERROR);
831  if (signal(SIGBUS, signalHandler) == SIG_ERR) return (NPERR_GENERIC_ERROR);
832 #endif
833 
834  /* prepare communication sockets */
835  if ((err=init_socket(me, me->interfaceFile[SOCKET_2], FALSE))!=NPERR_NO_ERROR)
836  return err;
837  if ((err=init_socket(me, me->interfaceFile[SOCKET_1], TRUE))!=NPERR_NO_ERROR)
838  return err;
839  PRINT("NPP_New returning %d\n", err);
840  return err;
841 }
842 
843 NPError
844 NPP_Destroy(NPP instance, NPSavedData** save)
845 {
846  FW_PluginInstance* me = (FW_PluginInstance*) instance->pdata;
847  int status;
848 
849  /* fprintf(stderr, "NPP_Destroy (%p %p)\n", (void*)instance, (void*)save); */
850 
851  PRINT("NPP_Destroy begin\n");
852 
853  if (instance == NULL)
854  return NPERR_INVALID_INSTANCE_ERROR;
855 
856  if (me != NULL) {
857 
858  if (me->fName != NULL) {
859  NPN_MemFree(me->fName);
860  }
861 
862  if (me->childPID >0) {
863 
864  PRINT("killing command kill %d\n", me->childPID);
865  /* which signal ? TERM, QUIT or KILL */
866  kill(me->childPID, SIGTERM);
867  waitpid(me->childPID, &status, 0);
868  }
869 
870  if (me->cacheFileName != NULL) {
871  NPN_MemFree(me->cacheFileName);
872  }
873 
874  if (me->interfacePipe[PIPE_FREEWRLSIDE] != 0) {
875  close (me->interfacePipe[PIPE_FREEWRLSIDE]);
876  close (me->interfacePipe[PIPE_PLUGINSIDE]);
877  }
878 
879  NPN_MemFree(instance->pdata);
880  instance->pdata = NULL;
881  }
882  me->freewrl_running = FALSE;
883  gotRequestFromFreeWRL = FALSE;
884 
885  PRINT("NPP_Destroy end\n");
886  return NPERR_NO_ERROR;
887 }
888 
889 void
890 NPP_URLNotify (NPP instance, const char *url, NPReason reason, void* notifyData)
891 {
892  FW_PluginInstance* me = (FW_PluginInstance*) instance->pdata;
893 #define returnBadURL "this file is not to be found on the internet"
894  int bytes;
895 
896  PRINT("NPP_URLNotify, url %s reason %d notifyData %p\n",
897  url, reason, notifyData);
898 
899  if (seqNo != notifyData) {
900  PRINT("NPP_URLNotify, expected seq %p, got %p for %s\n",
901  seqNo, notifyData, url);
902  return;
903  }
904 
905  if (reason == NPRES_DONE) {
906  PRINT("NPP_UrlNotify - NPRES_DONE\n");
907  bytes = (strlen(me->cacheFileName)+1)*sizeof(const char *);
908  if (write(me->interfaceFile[SOCKET_1], me->cacheFileName, bytes) < 0) {
909  PRINT("Call to write failed\n");
910  }
911 
912  /* send a "done" message to status bar */
913  NPN_Status(instance,"FreeWRL: Done");
914  return;
915 
916  } else if (reason == NPRES_USER_BREAK) {
917  PRINT("NPP_UrlNotify - NPRES_USER_BREAK\n");
918  } else if (reason == NPRES_NETWORK_ERR) {
919  PRINT("NPP_UrlNotify - NPRES_NETWORK_ERR\n");
920  } else {
921  PRINT("NPP_UrlNotify - unknown\n");
922  }
923 
924  PRINT("NPP_UrlNotify - writing %s (%u bytes) to socket %d\n",
925  returnBadURL, strlen(returnBadURL), me->interfaceFile[SOCKET_1]);
926 
927  NPN_Status(instance,"FreeWRL: NPP_URLNotify failed");
928 
929  /* if we got a request from FreeWRL for the file, then return
930  the name. If FreeWRL was "Run", from within NPP_NewStream,
931  it will not be expecting this write, until it asks for a
932  file - a case of "the cart before the horse" */
933 
934  if (gotRequestFromFreeWRL) {
935  PRINT("NPP_UrlNotify, gotRequestFromFreeWRL - writing data\n");
936  if (write(me->interfaceFile[SOCKET_1], returnBadURL,
937  strlen(returnBadURL)) < 0) {
938  PRINT("Call to write failed\n");
939  }
940  } else {
941  PRINT("call to write (for returnBadURL) skipped, because gotRequestFromFreeWRL = FALSE\n");
942  }
943 }
944 
945 
946 NPError
947 NPP_SetWindow(NPP instance, NPWindow *browser_window)
948 {
949  FW_PluginInstance* me = (FW_PluginInstance*) instance->pdata;
950  NPError result = NPERR_NO_ERROR;
951 
952  PRINT("start of NPP_SetWindow\n");
953 
954  if (instance == NULL)
955  return NPERR_INVALID_INSTANCE_ERROR;
956 
957  /* do we have a file name yet? */
958  PRINT("file name in SetWindow is %s\n", me->fName);
959 
960  /* set the display, if we know it yet */
961  if (!me->display) {
962  if ((NPSetWindowCallbackStruct *)(browser_window->ws_info) != NULL) {
963  me->display = ((NPSetWindowCallbackStruct *)
964  browser_window->ws_info)->display;
965 
966  PRINT("NPP_SetWindow, plugin display now is %u\n", me->display);
967  }
968  }
969 
970  /* verify that the display has not changed */
971  if ((NPSetWindowCallbackStruct *)(browser_window->ws_info) != NULL) {
972  if ((me->display) != ((NPSetWindowCallbackStruct *)
973  browser_window->ws_info)->display) {
974 
975  PRINT("HMMM - display has changed\n");
976  me->display = ((NPSetWindowCallbackStruct *)
977  browser_window->ws_info)->display;
978  }
979  }
980 
981  PRINT("NPP_SetWindow, moz window is %u childPID is %u\n",
982  browser_window->window, me->childPID);
983 
984  me->width = browser_window->width;
985  me->height = browser_window->height;
986 
987 
988  if (me->mozwindow != (Window) browser_window->window) {
989  me->mozwindow = (Window) browser_window->window;
990 
991  /* run FreeWRL, if it is not already running. It might not be... */
992  if (!me->freewrl_running) {
993 
994  PRINT("NPP_SetWindow, running FreeWRL here!\n");
995 
996  if (!Run(instance)) {
997  PRINT("NPP_SetWindow, FreeWRL program failed!\n");
998  return NPERR_MODULE_LOAD_FAILED_ERROR;
999  }
1000 
1001  PRINT("NPP_SetWindow, returned from Run!\n");
1002  }
1003  }
1004 
1005  /* Handle the FreeWRL window */
1006  if (me->fwwindow) {
1007  PRINT("xresize x %d y %d wid %d hei %d\n",
1008  me->x, me->y, me->width, me->height);
1009 
1010  XResizeWindow(me->display, me->fwwindow,
1011  me->width, me->height);
1012 
1013  XSync (me->display,FALSE);
1014  }
1015  PRINT("exiting NPP_SetWindow\n");
1016  return result;
1017 }
1018 
1019 NPError
1020 NPP_NewStream(NPP instance,
1021  NPMIMEType type,
1022  NPStream *stream,
1023  NPBool seekable,
1024  uint16 *stype)
1025 {
1026  FW_PluginInstance* me = (FW_PluginInstance*) instance->pdata;
1027 
1028  if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR;
1029 
1030  if (stream->url == NULL) return(NPERR_NO_DATA);
1031 
1032  if (currentStream == NULL) {
1033  currentStream = stream;
1034  } else {
1035  PRINT("NPP_NewStream, currentstream NOT NULL\n");
1036  }
1037 
1038  PRINT("NPP_NewStream, filename %s instance %p, type %s, "
1039  "stream %p, seekable %s stype %d\n",
1040  me->fName, instance, type,
1041  stream, BOOL_STR(seekable), (stype ? (*stype) : 0));
1042 
1043  RECORD_FILE_NAME_IF_NULL;
1044 
1045  /* run FreeWRL, if it is not already running. It might not be... */
1046  if (!me->freewrl_running) {
1047 
1048  PRINT("NPP_NewStream, running FreeWRL here!\n");
1049 
1050  if (!Run(instance)) {
1051  PRINT("NPP_NewStream, FreeWRL program failed!\n");
1052  return NPERR_MODULE_LOAD_FAILED_ERROR;
1053  }
1054  }
1055 
1056  /* Lets tell netscape to save this to a file. */
1057  *stype = NP_ASFILEONLY;
1058  seekable = FALSE;
1059 
1060  PRINT("NPP_NewStream returning noerror\n");
1061  return NPERR_NO_ERROR;
1062 }
1063 
1064 
1065 /* PLUGIN DEVELOPERS:
1066  * These next 2 functions are directly relevant in a plug-in which
1067  * handles the data in a streaming manner. If you want zero bytes
1068  * because no buffer space is YET available, return 0. As long as
1069  * the stream has not been written to the plugin, Navigator will
1070  * continue trying to send bytes. If the plugin doesn't want them,
1071  * just return some large number from NPP_WriteReady(), and
1072  * ignore them in NPP_Write(). For a NP_ASFILE stream, they are
1073  * still called but can safely be ignored using this strategy.
1074  */
1075 
1076 int32 STREAMBUFSIZE = 0X0FFFFFFF; /* If we are reading from a file in NPAsFile
1077  * mode so we can take any size stream in our
1078  * write call (since we ignore it) */
1079 
1080 int32
1081 NPP_WriteReady(NPP instance, NPStream *stream)
1082 {
1083  FW_PluginInstance* me = (FW_PluginInstance *) instance->pdata;
1084  PRINT("NPP_WriteReady\n");
1085  /* Number of bytes ready to accept in NPP_Write() */
1086  return STREAMBUFSIZE;
1087 }
1088 
1089 
1090 int32 NPP_Write(NPP instance, NPStream *stream, int32 offset, int32 len, void *buffer)
1091 {
1092  FW_PluginInstance* me = (FW_PluginInstance *) instance->pdata;
1093  PRINT("NPP_Write\n");
1094  return 0;
1095  return len; /* The number of bytes accepted */
1096 }
1097 
1098 
1099 NPError
1100 NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason)
1101 {
1102  FW_PluginInstance* me = (FW_PluginInstance *) instance->pdata;
1103 
1104  PRINT("NPP_DestroyStream, instance %p stream %p\n",
1105  instance, stream);
1106 
1107  if (reason == NPRES_DONE) PRINT("reason: NPRES_DONE\n");
1108  if (reason == NPRES_USER_BREAK) PRINT("reason: NPRES_USER_BREAK\n");
1109  if (reason == NPRES_NETWORK_ERR) PRINT("reason: NPRES_NETWORK_ERR\n");
1110 
1111  if (stream == currentStream) {
1112  currentStream = NULL;
1113  } else {
1114  PRINT("NPP_DestroyStream, STREAMS DO NOT MATCH!\n");
1115  }
1116 
1117  if (instance == NULL)
1118  return NPERR_INVALID_INSTANCE_ERROR;
1119  return NPERR_NO_ERROR;
1120 }
1121 
1122 
1123 void
1124 NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname)
1125 {
1126  FW_PluginInstance* me = (FW_PluginInstance *) instance->pdata;
1127  int bytes;
1128 
1129  PRINT("NPP_StreamAsFile, start with fname %s\n", fname);
1130 
1131  if (instance != NULL) {
1132  me = (FW_PluginInstance*) instance->pdata;
1133 
1134  RECORD_FILE_NAME_IF_NULL;
1135 
1136  if (!me->freewrl_running) {
1137  /* if we are not running yet, see if we have enough to start. */
1138  if (!Run(instance)) {
1139  PRINT("NPP_StreamAsFile, FreeWRL program failed!\n");
1140  /* TODO: clean-up */
1141  return;
1142  }
1143 
1144  } else {
1145  if (fname == NULL) {
1146  PRINT("NPP_StreamAsFile has a NULL file\n");
1147 
1148  /* Try sending an empty string */
1149  if (write(me->interfaceFile[SOCKET_1], "", 1) < 0) {
1150  PRINT("Call to write failed\n");
1151  }
1152  } else {
1153 
1154  /* if we got a request from FreeWRL
1155  for the file, then return the
1156  name. If FreeWRL was "Run", from
1157  within NPP_NewStream, it will not
1158  be expecting this write, until it
1159  asks for a file - a case of "the
1160  cart before the horse" */
1161 
1162  if (gotRequestFromFreeWRL) {
1163  bytes = (strlen(fname)+1)*sizeof(const char *);
1164  if (bytes > (me->cacheFileNameLen -10)) {
1165  if (me->cacheFileName != NULL) {
1166  NPN_MemFree(me->cacheFileName);
1167  }
1168 
1169  me->cacheFileNameLen = bytes+20;
1170  me->cacheFileName = NPN_MemAlloc(me->cacheFileNameLen);
1171  }
1172 
1173  memcpy (me->cacheFileName, fname, bytes);
1174  PRINT("NPP_StreamAsFile: saving name to cachename\n");
1175  } else {
1176  PRINT("NPP_StreamAsFile: skipping file write, as gotRequestFromFreeWRL = FALSE\n");
1177  }
1178  }
1179  }
1180  }
1181 }
1182 
1183 
1184 void
1185 NPP_Print(NPP instance, NPPrint* printInfo)
1186 {
1187  if(printInfo == NULL)
1188  return;
1189 
1190  if (instance != NULL) {
1191 
1192  if (printInfo->mode == NP_FULL) {
1193  /*
1194  * PLUGIN DEVELOPERS:
1195  * If your plugin would like to take over
1196  * printing completely when it is in full-screen mode,
1197  * set printInfo->pluginPrinted to TRUE and print your
1198  * plugin as you see fit. If your plugin wants Netscape
1199  * to handle printing in this case, set
1200  * printInfo->pluginPrinted to FALSE (the default) and
1201  * do nothing. If you do want to handle printing
1202  * yourself, printOne is true if the print button
1203  * (as opposed to the print menu) was clicked.
1204  * On the Macintosh, platformPrint is a THPrint; on
1205  * Windows, platformPrint is a structure
1206  * (defined in npapi.h) containing the printer name, port,
1207  * etc.
1208  */
1209 
1210  /* void* platformPrint = */
1211  /* printInfo->print.fullPrint.platformPrint; */
1212  /* NPBool printOne = */
1213  /* printInfo->print.fullPrint.printOne; */
1214 
1215  /* Do the default*/
1216  printInfo->print.fullPrint.pluginPrinted = FALSE;
1217  }
1218  else { /* If not fullscreen, we must be embedded */
1219  /* NPWindow* printWindow = */
1220  /* &(printInfo->print.embedPrint.window); */
1221  /* void* platformPrint = */
1222  /* printInfo->print.embedPrint.platformPrint; */
1223  }
1224  }
1225 }
1226 
1227 /* Local Variables: */
1228 /* c-basic-offset: 8 */
1229 /* End: */
Definition: npapi.h:148