1 /* gcjwebplugin.cc -- web browser plugin to execute Java applets
2 Copyright (C) 2003, 2004, 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
37
38 // System includes.
39 #include <errno.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <unistd.h>
45
46 // Netscape plugin API includes.
47 #include <npapi.h>
48 #if (((NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR) < 20)
49 #include <npupp.h>
50 #else
51 #include <npfunctions.h>
52 #endif
53
54 // GLib includes.
55 #include <glib.h>
56 #include <glib/gstdio.h>
57
58 // GTK includes.
59 #include <gtk/gtk.h>
60
61 // gcjwebplugin includes.
62 #include "config.h"
63
64 // Documentbase retrieval includes.
65 #include <nsIPluginInstance.h>
66 #include <nsIPluginInstancePeer.h>
67 #include <nsIPluginTagInfo2.h>
68
69 // Debugging macros.
70 #define PLUGIN_DEBUG(message) \
71 g_print ("GCJ PLUGIN: thread %p: %s\n", g_thread_self (), message)
72
73 #define PLUGIN_DEBUG_TWO(first, second) \
74 g_print ("GCJ PLUGIN: thread %p: %s %s\n", g_thread_self (), \
75 first, second)
76
77 // Error reporting macros.
78 #define PLUGIN_ERROR(message) \
79 g_printerr ("%s:%d: thread %p: Error: %s\n", __FILE__, __LINE__, \
80 g_thread_self (), message)
81
82 #define PLUGIN_ERROR_TWO(first, second) \
83 g_printerr ("%s:%d: thread %p: Error: %s: %s\n", __FILE__, __LINE__, \
84 g_thread_self (), first, second)
85
86 #define PLUGIN_ERROR_THREE(first, second, third) \
87 g_printerr ("%s:%d: thread %p: Error: %s: %s: %s\n", __FILE__, \
88 __LINE__, g_thread_self (), first, second, third)
89
90 // Plugin information passed to about:plugins.
91 #define PLUGIN_NAME "GCJ Web Browser Plugin"
92 #define PLUGIN_DESC "The " PLUGIN_NAME " executes Java applets."
93 #define PLUGIN_MIME_DESC \
94 "application/x-java-vm:class,jar:GCJ;" \
95 "application/x-java-applet:class,jar:GCJ;" \
96 "application/x-java-applet;version=1.1:class,jar:GCJ;" \
97 "application/x-java-applet;version=1.1.1:class,jar:GCJ;" \
98 "application/x-java-applet;version=1.1.2:class,jar:GCJ;" \
99 "application/x-java-applet;version=1.1.3:class,jar:GCJ;" \
100 "application/x-java-applet;version=1.2:class,jar:GCJ;" \
101 "application/x-java-applet;version=1.2.1:class,jar:GCJ;" \
102 "application/x-java-applet;version=1.2.2:class,jar:GCJ;" \
103 "application/x-java-applet;version=1.3:class,jar:GCJ;" \
104 "application/x-java-applet;version=1.3.1:class,jar:GCJ;" \
105 "application/x-java-applet;version=1.4:class,jar:GCJ;" \
106 "application/x-java-applet;version=1.4.1:class,jar:GCJ;" \
107 "application/x-java-applet;version=1.4.2:class,jar:GCJ;" \
108 "application/x-java-applet;jpi-version=1.4.2_01:class,jar:GCJ;" \
109 "application/x-java-bean:class,jar:GCJ;" \
110 "application/x-java-bean;version=1.1:class,jar:GCJ;" \
111 "application/x-java-bean;version=1.1.1:class,jar:GCJ;" \
112 "application/x-java-bean;version=1.1.2:class,jar:GCJ;" \
113 "application/x-java-bean;version=1.1.3:class,jar:GCJ;" \
114 "application/x-java-bean;version=1.2:class,jar:GCJ;" \
115 "application/x-java-bean;version=1.2.1:class,jar:GCJ;" \
116 "application/x-java-bean;version=1.2.2:class,jar:GCJ;" \
117 "application/x-java-bean;version=1.3:class,jar:GCJ;" \
118 "application/x-java-bean;version=1.3.1:class,jar:GCJ;" \
119 "application/x-java-bean;version=1.4:class,jar:GCJ;" \
120 "application/x-java-bean;version=1.4.1:class,jar:GCJ;" \
121 "application/x-java-bean;version=1.4.2:class,jar:GCJ;" \
122 "application/x-java-bean;jpi-version=1.4.2_01:class,jar:GCJ;"
123 #define PLUGIN_URL NS_INLINE_PLUGIN_CONTRACTID_PREFIX NS_JVM_MIME_TYPE
124 #define PLUGIN_MIME_TYPE "application/x-java-vm"
125 #define PLUGIN_FILE_EXTS "class,jar,zip"
126 #define PLUGIN_MIME_COUNT 1
127
128 // Security dialog messages.
129 #define RESPONSE_TRUST_APPLET "Trust Applet"
130 #define RESPONSE_TRUST_APPLET_ADD_TO_LIST "Trust Applet and Add to Whitelist"
131 #define SECURITY_WARNING \
132 "%s wants to load an applet.\n" \
133 "GNU Classpath's security implementation is not complete.\n" \
134 "HOSTILE APPLETS WILL STEAL AND/OR DESTROY YOUR DATA!\n"
135 #define SECURITY_DESCRIPTION \
136 "Click \"Cancel\" if you do not trust the source of this applet.\n" \
137 "Click \"Trust Applet\" to load and run this applet now.\n" \
138 "Click \"Trust Applet and Add To Whitelist\" to always load" \
139 " and run this applet from now on, without asking.\n" \
140 "The whitelist is a list of the URLs from which you trust" \
141 " applets.\n" \
142 "Your whitelist file is \" %s \"."
143 #define FAILURE_MESSAGE \
144 "This page wants to load an applet.\n" \
145 "The appletviewer is missing or not installed properly in \"" \
146 APPLETVIEWER_EXECUTABLE "\"."
147
148 // Documentbase retrieval required definition.
149 static NS_DEFINE_IID (kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID);
150
151 // Browser function table.
152 static NPNetscapeFuncs browserFunctions;
153
154 // Data directory for plugin.
155 static gchar* data_directory;
156
157 // Whitelist filename
158 static gchar* whitelist_filename;
159
160 // Keeps track of initialization. NP_Initialize should only be
161 // called once.
162 gboolean initialized = false;
163
164 // GCJPluginData stores all the data associated with a single plugin
165 // instance. A separate plugin instance is created for each <APPLET>
166 // tag. For now, each plugin instance spawns its own applet viewer
167 // process but this may need to change if we find pages containing
168 // multiple applets that expect to be running in the same VM.
169 struct GCJPluginData
170 {
171 // A unique identifier for this plugin window.
172 gchar* instance_string;
173 // Applet viewer input pipe name.
174 gchar* in_pipe_name;
175 // Applet viewer input channel.
176 GIOChannel* in_from_appletviewer;
177 // Applet viewer input watch source.
178 gint in_watch_source;
179 // Applet viewer output pipe name.
180 gchar* out_pipe_name;
181 // Applet viewer output channel.
182 GIOChannel* out_to_appletviewer;
183 // Applet viewer output watch source.
184 gint out_watch_source;
185 // Mutex to protect appletviewer_alive.
186 GMutex* appletviewer_mutex;
187 // Back-pointer to the plugin instance to which this data belongs.
188 // This should not be freed but instead simply set to NULL.
189 NPP owner;
190 // FALSE if the applet viewer process has died. All code
191 // communicating with the applet viewer should check this flag
192 // before attempting to read from/write to the applet viewer pipes.
193 gboolean appletviewer_alive;
194 // The address of the plugin window. This should not be freed but
195 // instead simply set to NULL.
196 gpointer window_handle;
197 // The last plugin window width sent to us by the browser.
198 guint32 window_width;
199 // The last plugin window height sent to us by the browser.
200 guint32 window_height;
201 };
202
203 // Documentbase retrieval type-punning union.
204 typedef union
205 {
206 void** void_field;
207 nsIPluginTagInfo2** info_field;
208 } info_union;
209
210 // Static instance helper functions.
211 // Have the browser allocate a new GCJPluginData structure.
212 static void plugin_data_new (GCJPluginData** data);
213 // Documentbase retrieval.
214 static gchar* plugin_get_documentbase (NPP instance);
215 // plugin failure handling.
216 static bool plugin_failed ();
217 // Whitelist handling.
218 static bool plugin_user_trusts_documentbase (char* documentbase);
219 static bool plugin_ask_user_about_documentbase (char* documentbase);
220 static void plugin_add_documentbase_to_whitelist (char* documentbase);
221 // Callback used to monitor input pipe status.
222 static gboolean plugin_in_pipe_callback (GIOChannel* source,
223 GIOCondition condition,
224 gpointer plugin_data);
225 // Callback used to monitor output pipe status.
226 static gboolean plugin_out_pipe_callback (GIOChannel* source,
227 GIOCondition condition,
228 gpointer plugin_data);
229 static NPError plugin_start_appletviewer (GCJPluginData* data);
230 static gchar* plugin_create_applet_tag (int16 argc, char* argn[],
231 char* argv[]);
232 static void plugin_send_message_to_appletviewer (GCJPluginData* data,
233 gchar const* message);
234 static void plugin_stop_appletviewer (GCJPluginData* data);
235 // Uninitialize GCJPluginData structure and delete pipes.
236 static void plugin_data_destroy (GCJPluginData** data);
237
238 // Global instance counter.
239 // Mutex to protect plugin_instance_counter.
240 static GMutex* plugin_instance_mutex = NULL;
241 // A counter used to create uniquely named pipes.
242 static gulong plugin_instance_counter = 0;
243 // The user's documentbase whitelist.
244 static GIOChannel* whitelist_file = NULL;
245 // A global variable for reporting GLib errors. This must be free'd
246 // and set to NULL after each use.
247 static GError* channel_error = NULL;
248
249 // Functions prefixed by GCJ_ are instance functions. They are called
250 // by the browser and operate on instances of GCJPluginData.
251 // Functions prefixed by plugin_ are static helper functions.
252 // Functions prefixed by NP_ are factory functions. They are called
253 // by the browser and provide functionality needed to create plugin
254 // instances.
255
256 // INSTANCE FUNCTIONS
257
258 // Creates a new gcjwebplugin instance. This function creates a
259 // GCJPluginData* and stores it in instance->pdata. The following
260 // GCJPluginData fiels are initialized: instance_string, in_pipe_name,
261 // in_from_appletviewer, in_watch_source, out_pipe_name,
262 // out_to_appletviewer, out_watch_source, appletviewer_mutex, owner,
263 // appletviewer_alive. In addition two pipe files are created. All
264 // of those fields must be properly destroyed, and the pipes deleted,
265 // by GCJ_Destroy. If an error occurs during initialization then this
266 // function will free anything that's been allocated so far, set
267 // instance->pdata to NULL and return an error code.
268 NPError
GCJ_New(NPMIMEType pluginType,NPP instance,uint16 mode,int16 argc,char * argn[],char * argv[],NPSavedData * saved)269 GCJ_New (NPMIMEType pluginType, NPP instance, uint16 mode,
270 int16 argc, char* argn[], char* argv[],
271 NPSavedData* saved)
272 {
273 PLUGIN_DEBUG ("GCJ_New");
274
275 NPError np_error = NPERR_NO_ERROR;
276 GCJPluginData* data = NULL;
277
278 gchar* documentbase = NULL;
279 gchar* read_message = NULL;
280 gchar* applet_tag = NULL;
281 gchar* tag_message = NULL;
282
283 if (!instance)
284 {
285 PLUGIN_ERROR ("Browser-provided instance pointer is NULL.");
286 np_error = NPERR_INVALID_INSTANCE_ERROR;
287 goto cleanup_done;
288 }
289
290 // data
291 plugin_data_new (&data);
292 if (data == NULL)
293 {
294 PLUGIN_ERROR ("Failed to allocate plugin data.");
295 np_error = NPERR_OUT_OF_MEMORY_ERROR;
296 goto cleanup_done;
297 }
298
299 // Initialize data->instance_string.
300 //
301 // instance_string should be unique for this process so we use a
302 // combination of getpid and plugin_instance_counter.
303 //
304 // Critical region. Reference and increment plugin_instance_counter
305 // global.
306 g_mutex_lock (plugin_instance_mutex);
307
308 // data->instance_string
309 data->instance_string = g_strdup_printf ("instance-%d-%ld",
310 getpid (),
311 plugin_instance_counter++);
312
313 g_mutex_unlock (plugin_instance_mutex);
314
315 // data->appletviewer_mutex
316 data->appletviewer_mutex = g_mutex_new ();
317
318 // Documentbase retrieval.
319 documentbase = plugin_get_documentbase (instance);
320 if (!documentbase)
321 {
322 PLUGIN_ERROR ("Documentbase retrieval failed."
323 " Browser not Mozilla-based?");
324 goto cleanup_appletviewer_mutex;
325 }
326
327 // Open the user's documentbase whitelist.
328 whitelist_file = g_io_channel_new_file (whitelist_filename,
329 "a+", &channel_error);
330 if (!whitelist_file)
331 {
332 if (channel_error)
333 {
334 PLUGIN_ERROR_THREE ("Failed to open whitelist file",
335 whitelist_filename,
336 channel_error->message);
337 g_error_free (channel_error);
338 channel_error = NULL;
339 }
340 else
341 PLUGIN_ERROR_TWO ("Failed to open whitelist file",
342 whitelist_filename);
343
344 return NPERR_GENERIC_ERROR;
345 }
346
347 if (!plugin_user_trusts_documentbase (documentbase))
348 {
349 PLUGIN_ERROR ("User does not trust applet.");
350 np_error = NPERR_GENERIC_ERROR;
351 goto cleanup_appletviewer_mutex;
352 }
353
354 // Create appletviewer-to-plugin pipe which we refer to as the input
355 // pipe.
356
357 // data->in_pipe_name
358 data->in_pipe_name = g_strdup_printf ("%s/gcj-%s-appletviewer-to-plugin",
359 data_directory, data->instance_string);
360 if (!data->in_pipe_name)
361 {
362 PLUGIN_ERROR ("Failed to create input pipe name.");
363 np_error = NPERR_OUT_OF_MEMORY_ERROR;
364 // If data->in_pipe_name is NULL then the g_free at
365 // cleanup_in_pipe_name will simply return.
366 goto cleanup_in_pipe_name;
367 }
368
369 if (mkfifo (data->in_pipe_name, 0700) == -1 && errno != EEXIST)
370 {
371 PLUGIN_ERROR_TWO ("Failed to create input pipe", strerror (errno));
372 np_error = NPERR_GENERIC_ERROR;
373 goto cleanup_in_pipe_name;
374 }
375
376 // Create plugin-to-appletviewer pipe which we refer to as the
377 // output pipe.
378
379 // data->out_pipe_name
380 data->out_pipe_name = g_strdup_printf ("%s/gcj-%s-plugin-to-appletviewer",
381 data_directory, data->instance_string);
382
383 if (!data->out_pipe_name)
384 {
385 PLUGIN_ERROR ("Failed to create output pipe name.");
386 np_error = NPERR_OUT_OF_MEMORY_ERROR;
387 goto cleanup_out_pipe_name;
388 }
389
390 if (mkfifo (data->out_pipe_name, 0700) == -1 && errno != EEXIST)
391 {
392 PLUGIN_ERROR_TWO ("Failed to create output pipe", strerror (errno));
393 np_error = NPERR_GENERIC_ERROR;
394 goto cleanup_out_pipe_name;
395 }
396
397 // Start a separate appletviewer process for each applet, even if
398 // there are multiple applets in the same page. We may need to
399 // change this behaviour if we find pages with multiple applets that
400 // rely on being run in the same VM.
401
402 // Critical region. Hold appletviewer_mutex while we start the
403 // appletviewer, create the IO channels and install the channel
404 // watch callbacks.
405 g_mutex_lock (data->appletviewer_mutex);
406
407 np_error = plugin_start_appletviewer (data);
408
409 // If the appletviewer is not installed, then a dialog box will
410 // show up and the plugin will be killed.
411 if (np_error != NPERR_NO_ERROR)
412 {
413 if (plugin_failed ())
414 goto cleanup_applet_failure;
415 }
416
417 // Create plugin-to-appletviewer channel. The default encoding for
418 // the file is UTF-8.
419 // data->out_to_appletviewer
420 data->out_to_appletviewer = g_io_channel_new_file (data->out_pipe_name,
421 "w", &channel_error);
422 if (!data->out_to_appletviewer)
423 {
424 if (channel_error)
425 {
426 PLUGIN_ERROR_TWO ("Failed to create output channel",
427 channel_error->message);
428 g_error_free (channel_error);
429 channel_error = NULL;
430 }
431 else
432 PLUGIN_ERROR ("Failed to create output channel");
433
434 np_error = NPERR_GENERIC_ERROR;
435 goto cleanup_out_to_appletviewer;
436 }
437
438 // Watch for hangup and error signals on the output pipe.
439 data->out_watch_source =
440 g_io_add_watch (data->out_to_appletviewer,
441 (GIOCondition) (G_IO_ERR | G_IO_HUP),
442 plugin_out_pipe_callback, (gpointer) data);
443
444 // Create appletviewer-to-plugin channel. The default encoding for
445 // the file is UTF-8.
446 // data->in_from_appletviewer
447 data->in_from_appletviewer = g_io_channel_new_file (data->in_pipe_name,
448 "r", &channel_error);
449 if (!data->in_from_appletviewer)
450 {
451 if (channel_error)
452 {
453 PLUGIN_ERROR_TWO ("Failed to create input channel",
454 channel_error->message);
455 g_error_free (channel_error);
456 channel_error = NULL;
457 }
458 else
459 PLUGIN_ERROR ("Failed to create input channel");
460
461 np_error = NPERR_GENERIC_ERROR;
462 goto cleanup_in_from_appletviewer;
463 }
464
465 // Watch for hangup and error signals on the input pipe.
466 data->in_watch_source =
467 g_io_add_watch (data->in_from_appletviewer,
468 (GIOCondition) (G_IO_IN | G_IO_ERR | G_IO_HUP),
469 plugin_in_pipe_callback, (gpointer) data);
470
471 // Wait until we receive confirmation that the appletviewer has
472 // started.
473 if (g_io_channel_read_line (data->in_from_appletviewer,
474 &read_message, NULL, NULL,
475 &channel_error)
476 != G_IO_STATUS_NORMAL)
477 {
478 if (channel_error)
479 {
480 PLUGIN_ERROR_TWO ("Receiving confirmation from appletviewer failed",
481 channel_error->message);
482 g_error_free (channel_error);
483 channel_error = NULL;
484 }
485 else
486 PLUGIN_ERROR ("Receiving confirmation from appletviewer failed");
487
488 np_error = NPERR_GENERIC_ERROR;
489 goto cleanup_in_watch_source;
490 }
491
492 PLUGIN_DEBUG ("GCJ_New: got confirmation that appletviewer is running.");
493 data->appletviewer_alive = TRUE;
494
495 // Send applet tag message to appletviewer.
496 applet_tag = plugin_create_applet_tag (argc, argn, argv);
497 tag_message = g_strconcat ("tag ", documentbase, " ", applet_tag, NULL);
498
499 plugin_send_message_to_appletviewer (data, data->instance_string);
500 plugin_send_message_to_appletviewer (data, tag_message);
501
502 g_mutex_unlock (data->appletviewer_mutex);
503
504 // If initialization succeeded entirely then we store the plugin
505 // data in the instance structure and return. Otherwise we free the
506 // data we've allocated so far and set instance->pdata to NULL.
507
508 // Set back-pointer to owner instance.
509 data->owner = instance;
510 instance->pdata = data;
511 goto cleanup_done;
512
513 // An error occurred while initializing the plugin data or spawning
514 // the appletviewer so we free the data we've already allocated.
515
516 cleanup_in_watch_source:
517 // Removing a source is harmless if it fails since it just means the
518 // source has already been removed.
519 g_source_remove (data->in_watch_source);
520 data->in_watch_source = 0;
521
522 cleanup_in_from_appletviewer:
523 if (data->in_from_appletviewer)
524 g_io_channel_unref (data->in_from_appletviewer);
525 data->in_from_appletviewer = NULL;
526
527 // cleanup_out_watch_source:
528 g_source_remove (data->out_watch_source);
529 data->out_watch_source = 0;
530
531 cleanup_out_to_appletviewer:
532 if (data->out_to_appletviewer)
533 g_io_channel_unref (data->out_to_appletviewer);
534 data->out_to_appletviewer = NULL;
535
536 // cleanup_out_pipe:
537 // Delete output pipe.
538 unlink (data->out_pipe_name);
539
540 cleanup_applet_failure:
541 cleanup_out_pipe_name:
542 g_free (data->out_pipe_name);
543 data->out_pipe_name = NULL;
544
545 // cleanup_in_pipe:
546 // Delete input pipe.
547 unlink (data->in_pipe_name);
548
549 cleanup_in_pipe_name:
550 g_free (data->in_pipe_name);
551 data->in_pipe_name = NULL;
552
553 cleanup_appletviewer_mutex:
554 g_free (data->appletviewer_mutex);
555 data->appletviewer_mutex = NULL;
556
557 // cleanup_instance_string:
558 g_free (data->instance_string);
559 data->instance_string = NULL;
560
561 // cleanup_data:
562 // Eliminate back-pointer to plugin instance.
563 data->owner = NULL;
564 (*browserFunctions.memfree) (data);
565 data = NULL;
566
567 // Initialization failed so return a NULL pointer for the browser
568 // data.
569 instance->pdata = NULL;
570
571 cleanup_done:
572 g_free (tag_message);
573 tag_message = NULL;
574 g_free (applet_tag);
575 applet_tag = NULL;
576 g_free (read_message);
577 read_message = NULL;
578 g_free (documentbase);
579 documentbase = NULL;
580
581 PLUGIN_DEBUG ("GCJ_New return");
582
583 return np_error;
584 }
585
586 NPError
GCJ_GetValue(NPP instance,NPPVariable variable,void * value)587 GCJ_GetValue (NPP instance, NPPVariable variable, void* value)
588 {
589 PLUGIN_DEBUG ("GCJ_GetValue");
590
591 NPError np_error = NPERR_NO_ERROR;
592
593 switch (variable)
594 {
595 // This plugin needs XEmbed support.
596 case NPPVpluginNeedsXEmbed:
597 {
598 PLUGIN_DEBUG ("GCJ_GetValue: returning TRUE for NeedsXEmbed.");
599 PRBool* bool_value = (PRBool*) value;
600 *bool_value = PR_TRUE;
601 }
602 break;
603
604 default:
605 PLUGIN_ERROR ("Unknown plugin value requested.");
606 np_error = NPERR_GENERIC_ERROR;
607 break;
608 }
609
610 PLUGIN_DEBUG ("GCJ_GetValue return");
611
612 return np_error;
613 }
614
615 NPError
GCJ_Destroy(NPP instance,NPSavedData ** save)616 GCJ_Destroy (NPP instance, NPSavedData** save)
617 {
618 PLUGIN_DEBUG ("GCJ_Destroy");
619
620 GCJPluginData* data = (GCJPluginData*) instance->pdata;
621
622 if (data)
623 {
624 // Critical region. Stop the appletviewer.
625 g_mutex_lock (data->appletviewer_mutex);
626
627 // Tell the appletviewer to destroy its embedded plugin window.
628 plugin_send_message_to_appletviewer (data, "destroy");
629 // Shut down the appletviewer.
630 plugin_stop_appletviewer (data);
631
632 g_mutex_unlock (data->appletviewer_mutex);
633
634 // Free plugin data.
635 plugin_data_destroy (&data);
636 }
637
638 PLUGIN_DEBUG ("GCJ_Destroy return");
639
640 return NPERR_NO_ERROR;
641 }
642
643 NPError
GCJ_SetWindow(NPP instance,NPWindow * window)644 GCJ_SetWindow (NPP instance, NPWindow* window)
645 {
646 PLUGIN_DEBUG ("GCJ_SetWindow");
647
648 if (instance == NULL)
649 {
650 PLUGIN_ERROR ("Invalid instance.");
651
652 return NPERR_INVALID_INSTANCE_ERROR;
653 }
654
655 GCJPluginData* data = (GCJPluginData*) instance->pdata;
656
657 // Simply return if we receive a NULL window.
658 if ((window == NULL) || (window->window == NULL))
659 {
660 PLUGIN_DEBUG ("GCJ_SetWindow: got NULL window.");
661
662 return NPERR_NO_ERROR;
663 }
664
665 if (data->window_handle)
666 {
667 // The window already exists.
668 if (data->window_handle == window->window)
669 {
670 // The parent window is the same as in previous calls.
671 PLUGIN_DEBUG ("GCJ_SetWindow: window already exists.");
672
673 // Critical region. Read data->appletviewer_mutex and send
674 // a message to the appletviewer.
675 g_mutex_lock (data->appletviewer_mutex);
676
677 if (data->appletviewer_alive)
678 {
679 // The window is the same as it was for the last
680 // SetWindow call.
681 if (window->width != data->window_width)
682 {
683 PLUGIN_DEBUG ("GCJ_SetWindow: window width changed.");
684 // The width of the plugin window has changed.
685
686 // Send the new width to the appletviewer.
687 plugin_send_message_to_appletviewer (data,
688 data->instance_string);
689 gchar* width_message = g_strdup_printf ("width %d",
690 window->width);
691 plugin_send_message_to_appletviewer (data, width_message);
692 g_free (width_message);
693 width_message = NULL;
694
695 // Store the new width.
696 data->window_width = window->width;
697 }
698
699 if (window->height != data->window_height)
700 {
701 PLUGIN_DEBUG ("GCJ_SetWindow: window height changed.");
702 // The height of the plugin window has changed.
703
704 // Send the new height to the appletviewer.
705 plugin_send_message_to_appletviewer (data,
706 data->instance_string);
707 gchar* height_message = g_strdup_printf ("height %d",
708 window->height);
709 plugin_send_message_to_appletviewer (data, height_message);
710 g_free (height_message);
711 height_message = NULL;
712
713 // Store the new height.
714 data->window_height = window->height;
715 }
716 }
717 else
718 {
719 // The appletviewer is not running.
720 PLUGIN_DEBUG ("GCJ_SetWindow: appletviewer is not running.");
721 }
722
723 g_mutex_unlock (data->appletviewer_mutex);
724 }
725 else
726 {
727 // The parent window has changed. This branch does run but
728 // doing nothing in response seems to be sufficient.
729 PLUGIN_DEBUG ("GCJ_SetWindow: parent window changed.");
730 }
731 }
732 else
733 {
734 PLUGIN_DEBUG ("GCJ_SetWindow: setting window.");
735
736 // Critical region. Send messages to appletviewer.
737 g_mutex_lock (data->appletviewer_mutex);
738
739 plugin_send_message_to_appletviewer (data, data->instance_string);
740 gchar *window_message = g_strdup_printf ("handle %ld",
741 (gulong) window->window);
742 plugin_send_message_to_appletviewer (data, window_message);
743 g_free (window_message);
744 window_message = NULL;
745
746 g_mutex_unlock (data->appletviewer_mutex);
747
748 // Store the window handle.
749 data->window_handle = window->window;
750 }
751
752 PLUGIN_DEBUG ("GCJ_SetWindow return");
753
754 return NPERR_NO_ERROR;
755 }
756
757 NPError
GCJ_NewStream(NPP instance,NPMIMEType type,NPStream * stream,NPBool seekable,uint16 * stype)758 GCJ_NewStream (NPP instance, NPMIMEType type, NPStream* stream,
759 NPBool seekable, uint16* stype)
760 {
761 PLUGIN_DEBUG ("GCJ_NewStream");
762
763 PLUGIN_DEBUG ("GCJ_NewStream return");
764
765 return NPERR_NO_ERROR;
766 }
767
768 void
GCJ_StreamAsFile(NPP instance,NPStream * stream,const char * filename)769 GCJ_StreamAsFile (NPP instance, NPStream* stream, const char* filename)
770 {
771 PLUGIN_DEBUG ("GCJ_StreamAsFile");
772
773 PLUGIN_DEBUG ("GCJ_StreamAsFile return");
774 }
775
776 NPError
GCJ_DestroyStream(NPP instance,NPStream * stream,NPReason reason)777 GCJ_DestroyStream (NPP instance, NPStream* stream, NPReason reason)
778 {
779 PLUGIN_DEBUG ("GCJ_DestroyStream");
780
781 PLUGIN_DEBUG ("GCJ_DestroyStream return");
782
783 return NPERR_NO_ERROR;
784 }
785
786 int32
GCJ_WriteReady(NPP instance,NPStream * stream)787 GCJ_WriteReady (NPP instance, NPStream* stream)
788 {
789 PLUGIN_DEBUG ("GCJ_WriteReady");
790
791 PLUGIN_DEBUG ("GCJ_WriteReady return");
792
793 return 0;
794 }
795
796 int32
GCJ_Write(NPP instance,NPStream * stream,int32 offset,int32 len,void * buffer)797 GCJ_Write (NPP instance, NPStream* stream, int32 offset, int32 len,
798 void* buffer)
799 {
800 PLUGIN_DEBUG ("GCJ_Write");
801
802 PLUGIN_DEBUG ("GCJ_Write return");
803
804 return 0;
805 }
806
807 void
GCJ_Print(NPP instance,NPPrint * platformPrint)808 GCJ_Print (NPP instance, NPPrint* platformPrint)
809 {
810 PLUGIN_DEBUG ("GCJ_Print");
811
812 PLUGIN_DEBUG ("GCJ_Print return");
813 }
814
815 int16
GCJ_HandleEvent(NPP instance,void * event)816 GCJ_HandleEvent (NPP instance, void* event)
817 {
818 PLUGIN_DEBUG ("GCJ_HandleEvent");
819
820 PLUGIN_DEBUG ("GCJ_HandleEvent return");
821
822 return 0;
823 }
824
825 void
GCJ_URLNotify(NPP instance,const char * url,NPReason reason,void * notifyData)826 GCJ_URLNotify (NPP instance, const char* url, NPReason reason,
827 void* notifyData)
828 {
829 PLUGIN_DEBUG ("GCJ_URLNotify");
830
831 PLUGIN_DEBUG ("GCJ_URLNotify return");
832 }
833
834 #if (((NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR) < 20)
835 jref
836 #else
837 void*
838 #endif
GCJ_GetJavaClass(void)839 GCJ_GetJavaClass (void)
840 {
841 PLUGIN_DEBUG ("GCJ_GetJavaClass");
842
843 PLUGIN_DEBUG ("GCJ_GetJavaClass return");
844
845 return 0;
846 }
847
848 // HELPER FUNCTIONS
849
850 static void
plugin_data_new(GCJPluginData ** data)851 plugin_data_new (GCJPluginData** data)
852 {
853 PLUGIN_DEBUG ("plugin_data_new");
854
855 *data = (GCJPluginData*)
856 (*browserFunctions.memalloc) (sizeof (struct GCJPluginData));
857
858 // appletviewer_alive is false until the applet viewer is spawned.
859 if (*data)
860 memset (*data, 0, sizeof (struct GCJPluginData));
861
862 PLUGIN_DEBUG ("plugin_data_new return");
863 }
864
865 // Documentbase retrieval. This function gets the current document's
866 // documentbase. This function relies on browser-private data so it
867 // will only work when the plugin is loaded in a Mozilla-based
868 // browser. We could not find a way to retrieve the documentbase
869 // using the original Netscape plugin API so we use the XPCOM API
870 // instead.
871 static gchar*
plugin_get_documentbase(NPP instance)872 plugin_get_documentbase (NPP instance)
873 {
874 PLUGIN_DEBUG ("plugin_get_documentbase");
875
876 nsIPluginInstance* xpcom_instance = NULL;
877 nsIPluginInstancePeer* peer = NULL;
878 nsresult result = 0;
879 nsIPluginTagInfo2* pluginTagInfo2 = NULL;
880 info_union u = { NULL };
881 char const* documentbase = NULL;
882 gchar* documentbase_copy = NULL;
883
884 xpcom_instance = (nsIPluginInstance*) (instance->ndata);
885 if (!xpcom_instance)
886 {
887 PLUGIN_ERROR ("xpcom_instance is NULL.");
888 goto cleanup_done;
889 }
890
891 xpcom_instance->GetPeer (&peer);
892 if (!peer)
893 {
894 PLUGIN_ERROR ("peer is NULL.");
895 goto cleanup_done;
896 }
897
898 u.info_field = &pluginTagInfo2;
899
900 result = peer->QueryInterface (kIPluginTagInfo2IID,
901 u.void_field);
902 if (result || !pluginTagInfo2)
903 {
904 PLUGIN_ERROR ("pluginTagInfo2 retrieval failed.");
905 goto cleanup_peer;
906 }
907
908 pluginTagInfo2->GetDocumentBase (&documentbase);
909
910 if (!documentbase)
911 {
912 PLUGIN_ERROR ("documentbase is NULL.");
913 goto cleanup_plugintaginfo2;
914 }
915
916 documentbase_copy = g_strdup (documentbase);
917
918 // Release references.
919 cleanup_plugintaginfo2:
920 NS_RELEASE (pluginTagInfo2);
921
922 cleanup_peer:
923 NS_RELEASE (peer);
924
925 cleanup_done:
926 PLUGIN_DEBUG ("plugin_get_documentbase return");
927
928 return documentbase_copy;
929 }
930
931 // This function shows a error message if the appletviewer has
932 // not been installed. It returns true, if the user presses the
933 // ok button.
934 static bool
plugin_failed()935 plugin_failed ()
936 {
937 GtkWidget* dialog = NULL;
938 GtkWidget* ok_button = NULL;
939
940 dialog = gtk_message_dialog_new (NULL,
941 GTK_DIALOG_MODAL,
942 GTK_MESSAGE_WARNING,
943 GTK_BUTTONS_NONE,
944 FAILURE_MESSAGE);
945 ok_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
946 GTK_STOCK_OK,
947 GTK_RESPONSE_OK);
948 gtk_widget_show_all (dialog);
949 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
950 {
951 gtk_widget_destroy (dialog);
952 return true;
953 }
954 return false;
955 }
956
957 // plugin_user_trusts_documentbase returns true if the given
958 // documentbase is in the documentbase whitelist. Otherwise it asks
959 // the user if he trusts the given documentbase by calling
960 // plugin_ask_user_about_documentbase.
961 static bool
plugin_user_trusts_documentbase(char * documentbase)962 plugin_user_trusts_documentbase (char* documentbase)
963 {
964 bool applet_in_whitelist = false;
965
966 // Check if documentbase is in whitelist.
967 while (true)
968 {
969 gchar* whitelist_entry = NULL;
970 gchar* newline_documentbase = NULL;
971
972 // If reading fails, break out of this loop with
973 // applet_in_whitelist still set to false.
974 if (g_io_channel_read_line (whitelist_file, &whitelist_entry,
975 NULL, NULL, &channel_error)
976 != G_IO_STATUS_NORMAL)
977 {
978 if (channel_error)
979 {
980 PLUGIN_ERROR_TWO ("Failed to read line from whitelist file",
981 channel_error->message);
982 g_error_free (channel_error);
983 channel_error = NULL;
984 }
985 else
986 PLUGIN_ERROR ("Failed to read line from whitelist file.");
987 g_free (whitelist_entry);
988 whitelist_entry = NULL;
989 break;
990 }
991
992 newline_documentbase = g_strdup_printf ("%s\n", documentbase);
993 if (!strcmp (newline_documentbase, whitelist_entry))
994 {
995 applet_in_whitelist = true;
996 g_free (newline_documentbase);
997 newline_documentbase = NULL;
998 g_free (whitelist_entry);
999 whitelist_entry = NULL;
1000 break;
1001 }
1002 g_free (whitelist_entry);
1003 whitelist_entry = NULL;
1004 g_free (newline_documentbase);
1005 newline_documentbase = NULL;
1006 }
1007
1008 return applet_in_whitelist ? true
1009 : plugin_ask_user_about_documentbase (documentbase);
1010 }
1011
1012 // plugin_add_documentbase_to_whitelist adds the given documentbase to
1013 // the user's documentbase whitelist.
1014 static void
plugin_add_documentbase_to_whitelist(char * documentbase)1015 plugin_add_documentbase_to_whitelist (char* documentbase)
1016 {
1017 gsize bytes_written = 0;
1018 char* newline_documentbase = NULL;
1019 GIOStatus status = G_IO_STATUS_NORMAL;
1020
1021 newline_documentbase = g_strdup_printf ("%s\n", documentbase);
1022 status = g_io_channel_write_chars (whitelist_file,
1023 newline_documentbase, -1, &bytes_written,
1024 &channel_error);
1025 g_free (newline_documentbase);
1026 newline_documentbase = NULL;
1027
1028 if (status != G_IO_STATUS_NORMAL)
1029 {
1030 if (channel_error)
1031 {
1032 PLUGIN_ERROR_TWO ("Error writing to whitelist file",
1033 channel_error->message);
1034 g_error_free (channel_error);
1035 channel_error = NULL;
1036 }
1037 else
1038 PLUGIN_ERROR ("Error writing to whitelist file.");
1039 }
1040
1041 if (g_io_channel_flush (whitelist_file, &channel_error)
1042 != G_IO_STATUS_NORMAL)
1043 {
1044 if (channel_error)
1045 {
1046 PLUGIN_ERROR_TWO ("Failed to write whitelist file",
1047 channel_error->message);
1048 g_error_free (channel_error);
1049 channel_error = NULL;
1050 }
1051 else
1052 PLUGIN_ERROR ("Failed to write whitelist file.");
1053 }
1054
1055 if (g_io_channel_shutdown (whitelist_file, TRUE, &channel_error)
1056 != G_IO_STATUS_NORMAL)
1057 {
1058 if (channel_error)
1059 {
1060 PLUGIN_ERROR_TWO ("Failed to close whitelist file",
1061 channel_error->message);
1062 g_error_free (channel_error);
1063 channel_error = NULL;
1064 }
1065 else
1066 PLUGIN_ERROR ("Failed to close whitelist file.");
1067 }
1068 }
1069
1070 // plugin_ask_user_about_documentbase puts up a dialog box that asks if the
1071 // user trusts applets from this documentbase. The user has three
1072 // options: "Cancel", "Trust Applet" and "Trust Applet and Add to
1073 // Whitelist". If the user selects Cancel (the default) then a
1074 // generic error code is returned from GCJ_New, telling the browser
1075 // that the applet failed to load. If the user selects "Trust Applet"
1076 // then plugin loading proceeds. If the user selects "Trust Applet
1077 // and Add to Whitelist" then this documentbase is added to the user's
1078 // applet whitelist and plugin loading proceeds.
1079 static bool
plugin_ask_user_about_documentbase(char * documentbase)1080 plugin_ask_user_about_documentbase (char* documentbase)
1081 {
1082 GtkWidget* dialog = NULL;
1083 GtkWidget* ok_button = NULL;
1084 GtkWidget* cancel_button = NULL;
1085 GtkWidget* whitelist_button = NULL;
1086 gint dialog_response = GTK_RESPONSE_NONE;
1087
1088 dialog = gtk_message_dialog_new (NULL,
1089 GTK_DIALOG_MODAL,
1090 GTK_MESSAGE_WARNING,
1091 GTK_BUTTONS_NONE,
1092 SECURITY_WARNING,
1093 documentbase);
1094 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
1095 SECURITY_DESCRIPTION, whitelist_filename);
1096
1097 cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1098 GTK_STOCK_CANCEL,
1099 GTK_RESPONSE_CANCEL);
1100 ok_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1101 RESPONSE_TRUST_APPLET,
1102 GTK_RESPONSE_OK);
1103 whitelist_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1104 RESPONSE_TRUST_APPLET_ADD_TO_LIST,
1105 GTK_RESPONSE_APPLY);
1106 gtk_widget_grab_focus (cancel_button);
1107
1108 gtk_widget_show_all (dialog);
1109 dialog_response = gtk_dialog_run (GTK_DIALOG (dialog));
1110 gtk_widget_destroy (dialog);
1111 if (dialog_response == GTK_RESPONSE_CANCEL)
1112 {
1113 // The user does not trust this documentbase.
1114 return false;
1115 }
1116 else if (dialog_response == GTK_RESPONSE_APPLY)
1117 {
1118 // The user wants this documentbase added to his documentbase
1119 // whitelist.
1120 plugin_add_documentbase_to_whitelist (documentbase);
1121 }
1122 // The user trusts this documentbase.
1123 return true;
1124 }
1125
1126 // plugin_in_pipe_callback is called when data is available on the
1127 // input pipe, or when the appletviewer crashes or is killed. It may
1128 // be called after data has been destroyed in which case it simply
1129 // returns FALSE to remove itself from the glib main loop.
1130 static gboolean
plugin_in_pipe_callback(GIOChannel * source,GIOCondition condition,gpointer plugin_data)1131 plugin_in_pipe_callback (GIOChannel* source,
1132 GIOCondition condition,
1133 gpointer plugin_data)
1134 {
1135 PLUGIN_DEBUG ("plugin_in_pipe_callback");
1136
1137 GCJPluginData* data = (GCJPluginData*) plugin_data;
1138 gboolean keep_installed = TRUE;
1139
1140 // If data is NULL then GCJ_Destroy has already been called and
1141 // plugin_in_pipe_callback is being called after plugin
1142 // destruction. In that case all we need to do is return FALSE so
1143 // that the plugin_in_pipe_callback watch is removed.
1144 if (data)
1145 {
1146 // Critical region. Set or clear data->appletviewer_alive.
1147 g_mutex_lock (data->appletviewer_mutex);
1148
1149 if (condition & G_IO_IN)
1150 {
1151 gchar* message = NULL;
1152
1153 if (g_io_channel_read_line (data->in_from_appletviewer,
1154 &message, NULL, NULL,
1155 &channel_error)
1156 != G_IO_STATUS_NORMAL)
1157 {
1158 if (channel_error)
1159 {
1160 PLUGIN_ERROR_TWO ("Failed to read line from input channel",
1161 channel_error->message);
1162 g_error_free (channel_error);
1163 channel_error = NULL;
1164 }
1165 else
1166 PLUGIN_ERROR ("Failed to read line from input channel");
1167 }
1168 else
1169 {
1170 if (g_str_has_prefix (message, "url "))
1171 {
1172 gchar** parts = g_strsplit (message, " ", 3);
1173 PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback:"
1174 " opening URL", parts[1]);
1175 PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback:"
1176 " URL target", parts[2]);
1177 // Open the URL in a new browser window.
1178 NPError np_error =
1179 (*browserFunctions.geturl) (data->owner, parts[1], parts[2]);
1180 if (np_error != NPERR_NO_ERROR)
1181 PLUGIN_ERROR ("Failed to load URL.");
1182 g_strfreev (parts);
1183 parts = NULL;
1184 }
1185 else if (g_str_has_prefix (message, "status "))
1186 {
1187 gchar** parts = g_strsplit (message, " ", 2);
1188
1189 PLUGIN_DEBUG_TWO ("plugin_in_pipe_callback:"
1190 " setting status", parts[1]);
1191 (*browserFunctions.status) (data->owner, parts[1]);
1192 g_strfreev (parts);
1193 parts = NULL;
1194 }
1195 g_print (" PIPE: plugin read %s\n", message);
1196 }
1197
1198 g_free (message);
1199 message = NULL;
1200
1201 keep_installed = TRUE;
1202 }
1203
1204 if (condition & (G_IO_ERR | G_IO_HUP))
1205 {
1206 PLUGIN_DEBUG ("appletviewer has stopped.");
1207 data->appletviewer_alive = FALSE;
1208 keep_installed = FALSE;
1209 }
1210 g_mutex_unlock (data->appletviewer_mutex);
1211 }
1212
1213 PLUGIN_DEBUG ("plugin_in_pipe_callback return");
1214
1215 return keep_installed;
1216 }
1217
1218 // plugin_out_pipe_callback is called when the appletviewer crashes or
1219 // is killed. It may be called after data has been destroyed in which
1220 // case it simply returns FALSE to remove itself from the glib main
1221 // loop.
1222 static gboolean
plugin_out_pipe_callback(GIOChannel * source,GIOCondition condition,gpointer plugin_data)1223 plugin_out_pipe_callback (GIOChannel* source,
1224 GIOCondition condition,
1225 gpointer plugin_data)
1226 {
1227 PLUGIN_DEBUG ("plugin_out_pipe_callback");
1228
1229 GCJPluginData* data = (GCJPluginData*) plugin_data;
1230
1231 // If data is NULL then GCJ_Destroy has already been called and
1232 // plugin_out_pipe_callback is being called after plugin
1233 // destruction. In that case all we need to do is return FALSE so
1234 // that the plugin_out_pipe_callback watch is removed.
1235 if (data)
1236 {
1237 // Critical region. Clear data->appletviewer_alive.
1238 g_mutex_lock (data->appletviewer_mutex);
1239
1240 PLUGIN_DEBUG ("plugin_out_pipe_callback: appletviewer has stopped.");
1241 data->appletviewer_alive = FALSE;
1242
1243 g_mutex_unlock (data->appletviewer_mutex);
1244 }
1245
1246 PLUGIN_DEBUG ("plugin_out_pipe_callback return");
1247
1248 return FALSE;
1249 }
1250
1251 static NPError
plugin_start_appletviewer(GCJPluginData * data)1252 plugin_start_appletviewer (GCJPluginData* data)
1253 {
1254 PLUGIN_DEBUG ("plugin_start_appletviewer");
1255 NPError error = NPERR_NO_ERROR;
1256
1257 if (!data->appletviewer_alive)
1258 {
1259 gchar* command_line[3] = { NULL, NULL, NULL };
1260
1261 command_line[0] = g_strdup (APPLETVIEWER_EXECUTABLE);
1262 // Output from plugin's perspective is appletviewer's input.
1263 // Input from plugin's perspective is appletviewer's output.
1264 command_line[1] = g_strdup_printf ("--plugin=%s,%s",
1265 data->out_pipe_name,
1266 data->in_pipe_name);
1267 command_line[2] = NULL;
1268
1269 if (!g_spawn_async (NULL, command_line, NULL, (GSpawnFlags) 0,
1270 NULL, NULL, NULL, &channel_error))
1271 {
1272 if (channel_error)
1273 {
1274 PLUGIN_ERROR_TWO ("Failed to spawn applet viewer",
1275 channel_error->message);
1276 g_error_free (channel_error);
1277 channel_error = NULL;
1278 }
1279 else
1280 PLUGIN_ERROR ("Failed to spawn applet viewer");
1281 error = NPERR_GENERIC_ERROR;
1282 goto cleanup;
1283 }
1284
1285 cleanup:
1286 g_free (command_line[0]);
1287 command_line[0] = NULL;
1288 g_free (command_line[1]);
1289 command_line[1] = NULL;
1290 g_free (command_line[2]);
1291 command_line[2] = NULL;
1292 }
1293
1294 PLUGIN_DEBUG ("plugin_start_appletviewer return");
1295 return error;
1296 }
1297
1298 // Build up the applet tag string that we'll send to the applet
1299 // viewer.
1300 static gchar*
plugin_create_applet_tag(int16 argc,char * argn[],char * argv[])1301 plugin_create_applet_tag (int16 argc, char* argn[], char* argv[])
1302 {
1303 PLUGIN_DEBUG ("plugin_create_applet_tag");
1304
1305 gchar* applet_tag = g_strdup ("<EMBED ");
1306 gchar* parameters = g_strdup ("");
1307
1308 for (int16 i = 0; i < argc; i++)
1309 {
1310 if (!g_ascii_strcasecmp (argn[i], "code"))
1311 {
1312 gchar* code = g_strdup_printf ("CODE=\"%s\" ", argv[i]);
1313 applet_tag = g_strconcat (applet_tag, code, NULL);
1314 g_free (code);
1315 code = NULL;
1316 }
1317 else if (!g_ascii_strcasecmp (argn[i], "codebase"))
1318 {
1319 gchar* codebase = g_strdup_printf ("CODEBASE=\"%s\" ", argv[i]);
1320 applet_tag = g_strconcat (applet_tag, codebase, NULL);
1321 g_free (codebase);
1322 codebase = NULL;
1323 }
1324 else if (!g_ascii_strcasecmp (argn[i], "archive"))
1325 {
1326 gchar* archive = g_strdup_printf ("ARCHIVE=\"%s\" ", argv[i]);
1327 applet_tag = g_strconcat (applet_tag, archive, NULL);
1328 g_free (archive);
1329 archive = NULL;
1330 }
1331 else if (!g_ascii_strcasecmp (argn[i], "width"))
1332 {
1333 gchar* width = g_strdup_printf ("WIDTH=\"%s\" ", argv[i]);
1334 applet_tag = g_strconcat (applet_tag, width, NULL);
1335 g_free (width);
1336 width = NULL;
1337 }
1338 else if (!g_ascii_strcasecmp (argn[i], "height"))
1339 {
1340 gchar* height = g_strdup_printf ("HEIGHT=\"%s\" ", argv[i]);
1341 applet_tag = g_strconcat (applet_tag, height, NULL);
1342 g_free (height);
1343 height = NULL;
1344 }
1345 else
1346 {
1347 // Escape the parameter value so that line termination
1348 // characters will pass through the pipe.
1349 if (argv[i] != '\0')
1350 {
1351 gchar* escaped = NULL;
1352
1353 escaped = g_strescape (argv[i], NULL);
1354 parameters = g_strconcat (parameters, "<PARAM NAME=\"", argn[i],
1355 "\" VALUE=\"", escaped, "\">", NULL);
1356
1357 g_free (escaped);
1358 escaped = NULL;
1359 }
1360 }
1361 }
1362
1363 applet_tag = g_strconcat (applet_tag, ">", parameters, "</EMBED>", NULL);
1364
1365 g_free (parameters);
1366 parameters = NULL;
1367
1368 PLUGIN_DEBUG ("plugin_create_applet_tag return");
1369
1370 return applet_tag;
1371 }
1372
1373 // plugin_send_message_to_appletviewer must be called while holding
1374 // data->appletviewer_mutex.
1375 static void
plugin_send_message_to_appletviewer(GCJPluginData * data,gchar const * message)1376 plugin_send_message_to_appletviewer (GCJPluginData* data, gchar const* message)
1377 {
1378 PLUGIN_DEBUG ("plugin_send_message_to_appletviewer");
1379
1380 if (data->appletviewer_alive)
1381 {
1382 gchar* newline_message = NULL;
1383 gsize bytes_written = 0;
1384
1385 // Send message to appletviewer.
1386 newline_message = g_strdup_printf ("%s\n", message);
1387
1388 // g_io_channel_write_chars will return something other than
1389 // G_IO_STATUS_NORMAL if not all the data is written. In that
1390 // case we fail rather than retrying.
1391 if (g_io_channel_write_chars (data->out_to_appletviewer,
1392 newline_message, -1, &bytes_written,
1393 &channel_error)
1394 != G_IO_STATUS_NORMAL)
1395 {
1396 if (channel_error)
1397 {
1398 PLUGIN_ERROR_TWO ("Failed to write bytes to output channel",
1399 channel_error->message);
1400 g_error_free (channel_error);
1401 channel_error = NULL;
1402 }
1403 else
1404 PLUGIN_ERROR ("Failed to write bytes to output channel");
1405 }
1406
1407 if (g_io_channel_flush (data->out_to_appletviewer, &channel_error)
1408 != G_IO_STATUS_NORMAL)
1409 {
1410 if (channel_error)
1411 {
1412 PLUGIN_ERROR_TWO ("Failed to flush bytes to output channel",
1413 channel_error->message);
1414 g_error_free (channel_error);
1415 channel_error = NULL;
1416 }
1417 else
1418 PLUGIN_ERROR ("Failed to flush bytes to output channel");
1419 }
1420 g_free (newline_message);
1421 newline_message = NULL;
1422
1423 g_print (" PIPE: plugin wrote %s\n", message);
1424 }
1425
1426 PLUGIN_DEBUG ("plugin_send_message_to_appletviewer return");
1427 }
1428
1429 // Stop the appletviewer process. When this is called the
1430 // appletviewer can be in any of three states: running, crashed or
1431 // hung. If the appletviewer is running then sending it "shutdown"
1432 // will cause it to exit. This will cause
1433 // plugin_out_pipe_callback/plugin_in_pipe_callback to be called and
1434 // the input and output channels to be shut down. If the appletviewer
1435 // has crashed then plugin_out_pipe_callback/plugin_in_pipe_callback
1436 // would already have been called and data->appletviewer_alive cleared
1437 // in which case this function simply returns. If the appletviewer is
1438 // hung then this function will be successful and the input and output
1439 // watches will be removed by plugin_data_destroy.
1440 // plugin_stop_appletviewer must be called with
1441 // data->appletviewer_mutex held.
1442 static void
plugin_stop_appletviewer(GCJPluginData * data)1443 plugin_stop_appletviewer (GCJPluginData* data)
1444 {
1445 PLUGIN_DEBUG ("plugin_stop_appletviewer");
1446
1447 if (data->appletviewer_alive)
1448 {
1449 // Shut down the appletviewer.
1450 gsize bytes_written = 0;
1451
1452 if (data->out_to_appletviewer)
1453 {
1454 if (g_io_channel_write_chars (data->out_to_appletviewer, "shutdown",
1455 -1, &bytes_written, &channel_error)
1456 != G_IO_STATUS_NORMAL)
1457 {
1458 if (channel_error)
1459 {
1460 PLUGIN_ERROR_TWO ("Failed to write shutdown message to"
1461 " appletviewer", channel_error->message);
1462 g_error_free (channel_error);
1463 channel_error = NULL;
1464 }
1465 else
1466 PLUGIN_ERROR ("Failed to write shutdown message to");
1467 }
1468
1469 if (g_io_channel_flush (data->out_to_appletviewer, &channel_error)
1470 != G_IO_STATUS_NORMAL)
1471 {
1472 if (channel_error)
1473 {
1474 PLUGIN_ERROR_TWO ("Failed to write shutdown message to"
1475 " appletviewer", channel_error->message);
1476 g_error_free (channel_error);
1477 channel_error = NULL;
1478 }
1479 else
1480 PLUGIN_ERROR ("Failed to write shutdown message to");
1481 }
1482
1483 if (g_io_channel_shutdown (data->out_to_appletviewer,
1484 TRUE, &channel_error)
1485 != G_IO_STATUS_NORMAL)
1486 {
1487 if (channel_error)
1488 {
1489 PLUGIN_ERROR_TWO ("Failed to shut down appletviewer"
1490 " output channel", channel_error->message);
1491 g_error_free (channel_error);
1492 channel_error = NULL;
1493 }
1494 else
1495 PLUGIN_ERROR ("Failed to shut down appletviewer");
1496 }
1497 }
1498
1499 if (data->in_from_appletviewer)
1500 {
1501 if (g_io_channel_shutdown (data->in_from_appletviewer,
1502 TRUE, &channel_error)
1503 != G_IO_STATUS_NORMAL)
1504 {
1505 if (channel_error)
1506 {
1507 PLUGIN_ERROR_TWO ("Failed to shut down appletviewer"
1508 " input channel", channel_error->message);
1509 g_error_free (channel_error);
1510 channel_error = NULL;
1511 }
1512 else
1513 PLUGIN_ERROR ("Failed to shut down appletviewer");
1514 }
1515 }
1516 }
1517
1518 PLUGIN_DEBUG ("plugin_stop_appletviewer return");
1519 }
1520
1521 static void
plugin_data_destroy(GCJPluginData ** data)1522 plugin_data_destroy (GCJPluginData** data)
1523 {
1524 PLUGIN_DEBUG ("plugin_data_destroy");
1525
1526 GCJPluginData* tofree = *data;
1527
1528 tofree->window_handle = NULL;
1529 tofree->window_height = 0;
1530 tofree->window_width = 0;
1531
1532 // Copied from GCJ_New.
1533
1534 // cleanup_in_watch_source:
1535 // Removing a source is harmless if it fails since it just means the
1536 // source has already been removed.
1537 g_source_remove (tofree->in_watch_source);
1538 tofree->in_watch_source = 0;
1539
1540 // cleanup_in_from_appletviewer:
1541 if (tofree->in_from_appletviewer)
1542 g_io_channel_unref (tofree->in_from_appletviewer);
1543 tofree->in_from_appletviewer = NULL;
1544
1545 // cleanup_out_watch_source:
1546 g_source_remove (tofree->out_watch_source);
1547 tofree->out_watch_source = 0;
1548
1549 // cleanup_out_to_appletviewer:
1550 if (tofree->out_to_appletviewer)
1551 g_io_channel_unref (tofree->out_to_appletviewer);
1552 tofree->out_to_appletviewer = NULL;
1553
1554 // cleanup_out_pipe:
1555 // Delete output pipe.
1556 unlink (tofree->out_pipe_name);
1557
1558 // cleanup_out_pipe_name:
1559 g_free (tofree->out_pipe_name);
1560 tofree->out_pipe_name = NULL;
1561
1562 // cleanup_in_pipe:
1563 // Delete input pipe.
1564 unlink (tofree->in_pipe_name);
1565
1566 // cleanup_in_pipe_name:
1567 g_free (tofree->in_pipe_name);
1568 tofree->in_pipe_name = NULL;
1569
1570 // cleanup_appletviewer_mutex:
1571 g_free (tofree->appletviewer_mutex);
1572 tofree->appletviewer_mutex = NULL;
1573
1574 // cleanup_instance_string:
1575 g_free (tofree->instance_string);
1576 tofree->instance_string = NULL;
1577
1578 // cleanup_data:
1579 // Eliminate back-pointer to plugin instance.
1580 tofree->owner = NULL;
1581 (*browserFunctions.memfree) (tofree);
1582 tofree = NULL;
1583
1584 PLUGIN_DEBUG ("plugin_data_destroy return");
1585 }
1586
1587 // FACTORY FUNCTIONS
1588
1589 // Provides the browser with pointers to the plugin functions that we
1590 // implement and initializes a local table with browser functions that
1591 // we may wish to call. Called once, after browser startup and before
1592 // the first plugin instance is created.
1593 // The field 'initialized' is set to true once this function has
1594 // finished. If 'initialized' is already true at the beginning of
1595 // this function, then it is evident that NP_Initialize has already
1596 // been called. There is no need to call this function more than once and
1597 // this workaround avoids any duplicate calls.
1598 NPError
NP_Initialize(NPNetscapeFuncs * browserTable,NPPluginFuncs * pluginTable)1599 NP_Initialize (NPNetscapeFuncs* browserTable, NPPluginFuncs* pluginTable)
1600 {
1601 PLUGIN_DEBUG ("NP_Initialize");
1602
1603 if (initialized)
1604 return NPERR_NO_ERROR;
1605 else if ((browserTable == NULL) || (pluginTable == NULL))
1606 {
1607 PLUGIN_ERROR ("Browser or plugin function table is NULL.");
1608
1609 return NPERR_INVALID_FUNCTABLE_ERROR;
1610 }
1611
1612 // Ensure that the major version of the plugin API that the browser
1613 // expects is not more recent than the major version of the API that
1614 // we've implemented.
1615 if ((browserTable->version >> 8) > NP_VERSION_MAJOR)
1616 {
1617 PLUGIN_ERROR ("Incompatible version.");
1618
1619 return NPERR_INCOMPATIBLE_VERSION_ERROR;
1620 }
1621
1622 // Ensure that the plugin function table we've received is large
1623 // enough to store the number of functions that we may provide.
1624 if (pluginTable->size < sizeof (NPPluginFuncs))
1625 {
1626 PLUGIN_ERROR ("Invalid plugin function table.");
1627
1628 return NPERR_INVALID_FUNCTABLE_ERROR;
1629 }
1630
1631 // Ensure that the browser function table is large enough to store
1632 // the number of browser functions that we may use.
1633 if (browserTable->size < sizeof (NPNetscapeFuncs))
1634 {
1635 PLUGIN_ERROR ("Invalid browser function table.");
1636
1637 return NPERR_INVALID_FUNCTABLE_ERROR;
1638 }
1639
1640 data_directory = g_strconcat(getenv("HOME"), "/.gcjwebplugin", NULL);
1641 whitelist_filename = g_strconcat (data_directory, "/whitelist.txt", NULL);
1642 // Make sure the plugin data directory exists, creating it if
1643 // necessary.
1644 if (!g_file_test (data_directory,
1645 (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
1646 {
1647 int file_error = 0;
1648
1649 file_error = g_mkdir (data_directory, 0700);
1650 if (file_error != 0)
1651 {
1652 PLUGIN_ERROR_THREE ("Failed to create data directory",
1653 data_directory,
1654 strerror (errno));
1655 return NPERR_GENERIC_ERROR;
1656 }
1657 }
1658
1659 // Store in a local table the browser functions that we may use.
1660 browserFunctions.version = browserTable->version;
1661 browserFunctions.size = browserTable->size;
1662 browserFunctions.posturl = browserTable->posturl;
1663 browserFunctions.geturl = browserTable->geturl;
1664 browserFunctions.geturlnotify = browserTable->geturlnotify;
1665 browserFunctions.requestread = browserTable->requestread;
1666 browserFunctions.newstream = browserTable->newstream;
1667 browserFunctions.write = browserTable->write;
1668 browserFunctions.destroystream = browserTable->destroystream;
1669 browserFunctions.status = browserTable->status;
1670 browserFunctions.uagent = browserTable->uagent;
1671 browserFunctions.memalloc = browserTable->memalloc;
1672 browserFunctions.memfree = browserTable->memfree;
1673 browserFunctions.memflush = browserTable->memflush;
1674 browserFunctions.reloadplugins = browserTable->reloadplugins;
1675 browserFunctions.getvalue = browserTable->getvalue;
1676
1677 // Return to the browser the plugin functions that we implement.
1678 pluginTable->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
1679 pluginTable->size = sizeof (NPPluginFuncs);
1680 #if (((NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR) < 20)
1681 pluginTable->newp = NewNPP_NewProc (GCJ_New);
1682 pluginTable->destroy = NewNPP_DestroyProc (GCJ_Destroy);
1683 pluginTable->setwindow = NewNPP_SetWindowProc (GCJ_SetWindow);
1684 pluginTable->newstream = NewNPP_NewStreamProc (GCJ_NewStream);
1685 pluginTable->destroystream = NewNPP_DestroyStreamProc (GCJ_DestroyStream);
1686 pluginTable->asfile = NewNPP_StreamAsFileProc (GCJ_StreamAsFile);
1687 pluginTable->writeready = NewNPP_WriteReadyProc (GCJ_WriteReady);
1688 pluginTable->write = NewNPP_WriteProc (GCJ_Write);
1689 pluginTable->print = NewNPP_PrintProc (GCJ_Print);
1690 pluginTable->urlnotify = NewNPP_URLNotifyProc (GCJ_URLNotify);
1691 pluginTable->getvalue = NewNPP_GetValueProc (GCJ_GetValue);
1692 #else
1693 pluginTable->newp = (NPP_NewProcPtr) (GCJ_New);
1694 pluginTable->destroy = (NPP_DestroyProcPtr) (GCJ_Destroy);
1695 pluginTable->setwindow = (NPP_SetWindowProcPtr) (GCJ_SetWindow);
1696 pluginTable->newstream = (NPP_NewStreamProcPtr) (GCJ_NewStream);
1697 pluginTable->destroystream = (NPP_DestroyStreamProcPtr) (GCJ_DestroyStream);
1698 pluginTable->asfile = (NPP_StreamAsFileProcPtr) (GCJ_StreamAsFile);
1699 pluginTable->writeready = (NPP_WriteReadyProcPtr) (GCJ_WriteReady);
1700 pluginTable->write = (NPP_WriteProcPtr) (GCJ_Write);
1701 pluginTable->print = (NPP_PrintProcPtr) (GCJ_Print);
1702 pluginTable->urlnotify = (NPP_URLNotifyProcPtr) (GCJ_URLNotify);
1703 pluginTable->getvalue = (NPP_GetValueProcPtr) (GCJ_GetValue);
1704 #endif
1705
1706 initialized = true;
1707
1708 // Initialize threads (needed for mutexes).
1709 if (!g_thread_supported ())
1710 g_thread_init (NULL);
1711
1712 plugin_instance_mutex = g_mutex_new ();
1713
1714 PLUGIN_DEBUG ("NP_Initialize: using " APPLETVIEWER_EXECUTABLE ".");
1715
1716 PLUGIN_DEBUG ("NP_Initialize return");
1717
1718 return NPERR_NO_ERROR;
1719 }
1720
1721 // Returns a string describing the MIME type that this plugin
1722 // handles.
1723 char*
NP_GetMIMEDescription(void)1724 NP_GetMIMEDescription (void)
1725 {
1726 PLUGIN_DEBUG ("NP_GetMIMEDescription");
1727
1728 PLUGIN_DEBUG ("NP_GetMIMEDescription return");
1729
1730 return (char*) PLUGIN_MIME_DESC;
1731 }
1732
1733 // Returns a value relevant to the plugin as a whole. The browser
1734 // calls this function to obtain information about the plugin.
1735 NPError
NP_GetValue(void * future,NPPVariable variable,void * value)1736 NP_GetValue (void* future, NPPVariable variable, void* value)
1737 {
1738 PLUGIN_DEBUG ("NP_GetValue");
1739
1740 NPError result = NPERR_NO_ERROR;
1741 gchar** char_value = (gchar**) value;
1742
1743 switch (variable)
1744 {
1745 case NPPVpluginNameString:
1746 PLUGIN_DEBUG ("NP_GetValue: returning plugin name.");
1747 *char_value = g_strdup (PLUGIN_NAME " " PACKAGE_VERSION);
1748 break;
1749
1750 case NPPVpluginDescriptionString:
1751 PLUGIN_DEBUG ("NP_GetValue: returning plugin description.");
1752 *char_value = g_strdup (PLUGIN_DESC);
1753 break;
1754
1755 default:
1756 PLUGIN_ERROR ("Unknown plugin value requested.");
1757 result = NPERR_GENERIC_ERROR;
1758 break;
1759 }
1760
1761 PLUGIN_DEBUG ("NP_GetValue return");
1762
1763 return result;
1764 }
1765
1766 // Shuts down the plugin. Called after the last plugin instance is
1767 // destroyed.
1768 NPError
NP_Shutdown(void)1769 NP_Shutdown (void)
1770 {
1771 PLUGIN_DEBUG ("NP_Shutdown");
1772
1773 // Free mutex.
1774 if (plugin_instance_mutex)
1775 {
1776 g_mutex_free (plugin_instance_mutex);
1777 plugin_instance_mutex = NULL;
1778 }
1779
1780 if (whitelist_file)
1781 {
1782 g_io_channel_close (whitelist_file);
1783 whitelist_file = NULL;
1784 }
1785
1786 if (data_directory)
1787 {
1788 g_free (data_directory);
1789 data_directory = NULL;
1790 }
1791
1792 if (whitelist_filename)
1793 {
1794 g_free (whitelist_filename);
1795 whitelist_filename = NULL;
1796 }
1797
1798 initialized = false;
1799
1800 PLUGIN_DEBUG ("NP_Shutdown return");
1801
1802 return NPERR_NO_ERROR;
1803 }
1804