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 */
my_putenv(char * buffer,int bufLen,int offset,const char * var,const char * value)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 */
my_putenv_unsigned(char * buffer,int bufLen,int offset,const char * var,unsigned long value)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 */
my_putenv_hex(char * buffer,int bufLen,int offset,const char * var,unsigned long value)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 */
my_putenv_signed(char * buffer,int bufLen,int offset,const char * var,long value)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 */
reportError(NPP instance,const char * fmt,...)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
clearError(void)276 static void clearError(void)
277 {
278 errMsg[0] = 0;
279 }
280
haveError(void)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 */
allocStaticMem(int size)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 */
makeStrStatic(const char * str,int len)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 */
trim_trailing_spaces(char * buf)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 */
chkCfgLine(char * buffer)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
is_base_mozplugger(const char * magic)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 */
get_home_dir(void)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 */
get_cfg_path_prefix(const char * magic,char * buf,int bufLen)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 */
get_helper_paths(const char * magic)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 */
get_cmds_cfg_path(const char * magic)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 */
get_mimetypes_cfg_path(const char * magic)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 */
run(data_t * const THIS,const char * file,int pipeFd)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
NP_strdup2(const char * str,int len)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 */
NP_strdup(const char * str)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 */
isCfgMimeType(char * buffer)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 */
parseCfgMimeType(char * buffer)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 */
parseCfgCmdLine(char * buffer)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 */
read_config(FILE * f)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 */
do_read_config(const char * magic)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 */
safeName(const char * name,int isURL)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 */
parseHeaders(data_t * const THIS,const char * headers,char * fileName)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 */
parseURL(data_t * const THIS,int extract_filename)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
match_url(const char * matchStr,const char * url)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
match_command(const data_t * THIS,int streamOnly,const command_t * c)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
match_mime_type(const char * reqMimeType,mimetype_t * m)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
match_handler(const handler_t * h,const data_t * THIS,int streamOnly)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 */
find_command(const data_t * THIS,int streamOnly)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 */
NP2_GetPluginVersion(const char * magic)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 */
mozplugger_update(bool * pDoesntExist)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 */
chkValidLocalPluginDirs()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 */
chkTimeToUpdate(bool * update,bool * dont_update)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 */
chk_version_matches(char * buf)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 */
chk_cached_is_newer(char * buf,time_t cached_ftime)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
read_desc(const char * fname,time_t ts_ftime,bool * update,bool is_base)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 */
NP2_GetMIMEDescription(const char * magic)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 */
is_playing(NPP instance)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 */
getPluginName(const char * magic)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 */
getPluginDescription(const char * magic)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 */
getPluginNeedsXembed(NPP instance,NPError * pErr)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 */
NP2_GetValue(const char * magic,NPPVariable variable,void * value)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 */
NPP_GetValue(NPP instance,NPPVariable variable,void * value)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 */
NPP_SetValue(NPP instance,NPNVariable variable,void * value)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 */
my_atoi(const char * s,int my_true,int my_false)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 */
NPP_New(NPMIMEType pluginType,NPP instance,uint16_t mode,int16_t argc,char * argn[],char * argv[],NPSavedData * saved)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 */
sendShutdownMsg(int pipeFd,pid_t pid)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 */
NPP_Destroy(NPP instance,NPSavedData ** save)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 */
new_child(NPP instance,const char * fname,int isURL)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 */
escapeBadChars(char * string)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 */
guessTmpFile(const char * fileName,int soFar,char * tmpFilePath,int maxTmpFilePathLen)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 */
createTmpFile(char ** pFileName)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 */
NPP_NewStream(NPP instance,NPMIMEType type,NPStream * stream,NPBool seekable,uint16_t * stype)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 */
NPP_StreamAsFile(NPP instance,NPStream * stream,const char * fname)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 */
resize_window(Display * dpy,Window win,unsigned width,unsigned height)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 */
sendWindowMsg(data_t * THIS)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 */
NPP_SetWindow(NPP instance,NPWindow * window)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 */
NPP_HandleEvent(NPP instance,void * event)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 */
sendProgressMsg(data_t * THIS)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 */
NPP_DestroyStream(NPP instance,NPStream * stream,NPError reason)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 */
NP2_Initialize(const char * magic,const NPNetscapeFuncs * nsTable,NPPluginFuncs * pluginFuncs)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 */
NP2_Shutdown(const char * magic)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 */
NPP_Print(NPP instance,NPPrint * printInfo)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 */
NPP_Write(NPP instance,NPStream * stream,int32_t offset,int32_t len,void * buf)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 */
NPP_WriteReady(NPP instance,NPStream * stream)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 */
NPP_URLNotify(NPP instance,const char * url,NPReason reason,void * notifyData)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 */
NPP_GotFocus(NPP instance,NPFocusDirection direction)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 */
NPP_LostFocus(NPP instance)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 */
NPP_URLRedirectNotify(NPP instance,const char * url,int32_t status,void * noifyData)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 */
NPP_ClearSiteData(const char * site,uint64_t flags,uint64_t maxAge)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 */
NPP_GetSitesWithData(void)3134 char ** NPP_GetSitesWithData(void)
3135 {
3136 D("NPP_GetSitesWithData()\n");
3137 return 0;
3138 }
3139