"Fossies" - the Fresh Open Source Software Archive

Member "mozplugger-2.1.6/mozplugger.c" (17 Apr 2014, 84940 Bytes) of package /linux/www/old/mozplugger-2.1.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /**
    2  * This file is part of mozplugger a fork of plugger, for list of developers
    3  * see the README file.
    4  *
    5  * This program is free software; you can redistribute it and/or modify
    6  * it under the terms of the GNU General Public License as published by
    7  * the Free Software Foundation; either version 2 of the License, or
    8  * (at your option) any later version.
    9  *
   10  * This program is distributed in the hope that it will be useful,
   11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13  * GNU General Public License for more details.
   14  *
   15  * You should have received a copy of the GNU General Public License
   16  * along with this program; if not, write to the Free Software
   17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
   18  */
   19 
   20 #ifdef HAVE_CONFIG_H
   21 #include "config.h"
   22 #endif
   23 
   24 #include <unistd.h>
   25 #include <ctype.h>
   26 #include <string.h>
   27 #include <stdio.h>
   28 #include <stdlib.h>
   29 #include <sysexits.h>
   30 #include <signal.h>
   31 #include <fcntl.h>
   32 #include <sys/stat.h>
   33 #include <sys/wait.h>
   34 #include <sys/socket.h>
   35 #include <errno.h>
   36 #include <stdarg.h>
   37 #include <time.h>
   38 #include <utime.h>
   39 
   40 #include <X11/Xlib.h>
   41 #include <X11/Xutil.h>
   42 
   43 #ifdef HAVE_GETPWUID
   44 #include <pwd.h> /* For alternative way to find HOME dir */
   45 #endif
   46 
   47 #define XP_UNIX
   48 #include "npapi.h"
   49 #include "npruntime.h"
   50 #include "npn_func_tab.h"
   51 #include "npp_func_tab.h"
   52 #include "npn_funcs.h"
   53 #include "npp_funcs.h"
   54 #include "np_funcs.h"
   55 
   56 #include "mozplugger.h"
   57 #include "debug.h"
   58 #include "npn-get-helpers.h"
   59 #include "cmd_flags.h"
   60 #include "scriptable_obj.h"
   61 #include "pipe_msg.h"
   62 
   63 #ifndef __GNUC__
   64 #define __inline
   65 #endif
   66 
   67 #define AUTO_UPDATE
   68 #define CHUNK_SIZE (8192)
   69 
   70 /**
   71  * Element of linked list of commands created when parsing config file
   72  */
   73 typedef struct command
   74 {
   75      int flags;
   76      const char * cmd;
   77      const char * winname;
   78      const char * fmatchStr;
   79 
   80      struct command * pNext;
   81 } command_t;
   82 
   83 
   84 /**
   85  * Element of fixed size array created when parsing NPP_New call
   86  */
   87 typedef struct argument
   88 {
   89      char *name;
   90      char *value;
   91 } argument_t;
   92 
   93 /**
   94  * Data associated with an instance of an embed object, can be more than
   95  * one
   96  */
   97 typedef struct data
   98 {
   99      Display * display;
  100      Window window;
  101      uint32_t width;
  102      uint32_t height;
  103      pid_t pid;
  104      int commsPipeFd;
  105      int repeats;
  106      command_t * command;     /**< command to execute */
  107      unsigned int mode_flags; /**< flags associated with browser calls */
  108      char *mimetype;
  109      char *href;               /**< If QT this is set to handle special case */
  110      char *url;                /**< The URL */
  111      char browserCantHandleIt; /**< Is set if browser cant handle protocol */
  112      char *urlFragment;
  113 
  114      int tmpFileFd;     /**< File descriptor of temp file */
  115      const char * tmpFileName; /**< Name of the temp file */
  116      int tmpFileSize;   /**< Size of temp file so far */
  117 
  118      char autostart;
  119      char autostartNotSeen;
  120      int num_arguments;
  121      struct argument *args;
  122 } data_t;
  123 
  124 /**
  125  * Element of linked list of mimetypes created when parsing config file
  126  */
  127 typedef struct mimetype
  128 {
  129      const char * type;
  130 
  131      struct mimetype * pNext;
  132 } mimetype_t;
  133 
  134 /**
  135  * Element of linked list of handlers created when parsing config file
  136  */
  137 typedef struct handle
  138 {
  139      mimetype_t * types;
  140      command_t * cmds;
  141 
  142      struct handle * pNext;
  143 } handler_t;
  144 
  145 /**
  146  * Global variables
  147  */
  148 
  149 static char errMsg[512] = {0};
  150 static handler_t * g_handlers = 0;
  151 
  152 static const char * g_pluginName = "MozPlugger dummy Plugin";
  153 static const char * g_version = VERSION;
  154 static const char * g_linker = NULL;
  155 static const char * g_controller = NULL;
  156 static const char * g_helper = NULL;
  157 
  158 static char staticPool[MAX_STATIC_MEMORY_POOL];
  159 static int staticPoolIdx = 0;
  160 
  161 /**
  162  * Wrapper for putenv(). Instead of writing to the envirnoment, the envirnoment
  163  * variables are written to a buffer.
  164  *
  165  * @param[in,out] buffer The buffer where the environment variables are written
  166  * @param[in] bufLen The length of the buffer
  167  * @param[in] offset The current position in the buffer
  168  * @param[in] var The name of the environment variable
  169  * @param[in] value The value of the environment variable
  170  *
  171  * @return The new offset
  172  */
  173 static int my_putenv(char *buffer, int bufLen, int offset, const char *var,
  174                                                  const char *value)
  175 {
  176      if(value)
  177      {
  178           const int len = strlen(var) + strlen(value) + 2;
  179           if (offset + len >= bufLen)
  180           {
  181                D("Buffer overflow in putenv(%s=%s) offset=%i, bufLen=%i\n",
  182                                                    var, value, offset, bufLen);
  183           }
  184           else
  185           {
  186                snprintf(&buffer[offset], len, "%s=%s", var, value);
  187                putenv(&buffer[offset]);
  188                offset += len;
  189           }
  190      }
  191      else
  192      {
  193           D("putenv did nothing, no value for %s\n", var);
  194      }
  195      return offset;
  196 }
  197 
  198 /**
  199  * putenv with a unsigned value
  200  *
  201  * @param[in,out] buffer The buffer where the environment variables are written
  202  * @param[in] bufLen The length of the buffer
  203  * @param[in] offset The current position in the buffer
  204  * @param[in] var The name of the environment variable
  205  * @param[in] value The value of the environment variable
  206  *
  207  * @return The new offset
  208  */
  209 static int my_putenv_unsigned(char *buffer, int bufLen, int offset,
  210                                           const char *var, unsigned long value)
  211 {
  212      char temp[50];
  213      snprintf(temp, sizeof(temp), "%lu", value);
  214      return my_putenv(buffer, bufLen, offset, var, temp);
  215 }
  216 
  217 /**
  218  * putenv with a hex value
  219  *
  220  * @param[in,out] buffer The buffer where the environment variables are written
  221  * @param[in] bufLen The length of the buffer
  222  * @param[in] offset The current position in the buffer
  223  * @param[in] var The name of the environment variable
  224  * @param[in] value The value of the environment variable
  225  *
  226  * @return The new offset
  227  */
  228 static int my_putenv_hex(char *buffer, int bufLen, int offset,
  229                                           const char *var, unsigned long value)
  230 {
  231      char temp[50];
  232      snprintf(temp, sizeof(temp), "0x%lx", value);
  233      return my_putenv(buffer, bufLen, offset, var, temp);
  234 }
  235 
  236 /**
  237  * putenv with a string
  238  *
  239  * @param[in,out] buffer The buffer where the environment variables are written
  240  * @param[in] bufLen The length of the buffer
  241  * @param[in] offset The current position in the buffer
  242  * @param[in] var The name of the environment variable
  243  * @param[in] value The value of the environment variable
  244  *
  245  * @return The new offset
  246  */
  247 static int my_putenv_signed(char *buffer, int bufLen, int offset,
  248                                                    const char *var, long value)
  249 {
  250      char temp[50];
  251      snprintf(temp, sizeof(temp), "%ld", value);
  252      return my_putenv(buffer, bufLen, offset, var, temp);
  253 }
  254 
  255 /**
  256  * Report the error
  257  *
  258  * @param[in] fmt The format string
  259  */
  260 static void reportError(NPP instance, const char * fmt, ...)
  261 {
  262      va_list ap;
  263      va_start(ap, fmt);
  264 
  265      vsnprintf(errMsg, sizeof(errMsg), fmt, ap);
  266      va_end(ap);
  267 
  268      if(instance)
  269      {
  270           NPN_Status(instance, errMsg);
  271      }
  272      fprintf(stderr, "%s\n",errMsg);
  273      D("%s\n", errMsg);
  274 }
  275 
  276 static void clearError(void)
  277 {
  278      errMsg[0] = 0;
  279 }
  280 
  281 static bool haveError(void)
  282 {
  283      return errMsg[0] != 0;
  284 }
  285 
  286 /**
  287  * Allocate some memory from the static pool. We use a static pool for
  288  * the database because Mozilla can unload the plugin after use and this
  289  * would lead to memory leaks if we use the heap.
  290  *
  291  * @param[in] size The size of the memory to allocate
  292  *
  293  * @return Pointer to the memory allocated or NULL
  294  */
  295 static void * allocStaticMem(int size)
  296 {
  297      void * retVal = NULL;
  298      const int newIdx = staticPoolIdx + size;
  299 
  300      if(newIdx > MAX_STATIC_MEMORY_POOL)
  301      {
  302           reportError(NULL, "MozPlugger: config file is too big - delete some handlers/commands or mimetypes");
  303      }
  304      else
  305      {
  306           retVal = &staticPool[staticPoolIdx];
  307           staticPoolIdx = newIdx;
  308      }
  309      return retVal;
  310 }
  311 
  312 /**
  313  * Make a dynamic string static by copying to static memory.
  314  * Given a pointer to a string in temporary memory, return the same string
  315  * but this time stored in permanent (i.e. static) memory. Will only be deleted
  316  * when the plugin is unloaded by Mozilla.
  317  *
  318  * @param[in] str Pointer to the string
  319  * @param[in] len The length of the string
  320  *
  321  * @return Pointer to the copied string
  322  */
  323 static const char * makeStrStatic(const char * str, int len)
  324 {
  325      /* plus one for string terminator */
  326      char * const buf = allocStaticMem(len + 1);
  327 
  328      if(buf)
  329      {
  330           strncpy(buf, str, len);
  331           buf[len] = '\0';
  332      }
  333      return buf;
  334 }
  335 
  336 /**
  337  * Search backwards from end of string buffer until first non-white space
  338  * and then terminate string after that point to perform a trim.
  339  *
  340  * @param[in,out] buf The string to be trimmed.
  341  */
  342 static void trim_trailing_spaces(char * buf)
  343 {
  344      char * end = &buf[strlen(buf)-1];
  345      for( ;end >= buf; end--)
  346      {
  347           if((*end != '\r') && (*end != '\n') && (*end != '\t') && (*end != ' '))
  348           {
  349                end[1] = '\0';
  350                return;
  351           }
  352      }
  353 }
  354 
  355 /**
  356  * Trim the line buffer and if the line is empty or contains just a comment
  357  * return false
  358  *
  359  * @param[in] buffer The line read
  360  *
  361  * @return true if not a blank line
  362  */
  363 static bool chkCfgLine(char * buffer)
  364 {
  365      if(buffer[0] != '#')
  366      {
  367           trim_trailing_spaces(buffer);
  368           return true;
  369      }
  370      return false;
  371 }
  372 
  373 static bool is_base_mozplugger(const char * magic)
  374 {
  375      return (magic[0] == '-');
  376 }
  377 
  378 /**
  379  * Get the home directory
  380  *
  381  * @return a pointer to const string that contains home directory
  382  */
  383 static const char * get_home_dir(void)
  384 {
  385      const char * home = getenv("HOME");
  386 #ifdef HAVE_GETPWUID
  387      if(home == NULL)
  388      {
  389           struct passwd * pw = getpwuid(getuid());
  390           home = pw->pw_dir;
  391      }
  392 #endif
  393      return home;
  394 }
  395 
  396 /**
  397  * Get the prefix to the path to the config files based on the magic
  398  *
  399  * @param[in] magic references for this particular plugin type
  400  * @param[out] buf The buffer to put the prefix
  401  * @param[in] bufLen The length of the buffer
  402  *
  403  * @return the sizeof the resulting string
  404  */
  405 static int get_cfg_path_prefix(const char * magic, char * buf, int bufLen)
  406 {
  407      const char * fmt;
  408      int prefixLen = 1;
  409      const char * home;
  410 
  411      if(is_base_mozplugger(magic))
  412      {
  413           magic = "0";
  414      }
  415      else
  416      {
  417           prefixLen = strchr(magic, ':') - magic;
  418      }
  419 
  420      /* Locations are ...
  421       * $MOZPLUGGER_HOME/.cache/
  422       * $XDG_CACHE_HOME/mozplugger/
  423       * $HOME/.cache/mozplugger/
  424       */
  425 
  426      if( (home = getenv("MOZPLUGGER_HOME")) != NULL)
  427      {
  428           fmt = "%s/.cache/%.*s";
  429      }
  430      else if( (home = getenv("XDG_CACHE_HOME")) != NULL)
  431      {
  432           fmt = "%s/mozplugger/%.*s";
  433      }
  434      else if( (home = get_home_dir()) != NULL)
  435      {
  436           fmt = "%s/.cache/mozplugger/%.*s";
  437      }
  438      else
  439      {
  440           reportError(NULL, "Mozplugger cannot determine HOME directory\n");
  441           *buf = '\0';
  442           return 0;
  443      }
  444      return snprintf(buf, bufLen, fmt, home, prefixLen, magic);
  445 }
  446 
  447 /**
  448  * Get the paths to the mozplugger helpers.
  449  *
  450  * @param[in] magic references for this particular plugin type
  451  *
  452  * @return None
  453  */
  454 static void get_helper_paths(const char * magic)
  455 {
  456      char fname[200];
  457      FILE * fp;
  458      int n;
  459 
  460      if(g_controller || g_linker || g_helper)
  461          return;
  462 
  463      n = get_cfg_path_prefix(magic, fname, sizeof(fname));
  464      strncat(fname, ".helpers", sizeof(fname) - n);
  465 
  466      if((fp = fopen(fname, "rb")) != NULL)
  467      {
  468           char buffer[512];
  469           while(fgets(buffer, sizeof(buffer), fp))
  470           {
  471                if(chkCfgLine(buffer))
  472                {
  473                     char * sep = strchr(buffer, '\t');
  474                     int pathLen = strlen(&sep[1]);
  475                     *sep = '\0';
  476                     if(strcmp(buffer, "linker") == 0)
  477                     {
  478                          g_linker = makeStrStatic(&sep[1], pathLen);
  479                     }
  480                     else if(strcmp(buffer, "controller") == 0)
  481                     {
  482                          g_controller = makeStrStatic(&sep[1], pathLen);
  483                     }
  484                     else if(strcmp(buffer, "version") == 0)
  485                     {
  486                          g_version = makeStrStatic(&sep[1], pathLen);
  487                     }
  488                     else if(strcmp(buffer, "name") == 0)
  489                     {
  490                          g_pluginName = makeStrStatic(&sep[1], pathLen);
  491                     }
  492                     else if(strcmp(buffer, "helper") == 0)
  493                     {
  494                          g_helper = makeStrStatic(&sep[1], pathLen);
  495                     }
  496                }
  497           }
  498           fclose(fp);
  499      }
  500 }
  501 
  502 /**
  503  * Get the path to the configuration file.
  504  *
  505  * @param[in] magic references for this particular plugin type
  506  *
  507  * @return The full path to the configuration file
  508  */
  509 static char * get_cmds_cfg_path(const char * magic)
  510 {
  511      char fname[200];
  512 
  513      int n = get_cfg_path_prefix(magic, fname, sizeof(fname));
  514      strncat(fname, ".cmds", sizeof(fname) - n);
  515 
  516      return strdup(fname);
  517 }
  518 
  519 /**
  520  * Get the path to the mimetypes file.
  521  *
  522  * @param[in] magic references for this particular plugin type
  523  *
  524  * @return The full path to the configuration file
  525  */
  526 static char * get_mimetypes_cfg_path(const char * magic)
  527 {
  528      char fname[200];
  529 
  530      int n = get_cfg_path_prefix(magic, fname, sizeof(fname));
  531      strncat(fname, ".mimetypes", sizeof(fname) - n);
  532 
  533      return strdup(fname);
  534 }
  535 
  536 
  537 /**
  538  * Wrapper for execlp() that calls the helper.
  539  *
  540  * WARNING: This function runs in the daughter process so one must assume the
  541  * daughter uses a copy (including) heap memory of the parent's memory space
  542  * i.e. any write to memory here does not affect the parent memory space.
  543  * Since Linux uses copy-on-write, best leave memory read-only and once execlp
  544  * is called, all daughter memory is wiped anyway (except the stack).
  545  *
  546  * @param[in] THIS Pointer to the data associated with this instance of the
  547  *                     plugin
  548  * @param[in] file The url of the embedded object
  549  * @param[in] pipeFd The file descriptor of the pipe to the helper application
  550  */
  551 static void run(data_t * const THIS, const char *file, int pipeFd)
  552 {
  553      char buffer[ENV_BUFFER_SIZE];
  554      int offset = 0;
  555      int i;
  556      unsigned int flags = THIS->command->flags;
  557      int autostart = THIS->autostart;
  558      const char * launcher = NULL;
  559      const char * nextHelper = NULL;
  560 
  561      /* If there is no window to draw the controls in then
  562       * dont use controls -> mozdev bug #18837 */
  563      if((THIS->window == 0) &&  ((flags & (H_CONTROLS | H_LINKS)) != 0) )
  564      {
  565           D("Cannot use controls or link button as no window to draw"
  566                                                               " controls in\n");
  567       flags &= ~(H_CONTROLS | H_LINKS);
  568      }
  569 
  570      /* If no autostart seen and using controls dont autostart by default */
  571      if ((flags & (H_CONTROLS | H_LINKS)) && (THIS->autostartNotSeen))
  572      {
  573           autostart = 0;
  574      }
  575 
  576      snprintf(buffer, sizeof(buffer), "%d,%d,%d,%lu,%d,%d",
  577           flags,
  578           THIS->repeats,
  579           pipeFd,
  580           (unsigned long int) THIS->window,
  581           (int) THIS->width,
  582           (int) THIS->height);
  583 
  584      offset = strlen(buffer)+1;
  585 
  586      offset = my_putenv_unsigned(buffer, sizeof(buffer), offset,
  587                                                         "window", THIS->window);
  588 
  589      offset = my_putenv_hex(buffer, sizeof(buffer), offset,
  590                                                      "hexwindow", THIS->window);
  591 
  592      offset = my_putenv_signed(buffer, sizeof(buffer), offset,
  593                                                       "repeats", THIS->repeats);
  594 
  595      offset = my_putenv_unsigned(buffer, sizeof(buffer), offset,
  596                                                           "width", THIS->width);
  597 
  598      offset = my_putenv_unsigned(buffer, sizeof(buffer), offset,
  599                                                         "height", THIS->height);
  600 
  601      offset = my_putenv(buffer, sizeof(buffer), offset,
  602                                                     "mimetype", THIS->mimetype);
  603 
  604      offset = my_putenv(buffer, sizeof(buffer), offset, "file", file);
  605 
  606      offset = my_putenv(buffer, sizeof(buffer), offset,
  607                                                  "fragment", THIS->urlFragment);
  608 
  609      offset = my_putenv(buffer, sizeof(buffer), offset,
  610                                             "autostart", autostart ? "1" : "0");
  611 
  612      offset = my_putenv(buffer, sizeof(buffer), offset,
  613                                              "winname", THIS->command->winname);
  614 
  615      if(THIS->display)
  616      {
  617           char * displayname = XDisplayName(DisplayString(THIS->display));
  618           offset = my_putenv(buffer, sizeof(buffer), offset, "DISPLAY", displayname);
  619      }
  620 
  621      for (i = 0; i < THIS->num_arguments; i++)
  622      {
  623       offset = my_putenv(buffer, sizeof(buffer), offset,
  624                                        THIS->args[i].name, THIS->args[i].value);
  625      }
  626 
  627      if(flags & H_CONTROLS)
  628      {
  629           launcher = g_controller;
  630      }
  631      else if(flags & H_LINKS)
  632      {
  633           launcher = g_linker;
  634      }
  635      else if(!autostart && !(flags & H_AUTOSTART) && (THIS->window != 0))
  636      {
  637           /* Application doesn't do autostart and autostart is false and
  638            * we have a window to draw in */
  639       nextHelper = g_helper;
  640           launcher = g_linker;
  641      }
  642      else
  643      {
  644           launcher = g_helper;
  645      }
  646 
  647      if(launcher == 0)
  648      {
  649           D("No launcher defined");
  650           _exit(EX_UNAVAILABLE); /* Child exit, that's OK */
  651      }
  652 
  653      D("Executing helper: %s %s %s %s %s\n",
  654        launcher,
  655        buffer,
  656        file,
  657        THIS->command->cmd,
  658        THIS->mimetype);
  659 
  660      execlp(launcher, launcher, buffer, THIS->command->cmd, nextHelper, NULL);
  661 
  662      D("EXECLP FAILED! errno=%i\n", errno);
  663 
  664      _exit(EX_UNAVAILABLE); /* Child exit, that's OK */
  665 
  666 }
  667 
  668 static char * NP_strdup2(const char * str, int len)
  669 {
  670      char * dupStr = NPN_MemAlloc(len + 1);
  671      if(dupStr != NULL)
  672      {
  673           strncpy(dupStr, str, len);
  674           dupStr[len] = '\0';
  675      }
  676      else
  677      {
  678           D("NPN_MemAlloc failed, size=%i\n", len+1);
  679      }
  680      return dupStr;
  681 }
  682 
  683 
  684 /**
  685  * String dup function that uses NPN_MemAlloc as opposed to malloc
  686  *
  687  * WARNING, this function will not work if directly or indirectly called from
  688  * NPP_GetMimeDescription.
  689  *
  690  * @param[in] str The string to duplicate
  691  *
  692  * @return Pointer to the duplicate.
  693  */
  694 static char * NP_strdup(const char * str)
  695 {
  696      return NP_strdup2(str, strlen(str));
  697 }
  698 
  699 
  700 /**
  701  * Test if the line buffer contains a mimetype
  702  *
  703  * @param[in] buffer The line buffer
  704  *
  705  * @return true if contains a mimetype
  706  */
  707 static bool isCfgMimeType(char * buffer)
  708 {
  709     return !isspace(buffer[0]);
  710 }
  711 
  712 /**
  713  * Parse the mimetypec in the cfg file
  714  *
  715  * @param[in] buffer The line read from config file
  716  *
  717  * @return a mimetype_t structure if valid mimetype.
  718  */
  719 static mimetype_t * parseCfgMimeType(char * buffer)
  720 {
  721      mimetype_t * type = (mimetype_t *) allocStaticMem(sizeof(mimetype_t));
  722      if(type == 0)
  723      {
  724           D("Failed to alloc memory for mimetype\n");
  725           return NULL;
  726      }
  727      memset(type, 0, sizeof(mimetype_t));
  728 
  729      D("New mime type\n");
  730 
  731      /* Cant use NPN_MemAlloc in NPP_GetMimeDescription, use
  732       * makeStrStatic as opposed to strdup otherwise we get a
  733       * memory leak */
  734      type->type = makeStrStatic(buffer, strlen(buffer));
  735 
  736      return (type->type == 0) ? NULL : type;
  737 }
  738 
  739 /**
  740  * Parse the command found in the cfg file
  741  *
  742  * @param[in] buffer The line read from config file
  743  *
  744  * @return a command structure if command found.
  745  */
  746 static command_t * parseCfgCmdLine(char * buffer)
  747 {
  748      char * x = &buffer[1];
  749      char * sep;
  750 
  751      command_t * cmd = (command_t *) allocStaticMem(sizeof(command_t));
  752      if(cmd == NULL)
  753      {
  754            D("Failed to alloc memory for command\n");
  755            return NULL;
  756      }
  757      memset(cmd, 0, sizeof(command_t));
  758 
  759      D("-- reading cmd line %s\n", x);
  760 
  761      sep = strchr(x, '\t');
  762      cmd->flags = strtol(x, NULL, 16);
  763      x = &sep[1];
  764      sep = strchr(x, '\t');
  765      if(sep > x)
  766      {
  767           cmd->winname = makeStrStatic(x, sep - x);
  768      }
  769      x = &sep[1];
  770      sep = strchr(x, '\t');
  771      if( sep > x)
  772      {
  773           cmd->fmatchStr = makeStrStatic(x, sep - x);
  774      }
  775      x = &sep[1];
  776      cmd->cmd = makeStrStatic(x, strlen(x));
  777      return cmd;
  778 }
  779 
  780 /**
  781  * Read the configuration file into memory.
  782  *
  783  * @param[in] f The FILE pointer
  784  */
  785 static void read_config(FILE * f)
  786 {
  787      int num_handlers = 0;
  788 
  789      handler_t * prev_handler = NULL;
  790      handler_t * handler = NULL;
  791 
  792      command_t * prev_cmd = NULL;
  793      mimetype_t * prev_type = NULL;
  794 
  795      char lineBuf[512];
  796      int lineNum;
  797 
  798      D("read_config\n");
  799 
  800      lineNum = 0;
  801      while (fgets(lineBuf, sizeof(lineBuf), f))
  802      {
  803           lineNum++;
  804           if(!chkCfgLine(lineBuf))
  805           {
  806                continue;
  807           }
  808 
  809       D("%5i::|%s|\n", lineNum, lineBuf);
  810 
  811           if(isCfgMimeType(lineBuf))
  812       {
  813            /* Mime type */
  814                mimetype_t * type = NULL;
  815 
  816            if (!handler || handler->cmds)
  817            {
  818             D("------------ Starting new handler ---------\n");
  819 
  820             handler = (handler_t *) allocStaticMem(sizeof(handler_t));
  821                     if(handler == 0)
  822                     {
  823                          return;
  824                     }
  825                     memset(handler, 0, sizeof(handler_t));
  826 
  827                     prev_type = NULL;
  828                     prev_cmd = NULL;
  829 
  830                     if(prev_handler)
  831                     {
  832                          /* Add to end of linked list of handlers */
  833                          prev_handler->pNext = handler;
  834                     }
  835                     else
  836                     {
  837                          /* Add at header of link list of handlers */
  838                          g_handlers = handler;
  839                     }
  840                     /* Remember the current end of linked list */
  841                     prev_handler = handler;
  842 
  843                     num_handlers++;
  844            }
  845 
  846                type = parseCfgMimeType(lineBuf);
  847                if(!type)
  848                {
  849                     return; /* run out of memory! */
  850                }
  851                if(prev_type)
  852                {
  853                     /* Add to end of linked list of handlers */
  854                     prev_type->pNext = type;
  855                }
  856                else
  857                {
  858                     /* Add at header of link list of handlers */
  859                     handler->types = type;
  860                }
  861                /* Remember the current end of linked list */
  862                prev_type = type;
  863       }
  864       else
  865       {
  866                command_t * cmd = parseCfgCmdLine(lineBuf);
  867                if(!cmd)
  868                {
  869                     return; /* run out of memory! */
  870                }
  871                if(!handler)
  872                {
  873                     D("Command before mimetype!\n");
  874                     return;
  875                }
  876 
  877                if(prev_cmd)
  878                {
  879                     /* Add to end of linked list of handlers */
  880                     prev_cmd->pNext = cmd;
  881                }
  882                else
  883                {
  884                     /* Add at header of link list of handlers */
  885                     handler->cmds = cmd;
  886                }
  887                /* Remember the current end of linked list */
  888                prev_cmd = cmd;
  889           }
  890      }
  891      D("Num handlers: %d\n", num_handlers);
  892 }
  893 
  894 /**
  895  * Find configuration file, helper and controller executables. Call the
  896  * appropriate xxx_cb function to handle the action (e.g. for configuration
  897  * file, the parsering and reading into memory).
  898  *
  899  * @param[in] magic references for this particular plugin type
  900  *
  901  * @return false if failed, true otherwise
  902  */
  903 static bool do_read_config(const char * magic)
  904 {
  905      bool retVal = true;
  906      char * config_fname;
  907      if (g_handlers)
  908      {
  909           return retVal;
  910      }
  911 
  912      D("do_read_config(%s)\n", magic);
  913 
  914      config_fname = get_cmds_cfg_path(magic);
  915      get_helper_paths(magic);
  916 
  917      if(config_fname)
  918      {
  919           FILE * fd = fopen(config_fname, "rb");
  920           if(fd)
  921           {
  922                read_config(fd);
  923                fclose(fd);
  924                D("do_read_config done\n");
  925           }
  926           else
  927           {
  928                D("Failed to read config %s\n", config_fname);
  929                retVal = false;
  930           }
  931           free(config_fname);
  932      }
  933      else
  934      {
  935           if(!haveError())
  936           {
  937               reportError(NULL, "Mozplugger error - failed to locate %s", config_fname);
  938           }
  939       retVal = false;
  940      }
  941 
  942      return retVal;
  943 }
  944 
  945 /**
  946  * Check URL is safe. Since href's are passed to an app as an argument, just
  947  * check for ways that a shell can be tricked into executing a command.
  948  *
  949  * @param[in] name The name to check
  950  * @param[in] isURL Is the name a URL or filename?
  951  *
  952  * @return true if OK, false if not
  953  */
  954 static bool safeName(const char* name, int isURL)
  955 {
  956      int i = 0;
  957      const int len = strlen(name);
  958 
  959      if ((name[0] == '/') && (isURL))
  960      {
  961           D("safeName() - reject URL '%s' as starts with '/'\n", name);
  962       return false;
  963      }
  964 
  965      for (i = 0; i < len; i++)
  966      {
  967       if (name[i] == '`' || name[i] == ';')
  968       {
  969                D("safeName() - reject '%s' as contains either ';' or '`'\n", name);
  970            /* Somebody's trying to do something naughty. */
  971            return false;
  972       }
  973      }
  974      return true;
  975 }
  976 
  977 /**
  978  * Extract the file name from the HTTP Headers (if present). This is passed
  979  * via the fragment environment variable to the helper application. Copy
  980  * a suitable filename for the URL to be used for the cached temporay file
  981  * name.
  982  *
  983  * @param[in] THIS Pointer to the instance data
  984  * @param[in] headers The HTTP headers to parse
  985  * @param[out] fileName The file Name extracted from the headers
  986  * @param[in] maxFileNameLen The max length of fileName
  987  *
  988  * @return None
  989  */
  990 static char * parseHeaders(data_t * const THIS, const char * headers, char * fileName)
  991 {
  992      const char * p = headers;
  993 
  994      if (!headers)
  995      {
  996           return fileName;
  997      }
  998 
  999      while((p = strstr(p, "Content-Disposition:")) != NULL)
 1000      {
 1001           size_t len = strcspn(p, "\n\r");
 1002       const char * start = strstr(p, "filename=\"");
 1003 
 1004       if (len == 0)
 1005           {
 1006            break;
 1007           }
 1008 
 1009           if((start ==0) || (start - p > len))
 1010           {
 1011            p += len;
 1012            continue;
 1013       }
 1014       start += strlen("filename=\"");
 1015       len = len - (start - p) - 1;
 1016 
 1017       if(len > 0)
 1018           {
 1019                 if(fileName)
 1020                 {
 1021                      NPN_MemFree(fileName);
 1022                 }
 1023                 fileName = NP_strdup2(start, len);
 1024       }
 1025       p += len;
 1026      }
 1027      return fileName;
 1028 }
 1029 
 1030 /**
 1031  * Extract the 'fragment' from the end of the URL if present. This is passed
 1032  * via the fragment environment variable to the helper application. Copy
 1033  * a suitable filename for the URL to be used for the cached temporay file
 1034  * name.
 1035  *
 1036  * @param[in] THIS Pointer to the instance data
 1037  * @param[out] fileName The file Name extracted from the URL
 1038  * @param[in] maxFileNameLen The max length of fileName
 1039  *
 1040  * @return Pointer to allocated memory with fileName
 1041  */
 1042 static char * parseURL(data_t * const THIS, int extract_filename)
 1043 {
 1044      const char * frag = strchr(THIS->url, '#');
 1045 
 1046      if(frag)
 1047      {
 1048           if(THIS->urlFragment)
 1049           {
 1050               D("parseURL - replacing previous fragment\n");
 1051               NPN_MemFree(THIS->urlFragment);
 1052           }
 1053 
 1054           D("parseURL - fragment '%s' found at end of URL\n", &frag[1]);
 1055           THIS->urlFragment = NP_strdup(&frag[1]);
 1056      }
 1057 
 1058      if(extract_filename)
 1059      {
 1060           const char * end = strrchr(THIS->url, '?');
 1061           const char * start;
 1062           int len;
 1063 
 1064           /* Find end or url (but dont include variable params or fragment */
 1065           if(!end)
 1066           {
 1067                end = frag ? frag : &THIS->url[strlen(THIS->url)];
 1068           }
 1069           /* Work backwards to the first forward slash */
 1070           start = &end[-1];
 1071           while( (start > THIS->url) && (*start != '/'))
 1072           {
 1073                start--;
 1074           }
 1075           if(*start == '/')
 1076           {
 1077                start++;
 1078           }
 1079           len = end-start;
 1080           return NP_strdup2(start, len);
 1081      }
 1082      return NULL;
 1083 }
 1084 
 1085 /**
 1086  * See if the URL matches out match criteria.
 1087  *
 1088  * @param[in] matchStr The string to match
 1089  * @param[in] url The url
 1090  *
 1091  * @return 1(true) if matched, zero otherwise
 1092  */
 1093 __inline
 1094 static int match_url(const char * matchStr, const char * url)
 1095 {
 1096      int matchStrLen;
 1097      const char * end;
 1098 
 1099      switch (matchStr[0])
 1100      {
 1101      case '*':
 1102           /* Does the URL start with the match String */
 1103           matchStr++; /* Step over the asterisk */
 1104       return (strncasecmp(matchStr, url, strlen(matchStr)) == 0);
 1105 
 1106      case '%':
 1107           /* Does the URL end with the match String */
 1108           matchStr++; /* Step over the percent sign */
 1109 
 1110           /* Need to find the end of the url, before any
 1111            * extra params i.e'?=xxx' or '#yyy' */
 1112           if( (end = strchr(url, '?')) == NULL)
 1113           {
 1114                if( (end = strchr(url, '#')) == NULL)
 1115                {
 1116                     end = &url[strlen(url)];
 1117                }
 1118           }
 1119           matchStrLen = strlen(matchStr);
 1120           if(end - matchStrLen < url)
 1121           {
 1122                return 0;
 1123           }
 1124       return (strncasecmp(matchStr, end-matchStrLen, matchStrLen) == 0);
 1125 
 1126      default:
 1127           /* Is the match string anywhere in the URL */
 1128       return (strstr(url, matchStr) != NULL);
 1129      }
 1130 }
 1131 
 1132 /**
 1133  * Go through the commands in the config file and find one that fits our needs.
 1134  *
 1135  * @param[in] THIS Pointer to the data associated with this instance of the
 1136  *                       plugin
 1137  * @param[in] streamOnly If true select entry sith stream set only
 1138  * @param[in] c Pointer to command structure to match against
 1139  *
 1140  * @return 1(true) if match, else zero otherwise
 1141  */
 1142 __inline
 1143 static int match_command(const data_t * THIS, int streamOnly, const command_t *c)
 1144 {
 1145 #define MODE_MASK (H_NOEMBED | H_EMBED)
 1146 
 1147      D("Checking command: %s\n", c->cmd);
 1148 
 1149      /* If command is specific to a particular mode... */
 1150      if (c->flags & MODE_MASK)
 1151      {
 1152           /* Check it matches the current mode... */
 1153           if ( (THIS->mode_flags & MODE_MASK) != (c->flags & MODE_MASK) )
 1154           {
 1155            D("Flag mismatch: mode different %x != %x\n",
 1156                      THIS->mode_flags & MODE_MASK,  c->flags & MODE_MASK);
 1157            return 0;
 1158           }
 1159      }
 1160      if((THIS->mode_flags & H_LINKS) != 0)   /* Requires the links helper? */
 1161      {
 1162           if((c->flags & MODE_MASK) == 0)    /* But command is not */
 1163           {
 1164            D("Flag mismatch: cmd doesnt do links\n");
 1165            return 0;
 1166           }
 1167      }
 1168 
 1169      if ((c->flags & H_LOOP) && (THIS->repeats != INF_LOOPS))
 1170      {
 1171       D("Flag mismatch: loop\n");
 1172       return 0;
 1173      }
 1174      if (streamOnly && !(c->flags & H_STREAM))
 1175      {
 1176       D("Flag mismatch: stream only required\n");
 1177       return 0;
 1178      }
 1179 
 1180      if(c->fmatchStr)
 1181      {
 1182           if(!match_url(c->fmatchStr, THIS->url))
 1183           {
 1184                D("fmatch mismatch: url '%s' doesnt have '%s'\n",
 1185                                               THIS->url, c->fmatchStr);
 1186                return 0;
 1187           }
 1188      }
 1189      D("Match found!\n");
 1190      return 1;
 1191 }
 1192 
 1193 /**
 1194  * See if mimetype matches.
 1195  *
 1196  * @param[in] reqMimeType pointer to required mimetype
 1197  * @param[in] m Mimetype to match against
 1198  *
 1199  * @return 1(true) if match, else zero otherwise
 1200  */
 1201 __inline
 1202 static int match_mime_type(const char * reqMimeType, mimetype_t * m)
 1203 {
 1204      int retVal;
 1205      if ((strcasecmp(m->type, reqMimeType) != 0) && (strcmp(m->type, "*") != 0))
 1206      {
 1207           retVal = 0;
 1208      }
 1209      else
 1210      {
 1211           retVal = 1;
 1212      }
 1213      D("Checking '%s' ?= '%s', %s\n", m->type, reqMimeType,
 1214                                             retVal == 1 ? "same" : "different");
 1215      return retVal;
 1216 }
 1217 
 1218 /**
 1219  * See if handler matches, if so check a command is available and return that
 1220  * command.
 1221  *
 1222  * @param[in] h Pointer to handler to match against
 1223  * @param[in] THIS Pointer to data associated with this instance of plugin
 1224  * @param[in] streamOnly If True select only entry with stream flag
 1225  *
 1226  * @return Pointer to command struct if match or NULL
 1227  */
 1228 __inline
 1229 static command_t * match_handler(const handler_t * h, const data_t * THIS, int streamOnly)
 1230 {
 1231      mimetype_t *m;
 1232 
 1233      D("-------------------------------------------\n");
 1234      D("Commands for this handle at (%p):\n", h->cmds);
 1235 
 1236      for(m = h->types; m; m = m->pNext)
 1237      {
 1238       if (match_mime_type(THIS->mimetype, m))
 1239       {
 1240                command_t * c;
 1241                for(c = h->cmds; c; c = c->pNext)
 1242            {
 1243             if (match_command(THIS, streamOnly, c))
 1244             {
 1245              return c;
 1246             }
 1247            }
 1248       }
 1249      }
 1250      return NULL;
 1251 }
 1252 
 1253 /**
 1254  * Find the appropriate command
 1255  *
 1256  * @param[in] THIS Pointer to plugin instance data
 1257  * @param[in] streamOnly If true select only the command with stream flag
 1258  *
 1259  * @return Pointer to command struct if match, else NULL otherwise
 1260  */
 1261 static command_t * find_command(const data_t * THIS, int streamOnly)
 1262 {
 1263      handler_t * h;
 1264 
 1265      D("find_command...\n");
 1266 
 1267      for(h = g_handlers; h; h = h->pNext)
 1268      {
 1269           command_t * command = match_handler(h, THIS, streamOnly);
 1270           if(command)
 1271       {
 1272            D("Command found.\n");
 1273            return command;
 1274       }
 1275      }
 1276 
 1277      D("No command found.\n");
 1278      return NULL;
 1279 }
 1280 
 1281 /**
 1282  * Get the plugin version string
 1283  *
 1284  * @param[in] magic references for this particular plugin type
 1285  *
 1286  * @return Pointer to the version string
 1287  */
 1288 const char * NP2_GetPluginVersion(const char * magic)
 1289 {
 1290      D("NP_GetPluginVersion(%s)\n", magic);
 1291      if(!is_base_mozplugger(magic))
 1292      {
 1293           get_helper_paths(magic);
 1294      }
 1295      D("NP_GetPluginVersion returning '%s'\n", g_version);
 1296      return g_version;
 1297 }
 1298 
 1299 /**
 1300  * Rebuild the cached versions of configuration
 1301  *
 1302  * @return true if success.
 1303  */
 1304 static bool mozplugger_update(bool * pDoesntExist)
 1305 {
 1306      bool success = true;
 1307      pid_t pid;
 1308 
 1309      D("Called mozplugger_update\n");
 1310      pid = fork();
 1311      if(pid == -1)
 1312      {
 1313           fprintf(stderr, "Failed to fork\n");
 1314       exit(EXIT_FAILURE);
 1315      }
 1316      else if(pid == 0)
 1317      {
 1318       execlp("mozplugger-update", "mozplugger-update", NULL);
 1319           if( errno == EEXIST)
 1320           {
 1321                exit(1000);
 1322           }
 1323           exit(EXIT_FAILURE);
 1324      }
 1325      else
 1326      {
 1327          int status;
 1328          D("Waiting for mozplugger-update\n");
 1329          waitpid(pid, &status, 0);          /* If Application completed is a bad way, then lets give up now */
 1330          if(!WIFEXITED(status))
 1331          {
 1332                D("mozplugger-update dumped core or something...\n");
 1333                success = false;
 1334          }
 1335          else 
 1336          {
 1337                status = WEXITSTATUS(status);
 1338                if(status != EXIT_SUCCESS)
 1339                {
 1340                     D("mozplugger-update exited with status: %d\n", status);
 1341                     success = false;
 1342                     if(status == 1000)
 1343                     {
 1344                          *pDoesntExist = true;
 1345                     }
 1346                }
 1347          }
 1348      }
 1349      D("mozplugger-update done\n");
 1350      if(success)
 1351      {
 1352 //         NPN_ReloadPlugins(false);
 1353      }
 1354      return success;
 1355 }
 1356 
 1357 /**
 1358  * Check is the local plugin directories exist for various browsers
 1359  * If they do then its likely that the user uses those browsers
 1360  * so check mozplugger0.so exists in that directory
 1361  *
 1362  * @return false if we need to run update
 1363  */
 1364 static bool chkValidLocalPluginDirs()
 1365 {
 1366      static const char * browsers[] =
 1367      {
 1368           "%s/.mozilla/plugins",
 1369           "%s/.netscape/plugins",
 1370           "%s/.opera/plugins"
 1371      };
 1372 
 1373      const char * home = get_home_dir();
 1374      int i;
 1375 
 1376      if(home == NULL)
 1377      {
 1378            reportError(NULL, "Mozplugger cannot determine HOME directory");
 1379            return false;
 1380      }
 1381 
 1382      for(i = 0; i < sizeof(browsers)/sizeof(const char *); i++)
 1383      {
 1384           struct stat details;
 1385           char fname[256];
 1386           int n = snprintf(fname, sizeof(fname), browsers[i], home);
 1387 
 1388           if( (mkdir(fname, S_IRWXU) != 0) && (errno != EEXIST))
 1389           {
 1390                continue;
 1391           }
 1392 
 1393           strncat(fname, "/mozplugger0.so", sizeof(fname) - n);
 1394           if(stat(fname, &details) != 0)
 1395           {
 1396                return false;
 1397           }
 1398      }
 1399      return true;
 1400 }
 1401 
 1402 /**
 1403  * Check the last time we updated the cache
 1404  *
 1405  * return -1 too soon, +1 about time to update
 1406  */
 1407 static time_t chkTimeToUpdate(bool * update, bool * dont_update)
 1408 {
 1409      struct stat details;
 1410      char ts_fname[256];
 1411      time_t ts_ftime = 0;
 1412 
 1413      get_cfg_path_prefix(".last_update:", ts_fname, sizeof(ts_fname));
 1414      if(stat(ts_fname, &details) == 0)
 1415      {
 1416           time_t now = time(NULL);
 1417           ts_ftime = details.st_mtime;
 1418 
 1419           if(ts_ftime > now)
 1420           {
 1421                D("Strange .last_update written in the future? %lu s\n", ts_ftime - now); 
 1422           }
 1423           else  
 1424           {
 1425                time_t diff = now - ts_ftime;
 1426                if(diff < 10)
 1427                {
 1428                     D("Dont update, too soon %lu s\n", diff); 
 1429                     *dont_update = true;
 1430                }
 1431 #ifdef AUTO_UPDATE
 1432                else if(diff > 7*24*60*60)
 1433                {
 1434                     D("Auto update %lu s\n", diff); 
 1435                     *update = true;
 1436                }
 1437           }
 1438 #endif
 1439      }
 1440      return ts_ftime;
 1441 }
 1442 
 1443 /**
 1444  * Parse buf for text containing the VERSION of the config file. Check the
 1445  * version matches
 1446  *
 1447  * @param[in] buf The string read from the description file
 1448  *
 1449  * @return false if not matches
 1450  */
 1451 static bool chk_version_matches(char * buf)
 1452 {
 1453      D("Processed config version = '%s'\n", &buf[1]);
 1454      trim_trailing_spaces(buf);
 1455      if(strcmp(&buf[1],  VERSION) != 0)
 1456      {
 1457           D("Processed config format mismatch should be" VERSION "\n");
 1458           return false;
 1459      }
 1460      return true;
 1461 }
 1462  
 1463 /**
 1464  * Parse buf for text containing the name of the config file. Check the
 1465  * date and time of that file against the time of the cached file.
 1466  *
 1467  * @param[in] buf The string read from the description file
 1468  *
 1469  * @return false if description file written before timestamp
 1470  */
 1471 static bool chk_cached_is_newer(char * buf, time_t cached_ftime)
 1472 {
 1473      char * q = strstr(buf, "autogenerated from ");
 1474      if(q)
 1475      {
 1476           struct stat details;
 1477           q += 19;                  /* Skip text part */
 1478           trim_trailing_spaces(q);
 1479 
 1480           if( (stat(q, &details) == 0) && (details.st_mtime <= cached_ftime))
 1481           {
 1482                return true;
 1483           }
 1484           else
 1485           {
 1486                D("mozpluggerrc = %s %u - %u\n", q, (unsigned) details.st_mtime, (unsigned) cached_ftime);
 1487           }
 1488     }
 1489     return false;
 1490 }
 1491 
 1492 static char * read_desc(const char * fname, time_t ts_ftime, bool *update, bool is_base)
 1493 {
 1494      char * desc = NULL;
 1495      FILE * fp = fopen(fname, "rb");
 1496 
 1497      D("Reading '%s'\n", fname);
 1498      if(fp != NULL)
 1499      {
 1500           char linebuf[256];
 1501           
 1502           if( fgets(linebuf, sizeof(linebuf), fp)
 1503                   && chk_version_matches(linebuf) 
 1504                       && fgets(linebuf, sizeof(linebuf), fp)
 1505                           && chk_cached_is_newer(linebuf, ts_ftime))
 1506           {
 1507                 while( fgets(linebuf, sizeof(linebuf), fp) && (linebuf[0] == '#'))
 1508                      ;
 1509                
 1510                 if(!is_base)
 1511                 {
 1512                      struct stat details;
 1513 
 1514                      fstat(fileno(fp), &details);
 1515                      desc = malloc(details.st_size+1);
 1516                      if(desc)
 1517                      {
 1518                           D("Size '%u'\n", (unsigned) details.st_size);
 1519 
 1520                          strcpy(desc, linebuf);
 1521                          fgets(&desc[strlen(linebuf)], details.st_size, fp);
 1522                     }
 1523                }
 1524           }
 1525           else
 1526           {
 1527                *update = true;
 1528           }
 1529           fclose(fp);
 1530      }
 1531      else
 1532      {
 1533           D("Failed to read description\n");
 1534 #ifdef AUTO_UPDATE
 1535           *update = true;
 1536 #endif
 1537      }
 1538      return desc;
 1539 }
 1540 
 1541 /**
 1542  * Construct a MIME Description string for netscape so that mozilla shall know
 1543  * when to call us back.
 1544  *
 1545  * @param[in] magic references for this particular plugin type
 1546  *
 1547  * @return Pointer to string containing mime decription for this plugin
 1548  */
 1549 const char * NP2_GetMIMEDescription(const char * magic)
 1550 {
 1551      char * fname;
 1552      char * desc;
 1553      bool update = false;
 1554      bool dont_update = false;
 1555      bool doesnt_exist = false;
 1556      time_t ts_ftime;
 1557 
 1558      D("NP_GetMIMEDescription(%s)\n", magic);
 1559 
 1560      if(!chkValidLocalPluginDirs())
 1561      {
 1562           D("Local plugin dirs not valid");
 1563           update = true;
 1564      }
 1565 
 1566      /* Check the last time we updated the cache */
 1567      ts_ftime = chkTimeToUpdate( &update, &dont_update);
 1568 
 1569      if(update && !dont_update)
 1570      {
 1571           mozplugger_update(&doesnt_exist);
 1572           ts_ftime = time(NULL);
 1573           dont_update = true;
 1574           update = false;
 1575      }     
 1576 
 1577      fname = get_mimetypes_cfg_path(magic);
 1578      desc = read_desc(fname, ts_ftime, &update, is_base_mozplugger(magic));
 1579 
 1580      if(update && !dont_update)
 1581      {
 1582           mozplugger_update(&doesnt_exist);
 1583           ts_ftime = time(NULL);
 1584           update = false;
 1585 
 1586           free(desc);
 1587           desc = read_desc(fname, ts_ftime, &update, is_base_mozplugger(magic));
 1588 
 1589      }
 1590      free(fname);
 1591 
 1592      if(!desc && update && !doesnt_exist && !haveError())
 1593      {
 1594           reportError(NULL, "Please close browser and run mozplugger-update");
 1595      }
 1596 
 1597      if(haveError())
 1598      {
 1599           desc = realloc(desc, 512);
 1600           snprintf(desc, 511, "dummy/dummy:*.dummy:%s", errMsg);
 1601      }
 1602      D("Getmimedescription done: %.100s ...\n", desc);
 1603      return (const char *)desc;
 1604 }
 1605 
 1606 /**
 1607  * Is the plugin playing
 1608  *
 1609  * @return True if got property
 1610  */
 1611 bool is_playing(NPP instance)
 1612 {
 1613      data_t * THIS = instance->pdata;
 1614      if (THIS)
 1615      {
 1616           if((THIS->commsPipeFd >= 0) || (THIS->pid > -1))
 1617           {
 1618                int status;
 1619                if(waitpid(THIS->pid, &status, WNOHANG) == 0)
 1620                {
 1621                     /* If no status available from child then child
 1622                                   * must still be running!? */
 1623                     return true;
 1624                }
 1625           }
 1626      }
 1627      return false;
 1628 }
 1629 
 1630 /**
 1631  * Get the name of the plugin
 1632  *
 1633  * @param[in] magic references for this particular plugin type
 1634  */
 1635 static const char * getPluginName(const char * magic)
 1636 {
 1637      if(!is_base_mozplugger(magic))
 1638      {
 1639           get_helper_paths(magic);
 1640      }
 1641      return g_pluginName;
 1642 }
 1643 
 1644 /**
 1645  * Get plugin Description
 1646  *
 1647  * @param[in] magic references for this particular plugin type
 1648  *
 1649  * @return Returns the Description of the plugin
 1650  */
 1651 static const char * getPluginDescription(const char * magic)
 1652 {
 1653      static char desc_buffer[8192];
 1654      const char * dbgPath = get_debug_path();
 1655      char * config_fname = get_cmds_cfg_path(magic);
 1656      struct stat details;
 1657 
 1658      if(is_base_mozplugger(magic) || (!config_fname) || (stat(config_fname, &details) != 0))
 1659      {
 1660           snprintf(desc_buffer, sizeof(desc_buffer),
 1661            "MozPlugger version " VERSION
 1662                    " Refresh required, please close browser and run mozplugger-update, "
 1663            "for documentation on mozplugger see the man page."
 1664                   );
 1665      }
 1666      else
 1667      {
 1668           const char * home = get_home_dir();
 1669           char * pCfg = NULL;
 1670           struct stat details;
 1671           int i;
 1672 
 1673           details.st_mtime = 0;
 1674           stat(config_fname, &details);
 1675 
 1676           /* removed cmds and replace with '*' */
 1677           i = strlen(config_fname)-4;
 1678           config_fname[i++] = '*';
 1679           config_fname[i] = '\0';
 1680 
 1681           /* Hide the user's name!? */
 1682           i = strlen(home);
 1683           if(strncmp(home, config_fname, i) == 0)
 1684           {
 1685                pCfg = &config_fname[i-1];
 1686                *pCfg = '~';
 1687           }
 1688           else
 1689           {
 1690                pCfg = config_fname;
 1691           }
 1692 
 1693           snprintf(desc_buffer, sizeof(desc_buffer),
 1694            "MozPlugger version "
 1695            VERSION
 1696 #ifdef GCOV
 1697                    "(gcov)"
 1698 #endif
 1699            ", for documentation on mozplugger see the man page. "
 1700            "<table>"
 1701            "<tr><td>Cached config files:</td><td>%s</td><td>%s</td></tr>"
 1702                    "%s%s%s"
 1703            " </table>"
 1704            "<br clear=all>",
 1705                    pCfg, asctime(localtime(&details.st_mtime)),
 1706                    dbgPath ? "<tr><td>Debug file:</td><td>" : "",
 1707                    dbgPath ? dbgPath : "",
 1708                    dbgPath ? "/" DEBUG_FILENAME "</td><td></td></tr>" : ""
 1709                   );
 1710      }
 1711      free(config_fname);
 1712      return (const char *)desc_buffer;
 1713 }
 1714 
 1715 /**
 1716  * Get plugin needs Xembed
 1717  *
 1718  * @return Returns True if Xembed required
 1719  */
 1720 static NPBool getPluginNeedsXembed(NPP instance, NPError *pErr)
 1721 {
 1722      NPBool retVal = 0;
 1723 
 1724      if (instance == NULL)
 1725      {
 1726           *pErr = NPERR_GENERIC_ERROR;
 1727      }
 1728      else
 1729      {
 1730           data_t * this = instance->pdata;
 1731           if((this == NULL) || (this->command == NULL))
 1732           {
 1733                *pErr = NPERR_GENERIC_ERROR;
 1734           }
 1735 
 1736           else if( ((this->command->flags & H_NEEDS_XEMBED) != 0)
 1737                                               && does_browser_support_xembed())
 1738           {
 1739                D("Plugin needs XEmbed\n");
 1740                retVal = 1;
 1741           }
 1742           else
 1743           {
 1744                D("Plugin does not need XEmbed\n");
 1745           }
 1746      }
 1747      return retVal;
 1748 }
 1749 
 1750 /**
 1751  * Let Mozilla know things about mozplugger. This one is called without an
 1752  * instance pointer when loading the plugin.
 1753  *
 1754  * @param[in] magic references for this particular plugin type
 1755  * @param[in] variable Name of variable to get (enum)
 1756  * @param[out] value The value got
 1757  *
 1758  * @return Returns error code if problem
 1759  */
 1760 NPError NP2_GetValue(const char * magic, NPPVariable variable, void *value)
 1761 {
 1762      NPError err = NPERR_NO_ERROR;
 1763 
 1764      D("NP_GetValue(%.20s, %s)\n", magic, NPPVariableToString(variable));
 1765 
 1766      switch (variable)
 1767      {
 1768      case NPPVpluginNameString:
 1769       *((const char **)value) = getPluginName(magic);
 1770       break;
 1771 
 1772      case NPPVpluginDescriptionString:
 1773       *((const char **)value) = getPluginDescription(magic);
 1774       break;
 1775 
 1776      default:
 1777       D("NP_GetValue('%s' - %d) not implemented\n",
 1778                                       NPPVariableToString(variable), variable);
 1779       err = NPERR_GENERIC_ERROR;
 1780           break;
 1781      }
 1782      return err;
 1783 }
 1784 
 1785 /**
 1786  * Let Mozilla know things about this instance.
 1787  *
 1788  * @param[in] instance Pointer to plugin instance data
 1789  * @param[in] variable Name of variable to get (enum)
 1790  * @param[out] value The value got
 1791  *
 1792  * @return Returns error code if problem
 1793  */
 1794 NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
 1795 {
 1796      NPError err = NPERR_NO_ERROR;
 1797 
 1798      D("NPP_GetValue(%s)\n", NPPVariableToString(variable));
 1799 
 1800      switch (variable)
 1801      {
 1802      case NPPVpluginDescriptionString:
 1803       *((const char **)value) = getPluginDescription("");
 1804       break;
 1805 
 1806      case NPPVpluginNeedsXEmbed:
 1807 #ifdef ALWAYS_NEEDS_XEMBED
 1808           /* For Chromium always return 1 */
 1809           *((NPBool *)value) = 1;
 1810 #else
 1811           *((NPBool *)value) = getPluginNeedsXembed(instance, &err);
 1812 #endif
 1813           break;
 1814 
 1815      case NPPVpluginScriptableNPObject :
 1816           *((NPObject **)value) = getPluginScritableObject(instance, &err);
 1817           break;
 1818 
 1819      default :
 1820       D("NPP_GetValue('%s' - %d) not implemented\n",
 1821                                       NPPVariableToString(variable), variable);
 1822       err = NPERR_GENERIC_ERROR;
 1823           break;
 1824      }
 1825      return err;
 1826 }
 1827 
 1828 /**
 1829  * Let Mozilla set things on mozplugger.
 1830  *
 1831  * @param[in] instance Pointer to plugin instance data
 1832  * @param[in] variable Name of variable to get (enum)
 1833  * @param[in] value The value to set
 1834  *
 1835  * @return Returns error code if problem
 1836  */
 1837 NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value)
 1838 {
 1839      NPError err = NPERR_NO_ERROR;
 1840 
 1841      switch (variable)
 1842      {
 1843      default:
 1844           D("NPP_SetValue( %d)  not implemented\n", variable);
 1845       err = NPERR_GENERIC_ERROR;
 1846           break;
 1847      }
 1848      return err;
 1849 }
 1850 
 1851 
 1852 /**
 1853  * Convert a string to an integer.
 1854  * The string can be true, false, yes or no.
 1855  *
 1856  * @param[in] s String to convert
 1857  * @param[in] my_true The value associated with true
 1858  * @param[in] my_false The value associated with false
 1859  *
 1860  * @return The value
 1861  */
 1862 static int my_atoi(const char *s, int my_true, int my_false)
 1863 {
 1864      switch (s[0])
 1865      {
 1866      case 't': case 'T': case 'y': case 'Y':
 1867       return my_true;
 1868      case 'f': case 'F': case 'n': case 'N':
 1869       return my_false;
 1870      case '0': case '1': case '2': case '3': case '4':
 1871      case '5': case '6': case '7': case '8': case '9':
 1872       return atoi(s);
 1873      }
 1874      return -1;
 1875 }
 1876 
 1877 /**
 1878  * Initialize another instance of mozplugger. It is important to know
 1879  * that there might be several instances going at one time.
 1880  *
 1881  * @param[in] pluginType Type of embedded object (mime type)
 1882  * @param[in] instance Pointer to plugin instance data
 1883  * @param[in] mode Embedded or not
 1884  * @param[in] argc The number of associated tag attributes
 1885  * @param[in] argn Array of attribute names
 1886  * @param[in] argv Array of attribute values#
 1887  * @param[in] saved Pointer to any previously saved data
 1888  *
 1889  * @return Returns error code if problem
 1890  */
 1891 NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode,
 1892         int16_t argc, char* argn[], char* argv[], NPSavedData* saved)
 1893 {
 1894      int e;
 1895 
 1896      int src_idx = -1;
 1897      int href_idx = -1;
 1898      int data_idx = -1;
 1899      int alt_idx = -1;
 1900      int autostart_idx = -1;
 1901      int autohref_idx = -1;
 1902      int target_idx = -1;
 1903      data_t * THIS;
 1904 
 1905      char *url = NULL;
 1906 
 1907      D("NPP_New(%s) - instance=%p\n", pluginType, instance);
 1908 
 1909      if (!instance)
 1910      {
 1911       return NPERR_INVALID_INSTANCE_ERROR;
 1912      }
 1913 
 1914      if (!pluginType)
 1915      {
 1916       return NPERR_INVALID_INSTANCE_ERROR;
 1917      }
 1918 
 1919      THIS = NPN_MemAlloc(sizeof(data_t));
 1920      if (THIS == NULL)
 1921      {
 1922           return NPERR_OUT_OF_MEMORY_ERROR;
 1923      }
 1924      instance->pdata = THIS;
 1925 
 1926      memset((void *)THIS, 0, sizeof(data_t));
 1927 
 1928      /* Only initialise the non-zero fields */
 1929      THIS->pid = -1;
 1930      THIS->commsPipeFd = -1;
 1931      THIS->repeats = 1;
 1932      THIS->autostart = 1;
 1933      THIS->autostartNotSeen = 1;
 1934      THIS->tmpFileFd = -1;
 1935 
 1936      if(mode == NP_EMBED)
 1937      {
 1938          THIS->mode_flags = H_EMBED;
 1939      }
 1940      else
 1941      {
 1942          THIS->mode_flags = H_NOEMBED;
 1943      }
 1944 
 1945      if (!(THIS->mimetype = NP_strdup(pluginType)))
 1946      {
 1947       return NPERR_OUT_OF_MEMORY_ERROR;
 1948      }
 1949 
 1950      THIS->num_arguments = argc;
 1951      if(argc == 0)
 1952      {
 1953         return NPERR_NO_ERROR;
 1954      }
 1955 
 1956      if (!(THIS->args = (argument_t *)NPN_MemAlloc(
 1957                                           (uint32_t)(sizeof(argument_t) * argc))))
 1958      {
 1959       return NPERR_OUT_OF_MEMORY_ERROR;
 1960      }
 1961 
 1962      for (e = 0; e < argc; e++)
 1963      {
 1964       if (strcasecmp("loop", argn[e]) == 0)
 1965       {
 1966            THIS->repeats = my_atoi(argv[e], INF_LOOPS, 1);
 1967       }
 1968           /* realplayer also uses numloop tag */
 1969           /* windows media player uses playcount */
 1970           else if((strcasecmp("numloop", argn[e]) == 0) ||
 1971                   (strcasecmp("playcount", argn[e]) == 0))
 1972           {
 1973            THIS->repeats = atoi(argv[e]);
 1974           }
 1975       else if((strcasecmp("autostart", argn[e]) == 0) ||
 1976               (strcasecmp("autoplay", argn[e]) == 0))
 1977       {
 1978                autostart_idx = e;
 1979       }
 1980       /* get the index of the src attribute if this is a 'embed' tag */
 1981       else if (strcasecmp("src", argn[e]) == 0)
 1982       {
 1983            src_idx = e;
 1984       }
 1985       /* get the index of the data attribute if this is a 'object' tag */
 1986           else if (strcasecmp("data", argn[e]) == 0)
 1987           {
 1988                data_idx = e;
 1989           }
 1990           /* Special case for quicktime. If there's an href or qtsrc attribute,
 1991            * remember it for now */
 1992           else if((strcasecmp("href", argn[e]) == 0) ||
 1993                 (strcasecmp("qtsrc", argn[e]) == 0))
 1994           {
 1995                if(href_idx == -1)
 1996                {
 1997                     href_idx = e;
 1998                }
 1999           }
 2000           else if((strcasecmp("filename", argn[e]) == 0) ||
 2001                 (strcasecmp("url", argn[e]) == 0) ||
 2002                 (strcasecmp("location", argn[e]) == 0))
 2003           {
 2004                if(alt_idx == -1)
 2005                {
 2006                     alt_idx = e;
 2007                }
 2008           }
 2009           /* Special case for quicktime. If there's an autohref or target
 2010            * attributes remember them for now */
 2011           else if (strcasecmp("target", argn[e]) == 0)
 2012           {
 2013                target_idx = e;
 2014           }
 2015       else if(strcasecmp("autohref", argn[e]) == 0)
 2016       {
 2017                autohref_idx = e;
 2018       }
 2019 
 2020       /* copy the flag to put it into the environment later */
 2021       D("VAR_%s=%s\n", argn[e], argv[e]);
 2022           {
 2023                const int len = strlen(argn[e]) + 5;
 2024 
 2025                if (!(THIS->args[e].name = (char *)NPN_MemAlloc(len)))
 2026                {
 2027                 return NPERR_OUT_OF_MEMORY_ERROR;
 2028                }
 2029            snprintf(THIS->args[e].name, len, "VAR_%s", argn[e]);
 2030            THIS->args[e].value = argv[e] ? NP_strdup(argv[e]) : NULL;
 2031           }
 2032      }
 2033 
 2034      if (src_idx >= 0)
 2035      {
 2036           url = THIS->args[src_idx].value;
 2037           /* Special case for quicktime. If there's an href or qtsrc
 2038            * attribute, we want that instead of src but we HAVE to
 2039            * have a src first. */
 2040           if (href_idx >= 0)
 2041           {
 2042            D("Special case QT detected\n");
 2043            THIS->href = THIS->args[href_idx].value;
 2044 
 2045                autostart_idx = autohref_idx;
 2046 
 2047                if(target_idx >= 0)
 2048                {
 2049                    /* One of those clickable Quicktime linking objects! */
 2050                    THIS->mode_flags &= ~(H_EMBED | H_NOEMBED);
 2051                    THIS->mode_flags |= H_LINKS;
 2052                }
 2053           }
 2054      }
 2055      else if (data_idx >= 0)
 2056      {
 2057           D("Looks like an object tag with data attribute\n");
 2058           url = THIS->args[data_idx].value;
 2059      }
 2060      else if (alt_idx >= 0)
 2061      {
 2062           D("Fall-back use alternative tags\n");
 2063           url = THIS->args[alt_idx].value;
 2064      }
 2065 
 2066      /* Do the autostart check here, AFTER we have processed the QT special
 2067       * case which can change the autostart attribute */
 2068      if(autostart_idx > 0)
 2069      {
 2070       THIS->autostart = !!my_atoi(argv[autostart_idx], 1, 0);
 2071       THIS->autostartNotSeen = 0;
 2072      }
 2073 
 2074      if (url)
 2075      {
 2076           THIS->url = url;
 2077 
 2078           /* Mozilla does not support the following protocols directly and
 2079            * so it never calls NPP_NewStream for these protocols */
 2080       if(   (strncmp(url, "mms://", 6) == 0)
 2081              || (strncmp(url, "mmsu://", 7) == 0)    /* MMS over UDP */
 2082              || (strncmp(url, "mmst://", 7) == 0)    /* MMS over TCP */
 2083              || (strncmp(url, "rtsp://", 7) == 0)
 2084              || (strncmp(url, "rtspu://", 8) == 0)   /* RTSP over UDP */
 2085              || (strncmp(url, "rtspt://", 8) == 0))  /* RTSP over TCP */
 2086       {
 2087            D("Detected MMS -> url=%s\n", url);
 2088 
 2089                THIS->browserCantHandleIt = true;
 2090                THIS->command = find_command(THIS,1); /* Needs to be done early! so xembed
 2091                                                          flag is correctly set*/
 2092 
 2093 
 2094                /* The next call from browser will be NPP_SetWindow() &
 2095                 * NPP_NewStream will never be called */
 2096       }
 2097           else
 2098           {
 2099                THIS->command = find_command(THIS,0); /* Needs to be done early so xembed
 2100                                                        flag is correctly set*/
 2101 
 2102                /* For protocols that Mozilla does support, sometimes
 2103                 * the browser will call NPP_NewStream straight away, some
 2104                 * times it wont (depends on the nature of the tag). So that
 2105                 * it works in all cases call NPP_GetURL, this may result
 2106                 * in NPP_NewStream() being called twice (i.e. if this is an
 2107                 * embed tag with src attribute or object tag with data
 2108                 * attribute) */
 2109                if (mode == NP_EMBED)
 2110                {
 2111                     const NPError retVal = NPN_GetURL(instance, url, 0);
 2112                     if(retVal != NPERR_NO_ERROR)
 2113                     {
 2114                          D("NPN_GetURL(%s) failed with %i\n", url, retVal);
 2115 
 2116                      fprintf(stderr, "MozPlugger: Warning: Couldn't get"
 2117                                  "%s\n", url);
 2118                          return NPERR_GENERIC_ERROR;
 2119                     }
 2120                }
 2121           }
 2122      }
 2123 
 2124      D("New finished\n");
 2125 
 2126      return NPERR_NO_ERROR;
 2127 }
 2128 
 2129 /**
 2130  * Send the SHUTDOWN_MSG to the child process
 2131  *
 2132  * @param[in] pipeFd The pipe fd
 2133  * @param[in] pip The process ID
 2134  *
 2135  */
 2136 void sendShutdownMsg(int pipeFd, pid_t pid)
 2137 {
 2138      if(pipeFd >= 0)
 2139      {
 2140           PipeMsg_t msg;
 2141           ssize_t ret;
 2142 
 2143           msg.msgType = SHUTDOWN_MSG;
 2144 
 2145           D("Writing SHUTDOWN_MSG to fd %d\n", pipeFd);
 2146           ret = write(pipeFd, (char *) &msg, sizeof(msg));
 2147           if(ret == sizeof(msg))
 2148           {
 2149                if(pid >= 0)
 2150                {
 2151                     int i;
 2152                     for(i = 0; i < 5; i++)
 2153                     {
 2154                          int status;
 2155                          if(waitpid(pid, &status, WNOHANG) != 0)
 2156                          {
 2157                               pid = 0;
 2158                               break;
 2159                          }
 2160                          usleep(100000);
 2161                      }
 2162                }
 2163           }
 2164           else
 2165           {
 2166                D("Writing to comms pipe failed\n");
 2167           }
 2168           close(pipeFd); /* this will cause a broken pipe in the helper! */
 2169      }
 2170 
 2171      /* By this point the child should be dead, but just in case...*/
 2172      if(pid > 0)
 2173      {
 2174           int status;
 2175           if(kill(pid, SIGTERM) == 0)
 2176           {
 2177                usleep(100000);
 2178                kill(pid, SIGKILL);
 2179           }
 2180           waitpid(pid, &status, 0);
 2181      }
 2182 }
 2183 
 2184 
 2185 /**
 2186  * Free data, kill processes, it is time for this instance to die.
 2187  *
 2188  * @param[in] instance Pointer to the plugin instance data
 2189  * @param[out] save Pointer to any data to be saved (none in this case)
 2190  *
 2191  * @return Returns error code if a problem
 2192  */
 2193 NPError NPP_Destroy(NPP instance, NPSavedData** save)
 2194 {
 2195      data_t * THIS;
 2196 
 2197      D("NPP_Destroy(%p)\n", instance);
 2198 
 2199      if (!instance)
 2200      {
 2201       return NPERR_INVALID_INSTANCE_ERROR;
 2202      }
 2203 
 2204      THIS = instance->pdata;
 2205      if (THIS)
 2206      {
 2207           sendShutdownMsg(THIS->commsPipeFd, THIS->pid);
 2208           if(THIS->tmpFileFd >= 0)
 2209           {
 2210                close(THIS->tmpFileFd);
 2211           }
 2212           if(THIS->tmpFileName != 0)
 2213           {
 2214                char * p;
 2215                D("Deleting temp file '%s'\n", THIS->tmpFileName);
 2216 
 2217                chmod(THIS->tmpFileName, 0600);
 2218                unlink(THIS->tmpFileName);
 2219                p = strrchr(THIS->tmpFileName, '/');
 2220                if(p)
 2221                {
 2222                     *p = '\0';
 2223                     D("Deleting temp dir '%s'\n", THIS->tmpFileName);
 2224                     rmdir(THIS->tmpFileName);
 2225                }
 2226                NPN_MemFree((char *)THIS->tmpFileName);
 2227           }
 2228           if(THIS->args)
 2229           {
 2230                int e;
 2231            for (e = 0; e < THIS->num_arguments; e++)
 2232            {
 2233                 NPN_MemFree((char *)THIS->args[e].name);
 2234                 NPN_MemFree((char *)THIS->args[e].value);
 2235            }
 2236            NPN_MemFree((char *)THIS->args);
 2237           }
 2238 
 2239           if(THIS->mimetype)
 2240           {
 2241            NPN_MemFree(THIS->mimetype);
 2242           }
 2243 
 2244           if(THIS->urlFragment)
 2245           {
 2246                NPN_MemFree(THIS->urlFragment);
 2247           }
 2248 
 2249       NPN_MemFree(instance->pdata);
 2250       instance->pdata = NULL;
 2251      }
 2252 
 2253      D("Destroy finished\n");
 2254 
 2255      return NPERR_NO_ERROR;
 2256 }
 2257 
 2258 /**
 2259  * Check that no child is already running before forking one.
 2260  *
 2261  * @param[in] instance Pointer to the plugin instance data
 2262  * @param[in] fname The filename of the embedded object
 2263  * @param[in] isURL Is the filename a URL?
 2264  *
 2265  * @return Nothing
 2266  */
 2267 static void new_child(NPP instance, const char* fname, int isURL)
 2268 {
 2269      int commsPipe[2];
 2270      data_t * THIS;
 2271      sigset_t set;
 2272      sigset_t oset;
 2273 
 2274      D("NEW_CHILD(%s)\n", fname ? fname : "NULL");
 2275 
 2276      if(fname == NULL)
 2277      {
 2278           return;
 2279      }
 2280 
 2281      THIS = instance->pdata;
 2282 
 2283      if (THIS->pid != -1)
 2284      {
 2285           D("Child already running\n");
 2286       return;
 2287      }
 2288 
 2289      /* Guard against spawning helper if no command! */
 2290      if(THIS->command == 0)
 2291      {
 2292           D("Child has no command\n");
 2293           return;
 2294      }
 2295 
 2296      if(!safeName(fname, isURL)
 2297              || (THIS->urlFragment && !safeName(THIS->urlFragment, 0)))
 2298      {
 2299       reportError(instance, "MozPlugger: Detected unsafe URL aborting!");
 2300       return;
 2301      }
 2302 
 2303      if (socketpair(AF_UNIX, SOCK_STREAM, 0, commsPipe) < 0)
 2304      {
 2305       reportError(instance, "MozPlugger: Failed to create a pipe!");
 2306       return;
 2307      }
 2308 
 2309      /* Mask all the signals to avoid being interrupted by a signal */
 2310      sigfillset(&set);
 2311      sigprocmask(SIG_SETMASK, &set, &oset);
 2312 
 2313      D(">>>>>>>>Forking<<<<<<<<\n");
 2314 
 2315      THIS->pid = fork();
 2316      if(THIS->pid == 0)
 2317      {
 2318           int signum;
 2319           int i;
 2320           int maxFds;
 2321           const int commsFd = commsPipe[1];
 2322 
 2323       alarm(0);
 2324 
 2325       for (signum=0; signum < NSIG; signum++)
 2326           {
 2327            signal(signum, SIG_DFL);
 2328           }
 2329 
 2330       close_debug();
 2331 
 2332   /* Close all those File descriptors inherited from the
 2333    * parent, except the pipes and stdin, stdout, stderr */
 2334 
 2335           maxFds = sysconf(_SC_OPEN_MAX);
 2336           for(i = 3; i < maxFds; i++)
 2337           {
 2338               if(i != commsFd)
 2339               {
 2340                    close(i);
 2341               }
 2342           }
 2343           D("Closed up to %i Fds, except %i\n", maxFds, commsFd);
 2344 
 2345           /* Restore the signal mask */
 2346           sigprocmask(SIG_SETMASK, &oset, &set);
 2347 
 2348       run(THIS, fname, commsFd);
 2349 
 2350       _exit(EX_UNAVAILABLE); /* Child exit, that's OK */
 2351      }
 2352 
 2353      /* Restore the signal mask */
 2354      sigprocmask(SIG_SETMASK, &oset, &set);
 2355 
 2356      if(THIS->pid == -1)
 2357      {
 2358       reportError(instance, "MozPlugger: Failed to fork helper!");
 2359      }
 2360 
 2361      D("Child running with pid=%d\n", THIS->pid);
 2362 
 2363      THIS->commsPipeFd = commsPipe[0];
 2364      close(commsPipe[1]);
 2365 }
 2366 
 2367 /**
 2368  * Whilst creating a pdf watch out for characters that may
 2369  * cause issues...
 2370  *
 2371  * @param[in,out] string the string to be escaped
 2372  */
 2373 static void escapeBadChars(char * string)
 2374 {
 2375      for(;*string; string++)
 2376      {
 2377           char ch = *string;
 2378           if((ch == ';') || (ch == '`') || (ch == '&') ||
 2379                                       (ch == ' ') || (ch == '\t'))
 2380           {
 2381                *string = '_';
 2382           }
 2383      }
 2384 }
 2385 
 2386 /**
 2387  * Guess a temporary file name
 2388  *
 2389  * Creates a temporary file name based on the fileName provided. Checks that
 2390  * the filename created does not include any dangereous or awkward characters.
 2391  *
 2392  * @param[in] fileName Pointer to url string
 2393  *
 2394  * @return file descriptor
 2395  */
 2396 static int guessTmpFile(const char * fileName, int soFar,
 2397                                      char * tmpFilePath, int maxTmpFilePathLen)
 2398 {
 2399      int i = 0;
 2400      int fd = -1;
 2401      int spaceLeft = maxTmpFilePathLen - soFar - 1;
 2402      const int maxNameLen = pathconf(tmpFilePath, _PC_NAME_MAX);
 2403      const int fileNameLen = strlen(fileName);
 2404 
 2405      if(spaceLeft > maxNameLen)
 2406      {
 2407          spaceLeft = maxNameLen;
 2408      }
 2409      tmpFilePath[soFar++] = '/';
 2410 
 2411      while(1)
 2412      {
 2413           if(i < 100)
 2414       {
 2415                int n = 0;
 2416                int pos = 0;
 2417                if(i > 0)
 2418                {
 2419                 n = snprintf(&tmpFilePath[soFar], spaceLeft, "%03i-", i);
 2420                } 
 2421            if(fileNameLen > (spaceLeft-n))
 2422                {
 2423                     pos = fileNameLen - (spaceLeft-n);
 2424                }
 2425            strcpy(&tmpFilePath[soFar+n], &fileName[pos]);
 2426           }
 2427       else
 2428       {
 2429                strncpy(&tmpFilePath[soFar], "XXXXXX", spaceLeft);
 2430                fd = mkstemp(tmpFilePath);
 2431            break;
 2432       }
 2433 
 2434           escapeBadChars(&tmpFilePath[soFar]);
 2435 
 2436       fd = open(tmpFilePath, O_CREAT | O_EXCL | O_WRONLY,
 2437               S_IRUSR | S_IWUSR);
 2438       if(fd >= 0)
 2439       {
 2440            break;
 2441       }
 2442           i++;
 2443      }
 2444 
 2445      return fd;
 2446 }
 2447 
 2448 /**
 2449  * From the url create a temporary file to hold a copy of th URL contents.
 2450  *
 2451  * @param[in] fileName Pointer to url string
 2452  * @param[out] tmpFileName Pointer to place tmp file name string
 2453  * @param[in] maxTmpFileLen
 2454  *
 2455  * @return -1 on error or file descriptor
 2456  */
 2457 static int createTmpFile(char ** pFileName)
 2458 {
 2459      char tmpFilePath[512];
 2460      int fd = -1;
 2461      const char * root;
 2462      const pid_t pid = getpid();
 2463 
 2464      D("Creating temp file for '%s'\n", *pFileName);
 2465 
 2466      root = getenv("MOZPLUGGER_TMP");
 2467      if(root)
 2468      {
 2469           int soFar;
 2470 
 2471           strncpy(tmpFilePath, root, sizeof(tmpFilePath)-1);
 2472           soFar = strlen(tmpFilePath);
 2473 
 2474       soFar += snprintf(&tmpFilePath[soFar], sizeof(tmpFilePath)-soFar,
 2475                                                                 "/tmp-%i", pid);
 2476           if( (mkdir(tmpFilePath, S_IRWXU) == 0) || (errno == EEXIST))
 2477           {
 2478                D("Creating temp file in '%s'\n", tmpFilePath);
 2479 
 2480            fd = guessTmpFile(*pFileName, soFar, tmpFilePath, sizeof(tmpFilePath)-1);
 2481           }
 2482      }
 2483 
 2484      if(fd < 0)
 2485      {
 2486           root = getenv("TMPDIR");
 2487           if(!root)
 2488           {
 2489                root = "/tmp";
 2490           }
 2491 
 2492           snprintf(tmpFilePath, sizeof(tmpFilePath), "%s/mozplugger-%i",
 2493                                                                      root, pid);
 2494           if((mkdir(tmpFilePath, S_IRWXU) == 0) || (errno == EEXIST))
 2495           {
 2496                int soFar = strlen(tmpFilePath);
 2497 
 2498                D("Creating temp file in '%s'\n", tmpFilePath);
 2499 
 2500            fd = guessTmpFile(*pFileName, soFar, tmpFilePath, sizeof(tmpFilePath)-1);
 2501           }
 2502      }
 2503      NPN_MemFree(*pFileName);
 2504 
 2505      if(fd >= 0)
 2506      {
 2507           D("Opened temporary file '%s'\n", tmpFilePath);
 2508           *pFileName = NP_strdup(tmpFilePath);
 2509      }
 2510      else
 2511      {
 2512           *pFileName = NULL;
 2513      }
 2514      return fd;
 2515 }
 2516 
 2517 /**
 2518  * Open a new stream.
 2519  * Each instance can only handle one stream at a time.
 2520  *
 2521  * @param[in] instance Pointer to the plugin instance data
 2522  * @param[in] type The mime type
 2523  * @param[in] stream Pointer to the stream data structure
 2524  * @param[in] seekable Flag to say if stream is seekable
 2525  * @param[out] stype How the plugin will handle the stream
 2526  *
 2527  * @return Returns error code if a problem
 2528  */
 2529 NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream *stream,
 2530               NPBool seekable, uint16_t *stype)
 2531 {
 2532      char * fileName = NULL;
 2533      char * savedMimetype = NULL;
 2534      data_t * THIS;
 2535      char refind_command = 0;
 2536 
 2537      D("NPP_NewStream(%p)\n", instance);
 2538 
 2539      if (instance == NULL)
 2540      {
 2541       return NPERR_INVALID_INSTANCE_ERROR;
 2542      }
 2543 
 2544      THIS = instance->pdata;
 2545 
 2546      /* Looks like browser can handle this stream so we can clear the flag */
 2547      THIS->browserCantHandleIt = 0;
 2548 
 2549      if((THIS->pid != -1) || (THIS->tmpFileFd >= 0))
 2550      {
 2551           D("NewStream() exiting process already running\n");
 2552       return NPERR_GENERIC_ERROR;
 2553      }
 2554 
 2555      /*  Replace the stream's URL with the URL in THIS->href if it
 2556       *  exists. */
 2557      if(THIS->href != NULL)
 2558      {
 2559       D("Replacing SRC with HREF... \n");
 2560 
 2561           if((THIS->url == 0) || (strcmp(THIS->href, THIS->url) != 0))
 2562           {
 2563                /* URL has changed */
 2564                D("URL has changed to %s\n", THIS->href);
 2565                THIS->url = THIS->href;
 2566                refind_command = 1;
 2567           }
 2568      }
 2569      else if((THIS->url == 0) || (strcmp(stream->url, THIS->url) != 0))
 2570      {
 2571           /* URL has changed */
 2572           D("URL has changed to %s\n", stream->url);
 2573           THIS->url = (char *) stream->url;
 2574           refind_command = 1;
 2575      }
 2576 
 2577      D("Url is %s (seekable=%d)\n", THIS->url, seekable);
 2578 
 2579      /* Ocassionally the MIME type here is different to that passed to the
 2580       * NEW function - this is because of either badly configure web server
 2581       * who's HTTP response content-type does not match the mimetype in the
 2582       * preceding embebbed, object or link tag. Or badly constructed embedded
 2583       * tag or ambiguity in the file extension to mime type mapping. Lets
 2584       * first assume the HTTP response was correct and if not fall back to
 2585       * the original tag in the mime type. */
 2586      if(strcmp(type, THIS->mimetype) != 0)
 2587      {
 2588           D("Mismatching mimetype reported, originally was \'%s\' now '\%s' "
 2589                           "for url %s\n", THIS->mimetype, type, THIS->url);
 2590           savedMimetype = THIS->mimetype;
 2591           THIS->mimetype = NP_strdup(type);
 2592 
 2593           if(!(THIS->command = find_command(THIS, 0)))
 2594           {
 2595                NPN_MemFree(THIS->mimetype);
 2596                THIS->mimetype = savedMimetype;
 2597                THIS->command = find_command(THIS, 0);
 2598           }
 2599           else
 2600           {
 2601                NPN_MemFree(savedMimetype);
 2602           }
 2603      }
 2604      else if(refind_command)
 2605      {
 2606           THIS->command = find_command(THIS, 0);
 2607           D("Mime type %s\n", type);
 2608      }
 2609 
 2610      if(THIS->command == 0)
 2611      {
 2612       reportError(instance, "MozPlugger: No appropriate application found.");
 2613       return NPERR_GENERIC_ERROR;
 2614      }
 2615 
 2616      /* Extract from the URL the various additional information */
 2617      fileName = parseURL(THIS, 1);
 2618      D("fileName (pre-header parse) = %s\n", fileName);
 2619 
 2620      /* Extract the fileName from HTTP headers, overide URL fileName */
 2621      fileName = parseHeaders(THIS, stream->headers, fileName);
 2622      D("fileName = %s\n", fileName);
 2623 
 2624      if( (THIS->command->flags & H_STREAM) == 0)
 2625      {
 2626           THIS->tmpFileFd = createTmpFile(&fileName);
 2627 
 2628           if(THIS->tmpFileFd < 0)
 2629           {
 2630            reportError(instance, "MozPlugger: Failed to create tmp file");
 2631            return NPERR_GENERIC_ERROR;
 2632           }
 2633           else
 2634           {
 2635                /* Make file read only by us only */
 2636                fchmod(THIS->tmpFileFd, 0400);
 2637                THIS->tmpFileName = fileName;
 2638                THIS->tmpFileSize = 0;
 2639           }
 2640      }
 2641      else
 2642      {
 2643           NPN_MemFree(fileName);
 2644           new_child(instance, THIS->url, 1);
 2645      }
 2646 
 2647      *stype = NP_NORMAL;
 2648      return NPERR_NO_ERROR;
 2649 }
 2650 
 2651 /**
 2652  * Called after NPP_NewStream if *stype = NP_ASFILEONLY.
 2653  *
 2654  * @param[in] instance Pointer to plugin instance data
 2655  * @param[in] stream Pointer to the stream data structure
 2656  * @param[in] fname Name of the file to stream
 2657  *
 2658  * @return none
 2659  */
 2660 void NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname)
 2661 {
 2662      D("NPP_StreamAsFile(%p, %p, %s)\n", instance, stream, fname);
 2663 
 2664      if (instance != NULL)
 2665      {
 2666           new_child(instance, fname, 0);
 2667      }
 2668 }
 2669 
 2670 /**
 2671  * The browser should have resized the window for us, but this function was
 2672  * added because of a bug in Mozilla 1.7 (see Mozdev bug #7734) and
 2673  * https://bugzilla.mozilla.org/show_bug.cgi?id=201158
 2674  *
 2675  * Bug was fixed in Mozilla CVS repositary in version 1.115 of
 2676  * ns4xPluginInstance.cpp (13 Nov 2003), at the time version was 0.13.
 2677  * version 0.14 happened on 14th July 2004
 2678  *
 2679  * @param[in] dpy The display pointer
 2680  * @param[in] win The window ID
 2681  * @param[in] width The width of the window
 2682  * @param[in] height The height of the window
 2683  *
 2684  * @return none
 2685  */
 2686 static void resize_window(Display * dpy, Window win, unsigned width, unsigned height)
 2687 {
 2688      if(does_browser_have_resize_bug() && ((unsigned)win != 0))
 2689      {
 2690           XSetWindowAttributes attrib;
 2691 
 2692           attrib.override_redirect = True;
 2693           XChangeWindowAttributes(dpy, win, (unsigned long) CWOverrideRedirect, &attrib);
 2694 
 2695           D("Bug #7734 work around - resizing WIN 0x%x to %ux%u!?\n",
 2696                 (unsigned) win, width, height);
 2697 
 2698           XResizeWindow(dpy, win, width, height);
 2699      }
 2700 }
 2701 
 2702 /**
 2703  * Send the WINDOW_MSG to the child process
 2704  *
 2705  * @param[in] THIS The instance
 2706  * @param[in] Window The window ID
 2707  * @param[in] width The new window width
 2708  * @param[in] height The new window height
 2709  *
 2710  */
 2711 void sendWindowMsg(data_t * THIS)
 2712 {
 2713      if(THIS->commsPipeFd >= 0)
 2714      {
 2715           PipeMsg_t msg;
 2716           ssize_t ret;
 2717 
 2718           msg.msgType = WINDOW_MSG;
 2719           msg.window_msg.window = THIS->window;
 2720           msg.window_msg.width = THIS->width;
 2721           msg.window_msg.height = THIS->height;
 2722 
 2723           D("Sending WIN MSG to helper (win=0x%x - %u x %u)\n",
 2724                                   (unsigned) THIS->window, THIS->width, THIS->height);
 2725 
 2726           ret = write(THIS->commsPipeFd, (char *) &msg, sizeof(msg));
 2727           if(ret < sizeof(msg))
 2728           {
 2729                D("Writing to comms pipe failed\n");
 2730                close(THIS->commsPipeFd);
 2731                THIS->commsPipeFd = -1;
 2732           }
 2733     }
 2734 }
 2735 
 2736 
 2737 /**
 2738  * The browser calls NPP_SetWindow after creating the instance to allow drawing
 2739  * to begin. Subsequent calls to NPP_SetWindow indicate changes in size or
 2740  * position. If the window handle is set to null, the window is destroyed. In
 2741  * this case, the plug-in must not perform any additional graphics operations
 2742  * on the window and should free any associated resources.
 2743  *
 2744  * @param[in] instance Pointer to plugin instance data
 2745  * @param[in] window Pointer to NPWindow data structure
 2746  *
 2747  * @return Returns error code if problem
 2748  */
 2749 NPError NPP_SetWindow(NPP instance, NPWindow* window)
 2750 {
 2751      data_t * THIS;
 2752      D("NPP_SetWindow(%p)\n", instance);
 2753 
 2754      if(!instance)
 2755      {
 2756           D("NPP_SetWindow, ERROR NULL instance\n");
 2757       return NPERR_INVALID_INSTANCE_ERROR;
 2758      }
 2759 
 2760      if(!window)
 2761      {
 2762           D("NPP_SetWindow, WARN NULL window\n");
 2763       return NPERR_NO_ERROR;
 2764      }
 2765 
 2766      THIS = instance->pdata;
 2767 
 2768      if(!window->ws_info)
 2769      {
 2770           D("NPP_SetWindow, WARN NULL display\n");
 2771       return NPERR_NO_ERROR;
 2772      }
 2773 
 2774      if(!window->window)
 2775      {
 2776           D("NPP_SetWindow, WARN zero window ID\n");
 2777      }
 2778 
 2779      THIS->display = ((NPSetWindowCallbackStruct *)window->ws_info)->display;
 2780 
 2781      THIS->window = (Window) window->window;
 2782      THIS->width = window->width;
 2783      THIS->height = window->height;
 2784 
 2785      if ((THIS->url) && (THIS->browserCantHandleIt))
 2786      {
 2787           if(THIS->command == 0)
 2788           {
 2789                /* Can only use streaming commands, as Mozilla cannot handle
 2790                 * these types (mms) of urls */
 2791                if (!(THIS->command = find_command(THIS, 1)))
 2792                {
 2793                     if(haveError())
 2794                     {
 2795                          NPN_Status(instance, errMsg);
 2796                          clearError();
 2797                     }
 2798                     else
 2799                     {
 2800                      reportError(instance, "MozPlugger: No appropriate application found.");
 2801                     }
 2802                 return NPERR_GENERIC_ERROR;
 2803                }
 2804           }
 2805 
 2806           /* Extract from the URL the various additional information */
 2807           (void) parseURL(THIS, 0);
 2808 
 2809       new_child(instance, THIS->url, 1);
 2810           THIS->url = NULL; /* Stops new_child from being called again */
 2811       return NPERR_NO_ERROR;
 2812      }
 2813 
 2814      sendWindowMsg(THIS);
 2815 
 2816      resize_window(THIS->display, THIS->window, THIS->width, THIS->height);
 2817 
 2818      /* In case Mozilla would call NPP_SetWindow() in a loop. */
 2819      usleep(4000);
 2820 
 2821 //     get_browser_toolkit(instance);
 2822 //     does_browser_support_key_handling(instance);
 2823 
 2824      return NPERR_NO_ERROR;
 2825 }
 2826 
 2827 /**
 2828  * Called from browser when there is an event to be passed to the plugin. Only applicabe for
 2829  * windowless plugins
 2830  *
 2831  * @param[in] instance The instance pointer
 2832  * @param[in] event The event
 2833  *
 2834  * @return ??
 2835  */
 2836 int16_t NPP_HandleEvent(NPP instance, void* event)
 2837 {
 2838      D("NPP_HandleEvent(%p)\n", instance);
 2839      return 0;
 2840 }
 2841 
 2842 /**
 2843  * Send progress message to helper
 2844  */
 2845 static void sendProgressMsg(data_t * THIS)
 2846 {
 2847      if(THIS->commsPipeFd >= 0)
 2848      {
 2849           int ret;
 2850           PipeMsg_t msg;
 2851 
 2852           msg.msgType = PROGRESS_MSG;
 2853           msg.progress_msg.done = (THIS->tmpFileFd < 0);
 2854           msg.progress_msg.bytes = THIS->tmpFileSize;
 2855 
 2856           ret = write(THIS->commsPipeFd, (char *) &msg, sizeof(msg));
 2857           if(ret < sizeof(msg))
 2858           {
 2859                D("Writing to comms pipe failed\n");
 2860                close(THIS->commsPipeFd);
 2861                THIS->commsPipeFd = -1;
 2862           }
 2863      }
 2864 }
 2865 
 2866 /**
 2867  * Called from the Browser when the streaming has been completed by the Browser
 2868  * (the reason code indicates whether this was due to a User action, Network
 2869  * issue or that streaming has been done.
 2870  *
 2871  * @param[in] instance Pointer to plugin instance data
 2872  * @param[in] stream Pointer to the stream data structure
 2873  * @param[in] reason Reason for stream being destroyed
 2874  *
 2875  * @return Returns error code if a problem
 2876  */
 2877 NPError NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason)
 2878 {
 2879      data_t * THIS;
 2880      D("NPP_DestroyStream(%p, %p, %i)\n", instance, stream, reason);
 2881 
 2882      if (!instance)
 2883      {
 2884           return NPERR_INVALID_INSTANCE_ERROR;
 2885      }
 2886 
 2887      THIS = instance->pdata;
 2888 
 2889      if(THIS->tmpFileFd >= 0)
 2890      {
 2891           close(THIS->tmpFileFd);
 2892           THIS->tmpFileFd = -1;
 2893 
 2894           if( THIS->tmpFileName != NULL)
 2895           {
 2896                D("Closing Temporary file \'%s\'\n", THIS->tmpFileName);
 2897                if(THIS->commsPipeFd < 0)   /* is no helper? */
 2898                {
 2899                     new_child(instance, THIS->tmpFileName, 0);
 2900                }
 2901           }
 2902 
 2903           sendProgressMsg(THIS);
 2904      }
 2905      return NPERR_NO_ERROR;
 2906 }
 2907 
 2908 /**
 2909  * The browser calls this function only once; when the plug-in is loaded,
 2910  * before the first instance is created. NPP_Initialize tells the plug-in that
 2911  * the browser has loaded it.
 2912  *
 2913  * @param[in] magic references for this particular plugin type
 2914  * @param[in] nsTable The table of NPN functions
 2915  * @param[out] pluginFuncs On return contains the NPP functions
 2916  *
 2917  * @return Returns error code if a problem
 2918  */
 2919 NPError NP2_Initialize(const char * magic,
 2920                   const NPNetscapeFuncs * nsTable, NPPluginFuncs * pluginFuncs)
 2921 {
 2922      NPError err;
 2923      D("NP_Initialize(%.20s)\n", magic);
 2924 
 2925      if( (err = NPN_InitFuncTable(nsTable)) == NPERR_NO_ERROR)
 2926      {
 2927           if( (err = NPP_InitFuncTable(pluginFuncs)) == NPERR_NO_ERROR)
 2928           {
 2929                 get_api_version();
 2930 
 2931                 if(!do_read_config(magic))
 2932                 {
 2933                      err = NPERR_GENERIC_ERROR;
 2934                 }
 2935                 else
 2936                 {
 2937                      const int free = MAX_STATIC_MEMORY_POOL - staticPoolIdx;
 2938                      D("Static Pool used=%i, free=%i\n", staticPoolIdx, free);
 2939                 }
 2940           }
 2941      }
 2942      return err;
 2943 }
 2944 
 2945 /**
 2946  * The browser calls this function just before it unloads the plugin from
 2947  * memory. So this function should do any tidy up - in this case nothing is
 2948  * required.
 2949  *
 2950  * @param[in] magic references for this particular plugin type
 2951  *
 2952  * @return none
 2953  */
 2954 NPError NP2_Shutdown(const char * magic)
 2955 {
 2956      D("NP_Shutdown(%.20s)\n", magic);
 2957      return NPERR_NO_ERROR;
 2958 }
 2959 
 2960 /**
 2961  * Called when user as requested to print the webpage that contains a visible
 2962  * plug-in. For mozplugger this is ignored.
 2963  *
 2964  * @param[in] instance Pointer to the plugin instance data
 2965  * @param[in] printInfo Pointer to the print info data structure
 2966  *
 2967  * @return none
 2968  */
 2969 void NPP_Print(NPP instance, NPPrint* printInfo)
 2970 {
 2971      D("NPP_Print(%p)\n", instance);
 2972 }
 2973 
 2974 /**
 2975  * Called when the Browser wishes to deliver a block of data from a stream to
 2976  * the plugin. Since streaming is handled directly by the application specificed
 2977  * in the configuration file, mozplugger has no need for this data. Here it
 2978  * just pretends the data has been taken by returning 'len' to indicate all
 2979  * bytes consumed. Actaully this function should never be called by the
 2980  * Browser.
 2981  *
 2982  * @param[in] instance Pointer to the plugin instance data
 2983  * @param[in] stream Pointer to the stream data structure
 2984  * @param[in] offset Where the data starts in 'buf'
 2985  * @param[in] len The amount of data
 2986  * @param[in] buf The data to be delivered
 2987  *
 2988  * @return Always returns value of passed in 'len'
 2989  */
 2990 int32_t NPP_Write(NPP instance, NPStream *stream, int32_t offset, int32_t len,
 2991           void * buf)
 2992 {
 2993      D("NPP_Write(%p, %p, %d, %d)\n", instance, stream, offset, (int) len);
 2994      if(instance)
 2995      {
 2996           data_t * const THIS = instance->pdata;
 2997 
 2998           if(THIS->tmpFileFd >= 0)   /* is tmp file open? */
 2999           {
 3000                if(offset != THIS->tmpFileSize)
 3001                {
 3002                    D("Strange, there's a gap?\n");
 3003                }
 3004                len = write(THIS->tmpFileFd, buf, len);
 3005                THIS->tmpFileSize += len;
 3006                D("Temporary file size now=%i\n", THIS->tmpFileSize);
 3007           }
 3008 
 3009           sendProgressMsg(THIS);
 3010      }
 3011      return len;
 3012 }
 3013 
 3014 /**
 3015  * Browser calls this function before calling NPP_Write to see how much data
 3016  * the plugin is ready to accept.
 3017  *
 3018  * @param[in] instance Pointer to the plugin instance data
 3019  * @param[in] stream Pointer to the stream data structure
 3020  *
 3021  * @return CHUNK_SIZE or zero
 3022  */
 3023 int32_t NPP_WriteReady(NPP instance, NPStream *stream)
 3024 {
 3025      int32_t size = 0;
 3026 
 3027      D("NPP_WriteReady(%p, %p)\n", instance, stream);
 3028      if (instance != 0)
 3029      {
 3030           data_t * const THIS = instance->pdata;
 3031 
 3032           if(THIS->tmpFileFd >= 0)  /* is tmp file is open? */
 3033           {
 3034                size = CHUNK_SIZE;
 3035           }
 3036           else
 3037           {
 3038                D("Nothing to do - Application will handle stream\n");
 3039            /* Tell the browser that it can finish with the stream
 3040               (actually we just wanted the name of the stream!)
 3041               And not the stream data. */
 3042                NPN_DestroyStream(instance, stream, NPRES_DONE);
 3043           }
 3044      }
 3045      return size;
 3046 }
 3047 
 3048 /**
 3049  * Browser calls this function to notify when a GET or POST has completed
 3050  * Currently not used by mozplugger
 3051  *
 3052  * @param[in] instance Pointer to the plugin instance data
 3053  * @param[in] url The URL that was GET or POSTed
 3054  * @param[in] reason The reason for the notify event
 3055  * @param[in] notifyData Data that was passed in the original call to Get or Post URL
 3056  *
 3057  * @return none
 3058  */
 3059 void NPP_URLNotify(NPP instance, const char * url, NPReason reason, void * notifyData)
 3060 {
 3061      D("NPP_URLNotify(%p, %s, %i)\n", instance, url, reason);
 3062 }
 3063 
 3064 /**
 3065  * Called by the browser when the browser intends to focus an instance.
 3066  * Instance argument indicates the instance getting focus.
 3067  * Direction argument indicates the direction in which focus advanced to the instance.
 3068  * Return value indicates whether or not the plugin accepts focus.
 3069  * Currently not used by mozplugger
 3070  *
 3071  * @param[in] instance Pointer to the plugin instance data
 3072  * @param[in] direction The advancement direction
 3073  *
 3074  * @return True or False
 3075  */
 3076 NPBool NPP_GotFocus(NPP instance, NPFocusDirection direction)
 3077 {
 3078      D("NPP_GotFocus(%p, %i)\n", instance, direction);
 3079      return false;
 3080 }
 3081 
 3082 /**
 3083  * Called by the browser when the browser intends to take focus.
 3084  * Instance argument indicates the instances losing focus.
 3085  * There is no return value, plugins will lose focus when this is called.
 3086  * Currently not used by mozplugger
 3087  *
 3088  * @param[in] instance Pointer to the plugin instance data
 3089  *
 3090  * @return True or False
 3091  */
 3092 void NPP_LostFocus(NPP instance)
 3093 {
 3094      D("NPP_LostFocus(%p)\n", instance);
 3095 }
 3096 
 3097 /**
 3098  * Currently not used by mozplugger
 3099  *
 3100  * @param[in] instance Pointer to the plugin instance data
 3101  * @param[in] url The URL that was GET or POSTed
 3102  * @param[in] status ??
 3103  * @param[in] notifyData Data that was passed in the original call to Get or Post URL
 3104  *
 3105  * @return None
 3106  */
 3107 void NPP_URLRedirectNotify(NPP instance, const char * url, int32_t status, void * noifyData)
 3108 {
 3109      D("NPP_URLRedirectNotify(%p, %s, %i)\n", instance, url, status);
 3110 }
 3111 
 3112 /**
 3113  * Clear site data held by plugin (should this clear tmp files?)
 3114  * Currently not used by mozplugger
 3115  *
 3116  * @param[in] site The site name
 3117  * @param[in] flags
 3118  * @param[in] maxAge
 3119  *
 3120  * @return Error status
 3121  */
 3122 NPError NPP_ClearSiteData(const char * site, uint64_t flags, uint64_t maxAge)
 3123 {
 3124      D("NPP_ClearSiteData(%s)\n", site);
 3125      return NPERR_NO_ERROR;
 3126 }
 3127 
 3128 /**
 3129  * Get list of sites plugin has data for (should this be list of tmp files?)
 3130  * Currently not used by mozplugger
 3131  *
 3132  * @return List of sites plugin has data for.
 3133  */
 3134 char ** NPP_GetSitesWithData(void)
 3135 {
 3136      D("NPP_GetSitesWithData()\n");
 3137      return 0;
 3138 }