Merged revisions 143552,143554,143557,143560,143562,143564-143567,143570-143573,14357...
[official-gcc.git] / libjava / classpath / native / plugin / gcjwebplugin.cc
blob71cffccb6748611489e4e0b7f63f486759693a6a
1 /* gcjwebplugin.cc -- web browser plugin to execute Java applets
2 Copyright (C) 2003, 2004, 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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.
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.
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.
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.
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. */
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>
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
54 // GLib includes.
55 #include <glib.h>
56 #include <glib/gstdio.h>
58 // GTK includes.
59 #include <gtk/gtk.h>
61 // gcjwebplugin includes.
62 #include "config.h"
64 // Documentbase retrieval includes.
65 #include <nsIPluginInstance.h>
66 #include <nsIPluginInstancePeer.h>
67 #include <nsIPluginTagInfo2.h>
69 // Debugging macros.
70 #define PLUGIN_DEBUG(message) \
71 g_print ("GCJ PLUGIN: thread %p: %s\n", g_thread_self (), message)
73 #define PLUGIN_DEBUG_TWO(first, second) \
74 g_print ("GCJ PLUGIN: thread %p: %s %s\n", g_thread_self (), \
75 first, second)
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)
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)
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)
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
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 "\"."
148 // Documentbase retrieval required definition.
149 static NS_DEFINE_IID (kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID);
151 // Browser function table.
152 static NPNetscapeFuncs browserFunctions;
154 // Data directory for plugin.
155 static gchar* data_directory;
157 // Whitelist filename
158 static gchar* whitelist_filename;
160 // Keeps track of initialization. NP_Initialize should only be
161 // called once.
162 gboolean initialized = false;
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
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;
203 // Documentbase retrieval type-punning union.
204 typedef union
206 void** void_field;
207 nsIPluginTagInfo2** info_field;
208 } info_union;
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);
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;
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.
256 // INSTANCE FUNCTIONS
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
269 GCJ_New (NPMIMEType pluginType, NPP instance, uint16 mode,
270 int16 argc, char* argn[], char* argv[],
271 NPSavedData* saved)
273 PLUGIN_DEBUG ("GCJ_New");
275 NPError np_error = NPERR_NO_ERROR;
276 GCJPluginData* data = NULL;
278 gchar* documentbase = NULL;
279 gchar* read_message = NULL;
280 gchar* applet_tag = NULL;
281 gchar* tag_message = NULL;
283 if (!instance)
285 PLUGIN_ERROR ("Browser-provided instance pointer is NULL.");
286 np_error = NPERR_INVALID_INSTANCE_ERROR;
287 goto cleanup_done;
290 // data
291 plugin_data_new (&data);
292 if (data == NULL)
294 PLUGIN_ERROR ("Failed to allocate plugin data.");
295 np_error = NPERR_OUT_OF_MEMORY_ERROR;
296 goto cleanup_done;
299 // Initialize data->instance_string.
301 // instance_string should be unique for this process so we use a
302 // combination of getpid and plugin_instance_counter.
304 // Critical region. Reference and increment plugin_instance_counter
305 // global.
306 g_mutex_lock (plugin_instance_mutex);
308 // data->instance_string
309 data->instance_string = g_strdup_printf ("instance-%d-%ld",
310 getpid (),
311 plugin_instance_counter++);
313 g_mutex_unlock (plugin_instance_mutex);
315 // data->appletviewer_mutex
316 data->appletviewer_mutex = g_mutex_new ();
318 // Documentbase retrieval.
319 documentbase = plugin_get_documentbase (instance);
320 if (!documentbase)
322 PLUGIN_ERROR ("Documentbase retrieval failed."
323 " Browser not Mozilla-based?");
324 goto cleanup_appletviewer_mutex;
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)
332 if (channel_error)
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;
340 else
341 PLUGIN_ERROR_TWO ("Failed to open whitelist file",
342 whitelist_filename);
344 return NPERR_GENERIC_ERROR;
347 if (!plugin_user_trusts_documentbase (documentbase))
349 PLUGIN_ERROR ("User does not trust applet.");
350 np_error = NPERR_GENERIC_ERROR;
351 goto cleanup_appletviewer_mutex;
354 // Create appletviewer-to-plugin pipe which we refer to as the input
355 // pipe.
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)
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;
369 if (mkfifo (data->in_pipe_name, 0700) == -1 && errno != EEXIST)
371 PLUGIN_ERROR_TWO ("Failed to create input pipe", strerror (errno));
372 np_error = NPERR_GENERIC_ERROR;
373 goto cleanup_in_pipe_name;
376 // Create plugin-to-appletviewer pipe which we refer to as the
377 // output pipe.
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);
383 if (!data->out_pipe_name)
385 PLUGIN_ERROR ("Failed to create output pipe name.");
386 np_error = NPERR_OUT_OF_MEMORY_ERROR;
387 goto cleanup_out_pipe_name;
390 if (mkfifo (data->out_pipe_name, 0700) == -1 && errno != EEXIST)
392 PLUGIN_ERROR_TWO ("Failed to create output pipe", strerror (errno));
393 np_error = NPERR_GENERIC_ERROR;
394 goto cleanup_out_pipe_name;
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.
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);
407 np_error = plugin_start_appletviewer (data);
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)
413 if (plugin_failed ())
414 goto cleanup_applet_failure;
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)
424 if (channel_error)
426 PLUGIN_ERROR_TWO ("Failed to create output channel",
427 channel_error->message);
428 g_error_free (channel_error);
429 channel_error = NULL;
431 else
432 PLUGIN_ERROR ("Failed to create output channel");
434 np_error = NPERR_GENERIC_ERROR;
435 goto cleanup_out_to_appletviewer;
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);
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)
451 if (channel_error)
453 PLUGIN_ERROR_TWO ("Failed to create input channel",
454 channel_error->message);
455 g_error_free (channel_error);
456 channel_error = NULL;
458 else
459 PLUGIN_ERROR ("Failed to create input channel");
461 np_error = NPERR_GENERIC_ERROR;
462 goto cleanup_in_from_appletviewer;
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);
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)
478 if (channel_error)
480 PLUGIN_ERROR_TWO ("Receiving confirmation from appletviewer failed",
481 channel_error->message);
482 g_error_free (channel_error);
483 channel_error = NULL;
485 else
486 PLUGIN_ERROR ("Receiving confirmation from appletviewer failed");
488 np_error = NPERR_GENERIC_ERROR;
489 goto cleanup_in_watch_source;
492 PLUGIN_DEBUG ("GCJ_New: got confirmation that appletviewer is running.");
493 data->appletviewer_alive = TRUE;
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);
499 plugin_send_message_to_appletviewer (data, data->instance_string);
500 plugin_send_message_to_appletviewer (data, tag_message);
502 g_mutex_unlock (data->appletviewer_mutex);
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.
508 // Set back-pointer to owner instance.
509 data->owner = instance;
510 instance->pdata = data;
511 goto cleanup_done;
513 // An error occurred while initializing the plugin data or spawning
514 // the appletviewer so we free the data we've already allocated.
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;
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;
527 // cleanup_out_watch_source:
528 g_source_remove (data->out_watch_source);
529 data->out_watch_source = 0;
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;
536 // cleanup_out_pipe:
537 // Delete output pipe.
538 unlink (data->out_pipe_name);
540 cleanup_applet_failure:
541 cleanup_out_pipe_name:
542 g_free (data->out_pipe_name);
543 data->out_pipe_name = NULL;
545 // cleanup_in_pipe:
546 // Delete input pipe.
547 unlink (data->in_pipe_name);
549 cleanup_in_pipe_name:
550 g_free (data->in_pipe_name);
551 data->in_pipe_name = NULL;
553 cleanup_appletviewer_mutex:
554 g_free (data->appletviewer_mutex);
555 data->appletviewer_mutex = NULL;
557 // cleanup_instance_string:
558 g_free (data->instance_string);
559 data->instance_string = NULL;
561 // cleanup_data:
562 // Eliminate back-pointer to plugin instance.
563 data->owner = NULL;
564 (*browserFunctions.memfree) (data);
565 data = NULL;
567 // Initialization failed so return a NULL pointer for the browser
568 // data.
569 instance->pdata = NULL;
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;
581 PLUGIN_DEBUG ("GCJ_New return");
583 return np_error;
586 NPError
587 GCJ_GetValue (NPP instance, NPPVariable variable, void* value)
589 PLUGIN_DEBUG ("GCJ_GetValue");
591 NPError np_error = NPERR_NO_ERROR;
593 switch (variable)
595 // This plugin needs XEmbed support.
596 case NPPVpluginNeedsXEmbed:
598 PLUGIN_DEBUG ("GCJ_GetValue: returning TRUE for NeedsXEmbed.");
599 PRBool* bool_value = (PRBool*) value;
600 *bool_value = PR_TRUE;
602 break;
604 default:
605 PLUGIN_ERROR ("Unknown plugin value requested.");
606 np_error = NPERR_GENERIC_ERROR;
607 break;
610 PLUGIN_DEBUG ("GCJ_GetValue return");
612 return np_error;
615 NPError
616 GCJ_Destroy (NPP instance, NPSavedData** save)
618 PLUGIN_DEBUG ("GCJ_Destroy");
620 GCJPluginData* data = (GCJPluginData*) instance->pdata;
622 if (data)
624 // Critical region. Stop the appletviewer.
625 g_mutex_lock (data->appletviewer_mutex);
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);
632 g_mutex_unlock (data->appletviewer_mutex);
634 // Free plugin data.
635 plugin_data_destroy (&data);
638 PLUGIN_DEBUG ("GCJ_Destroy return");
640 return NPERR_NO_ERROR;
643 NPError
644 GCJ_SetWindow (NPP instance, NPWindow* window)
646 PLUGIN_DEBUG ("GCJ_SetWindow");
648 if (instance == NULL)
650 PLUGIN_ERROR ("Invalid instance.");
652 return NPERR_INVALID_INSTANCE_ERROR;
655 GCJPluginData* data = (GCJPluginData*) instance->pdata;
657 // Simply return if we receive a NULL window.
658 if ((window == NULL) || (window->window == NULL))
660 PLUGIN_DEBUG ("GCJ_SetWindow: got NULL window.");
662 return NPERR_NO_ERROR;
665 if (data->window_handle)
667 // The window already exists.
668 if (data->window_handle == window->window)
670 // The parent window is the same as in previous calls.
671 PLUGIN_DEBUG ("GCJ_SetWindow: window already exists.");
673 // Critical region. Read data->appletviewer_mutex and send
674 // a message to the appletviewer.
675 g_mutex_lock (data->appletviewer_mutex);
677 if (data->appletviewer_alive)
679 // The window is the same as it was for the last
680 // SetWindow call.
681 if (window->width != data->window_width)
683 PLUGIN_DEBUG ("GCJ_SetWindow: window width changed.");
684 // The width of the plugin window has changed.
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;
695 // Store the new width.
696 data->window_width = window->width;
699 if (window->height != data->window_height)
701 PLUGIN_DEBUG ("GCJ_SetWindow: window height changed.");
702 // The height of the plugin window has changed.
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;
713 // Store the new height.
714 data->window_height = window->height;
717 else
719 // The appletviewer is not running.
720 PLUGIN_DEBUG ("GCJ_SetWindow: appletviewer is not running.");
723 g_mutex_unlock (data->appletviewer_mutex);
725 else
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.");
732 else
734 PLUGIN_DEBUG ("GCJ_SetWindow: setting window.");
736 // Critical region. Send messages to appletviewer.
737 g_mutex_lock (data->appletviewer_mutex);
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;
746 g_mutex_unlock (data->appletviewer_mutex);
748 // Store the window handle.
749 data->window_handle = window->window;
752 PLUGIN_DEBUG ("GCJ_SetWindow return");
754 return NPERR_NO_ERROR;
757 NPError
758 GCJ_NewStream (NPP instance, NPMIMEType type, NPStream* stream,
759 NPBool seekable, uint16* stype)
761 PLUGIN_DEBUG ("GCJ_NewStream");
763 PLUGIN_DEBUG ("GCJ_NewStream return");
765 return NPERR_NO_ERROR;
768 void
769 GCJ_StreamAsFile (NPP instance, NPStream* stream, const char* filename)
771 PLUGIN_DEBUG ("GCJ_StreamAsFile");
773 PLUGIN_DEBUG ("GCJ_StreamAsFile return");
776 NPError
777 GCJ_DestroyStream (NPP instance, NPStream* stream, NPReason reason)
779 PLUGIN_DEBUG ("GCJ_DestroyStream");
781 PLUGIN_DEBUG ("GCJ_DestroyStream return");
783 return NPERR_NO_ERROR;
786 int32
787 GCJ_WriteReady (NPP instance, NPStream* stream)
789 PLUGIN_DEBUG ("GCJ_WriteReady");
791 PLUGIN_DEBUG ("GCJ_WriteReady return");
793 return 0;
796 int32
797 GCJ_Write (NPP instance, NPStream* stream, int32 offset, int32 len,
798 void* buffer)
800 PLUGIN_DEBUG ("GCJ_Write");
802 PLUGIN_DEBUG ("GCJ_Write return");
804 return 0;
807 void
808 GCJ_Print (NPP instance, NPPrint* platformPrint)
810 PLUGIN_DEBUG ("GCJ_Print");
812 PLUGIN_DEBUG ("GCJ_Print return");
815 int16
816 GCJ_HandleEvent (NPP instance, void* event)
818 PLUGIN_DEBUG ("GCJ_HandleEvent");
820 PLUGIN_DEBUG ("GCJ_HandleEvent return");
822 return 0;
825 void
826 GCJ_URLNotify (NPP instance, const char* url, NPReason reason,
827 void* notifyData)
829 PLUGIN_DEBUG ("GCJ_URLNotify");
831 PLUGIN_DEBUG ("GCJ_URLNotify return");
834 #if (((NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR) < 20)
835 jref
836 #else
837 void*
838 #endif
839 GCJ_GetJavaClass (void)
841 PLUGIN_DEBUG ("GCJ_GetJavaClass");
843 PLUGIN_DEBUG ("GCJ_GetJavaClass return");
845 return 0;
848 // HELPER FUNCTIONS
850 static void
851 plugin_data_new (GCJPluginData** data)
853 PLUGIN_DEBUG ("plugin_data_new");
855 *data = (GCJPluginData*)
856 (*browserFunctions.memalloc) (sizeof (struct GCJPluginData));
858 // appletviewer_alive is false until the applet viewer is spawned.
859 if (*data)
860 memset (*data, 0, sizeof (struct GCJPluginData));
862 PLUGIN_DEBUG ("plugin_data_new return");
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*
872 plugin_get_documentbase (NPP instance)
874 PLUGIN_DEBUG ("plugin_get_documentbase");
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;
884 xpcom_instance = (nsIPluginInstance*) (instance->ndata);
885 if (!xpcom_instance)
887 PLUGIN_ERROR ("xpcom_instance is NULL.");
888 goto cleanup_done;
891 xpcom_instance->GetPeer (&peer);
892 if (!peer)
894 PLUGIN_ERROR ("peer is NULL.");
895 goto cleanup_done;
898 u.info_field = &pluginTagInfo2;
900 result = peer->QueryInterface (kIPluginTagInfo2IID,
901 u.void_field);
902 if (result || !pluginTagInfo2)
904 PLUGIN_ERROR ("pluginTagInfo2 retrieval failed.");
905 goto cleanup_peer;
908 pluginTagInfo2->GetDocumentBase (&documentbase);
910 if (!documentbase)
912 PLUGIN_ERROR ("documentbase is NULL.");
913 goto cleanup_plugintaginfo2;
916 documentbase_copy = g_strdup (documentbase);
918 // Release references.
919 cleanup_plugintaginfo2:
920 NS_RELEASE (pluginTagInfo2);
922 cleanup_peer:
923 NS_RELEASE (peer);
925 cleanup_done:
926 PLUGIN_DEBUG ("plugin_get_documentbase return");
928 return documentbase_copy;
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
935 plugin_failed ()
937 GtkWidget* dialog = NULL;
938 GtkWidget* ok_button = NULL;
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)
951 gtk_widget_destroy (dialog);
952 return true;
954 return false;
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
962 plugin_user_trusts_documentbase (char* documentbase)
964 bool applet_in_whitelist = false;
966 // Check if documentbase is in whitelist.
967 while (true)
969 gchar* whitelist_entry = NULL;
970 gchar* newline_documentbase = NULL;
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)
978 if (channel_error)
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;
985 else
986 PLUGIN_ERROR ("Failed to read line from whitelist file.");
987 g_free (whitelist_entry);
988 whitelist_entry = NULL;
989 break;
992 newline_documentbase = g_strdup_printf ("%s\n", documentbase);
993 if (!strcmp (newline_documentbase, whitelist_entry))
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;
1002 g_free (whitelist_entry);
1003 whitelist_entry = NULL;
1004 g_free (newline_documentbase);
1005 newline_documentbase = NULL;
1008 return applet_in_whitelist ? true
1009 : plugin_ask_user_about_documentbase (documentbase);
1012 // plugin_add_documentbase_to_whitelist adds the given documentbase to
1013 // the user's documentbase whitelist.
1014 static void
1015 plugin_add_documentbase_to_whitelist (char* documentbase)
1017 gsize bytes_written = 0;
1018 char* newline_documentbase = NULL;
1019 GIOStatus status = G_IO_STATUS_NORMAL;
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;
1028 if (status != G_IO_STATUS_NORMAL)
1030 if (channel_error)
1032 PLUGIN_ERROR_TWO ("Error writing to whitelist file",
1033 channel_error->message);
1034 g_error_free (channel_error);
1035 channel_error = NULL;
1037 else
1038 PLUGIN_ERROR ("Error writing to whitelist file.");
1041 if (g_io_channel_flush (whitelist_file, &channel_error)
1042 != G_IO_STATUS_NORMAL)
1044 if (channel_error)
1046 PLUGIN_ERROR_TWO ("Failed to write whitelist file",
1047 channel_error->message);
1048 g_error_free (channel_error);
1049 channel_error = NULL;
1051 else
1052 PLUGIN_ERROR ("Failed to write whitelist file.");
1055 if (g_io_channel_shutdown (whitelist_file, TRUE, &channel_error)
1056 != G_IO_STATUS_NORMAL)
1058 if (channel_error)
1060 PLUGIN_ERROR_TWO ("Failed to close whitelist file",
1061 channel_error->message);
1062 g_error_free (channel_error);
1063 channel_error = NULL;
1065 else
1066 PLUGIN_ERROR ("Failed to close whitelist file.");
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
1080 plugin_ask_user_about_documentbase (char* documentbase)
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;
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);
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);
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)
1113 // The user does not trust this documentbase.
1114 return false;
1116 else if (dialog_response == GTK_RESPONSE_APPLY)
1118 // The user wants this documentbase added to his documentbase
1119 // whitelist.
1120 plugin_add_documentbase_to_whitelist (documentbase);
1122 // The user trusts this documentbase.
1123 return true;
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
1131 plugin_in_pipe_callback (GIOChannel* source,
1132 GIOCondition condition,
1133 gpointer plugin_data)
1135 PLUGIN_DEBUG ("plugin_in_pipe_callback");
1137 GCJPluginData* data = (GCJPluginData*) plugin_data;
1138 gboolean keep_installed = TRUE;
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)
1146 // Critical region. Set or clear data->appletviewer_alive.
1147 g_mutex_lock (data->appletviewer_mutex);
1149 if (condition & G_IO_IN)
1151 gchar* message = NULL;
1153 if (g_io_channel_read_line (data->in_from_appletviewer,
1154 &message, NULL, NULL,
1155 &channel_error)
1156 != G_IO_STATUS_NORMAL)
1158 if (channel_error)
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;
1165 else
1166 PLUGIN_ERROR ("Failed to read line from input channel");
1168 else
1170 if (g_str_has_prefix (message, "url "))
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;
1185 else if (g_str_has_prefix (message, "status "))
1187 gchar** parts = g_strsplit (message, " ", 2);
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;
1195 g_print (" PIPE: plugin read %s\n", message);
1198 g_free (message);
1199 message = NULL;
1201 keep_installed = TRUE;
1204 if (condition & (G_IO_ERR | G_IO_HUP))
1206 PLUGIN_DEBUG ("appletviewer has stopped.");
1207 data->appletviewer_alive = FALSE;
1208 keep_installed = FALSE;
1210 g_mutex_unlock (data->appletviewer_mutex);
1213 PLUGIN_DEBUG ("plugin_in_pipe_callback return");
1215 return keep_installed;
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
1223 plugin_out_pipe_callback (GIOChannel* source,
1224 GIOCondition condition,
1225 gpointer plugin_data)
1227 PLUGIN_DEBUG ("plugin_out_pipe_callback");
1229 GCJPluginData* data = (GCJPluginData*) plugin_data;
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)
1237 // Critical region. Clear data->appletviewer_alive.
1238 g_mutex_lock (data->appletviewer_mutex);
1240 PLUGIN_DEBUG ("plugin_out_pipe_callback: appletviewer has stopped.");
1241 data->appletviewer_alive = FALSE;
1243 g_mutex_unlock (data->appletviewer_mutex);
1246 PLUGIN_DEBUG ("plugin_out_pipe_callback return");
1248 return FALSE;
1251 static NPError
1252 plugin_start_appletviewer (GCJPluginData* data)
1254 PLUGIN_DEBUG ("plugin_start_appletviewer");
1255 NPError error = NPERR_NO_ERROR;
1257 if (!data->appletviewer_alive)
1259 gchar* command_line[3] = { NULL, NULL, NULL };
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;
1269 if (!g_spawn_async (NULL, command_line, NULL, (GSpawnFlags) 0,
1270 NULL, NULL, NULL, &channel_error))
1272 if (channel_error)
1274 PLUGIN_ERROR_TWO ("Failed to spawn applet viewer",
1275 channel_error->message);
1276 g_error_free (channel_error);
1277 channel_error = NULL;
1279 else
1280 PLUGIN_ERROR ("Failed to spawn applet viewer");
1281 error = NPERR_GENERIC_ERROR;
1282 goto cleanup;
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;
1294 PLUGIN_DEBUG ("plugin_start_appletviewer return");
1295 return error;
1298 // Build up the applet tag string that we'll send to the applet
1299 // viewer.
1300 static gchar*
1301 plugin_create_applet_tag (int16 argc, char* argn[], char* argv[])
1303 PLUGIN_DEBUG ("plugin_create_applet_tag");
1305 gchar* applet_tag = g_strdup ("<EMBED ");
1306 gchar* parameters = g_strdup ("");
1308 for (int16 i = 0; i < argc; i++)
1310 if (!g_ascii_strcasecmp (argn[i], "code"))
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;
1317 else if (!g_ascii_strcasecmp (argn[i], "codebase"))
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;
1324 else if (!g_ascii_strcasecmp (argn[i], "archive"))
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;
1331 else if (!g_ascii_strcasecmp (argn[i], "width"))
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;
1338 else if (!g_ascii_strcasecmp (argn[i], "height"))
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;
1345 else
1347 // Escape the parameter value so that line termination
1348 // characters will pass through the pipe.
1349 if (argv[i] != '\0')
1351 gchar* escaped = NULL;
1353 escaped = g_strescape (argv[i], NULL);
1354 parameters = g_strconcat (parameters, "<PARAM NAME=\"", argn[i],
1355 "\" VALUE=\"", escaped, "\">", NULL);
1357 g_free (escaped);
1358 escaped = NULL;
1363 applet_tag = g_strconcat (applet_tag, ">", parameters, "</EMBED>", NULL);
1365 g_free (parameters);
1366 parameters = NULL;
1368 PLUGIN_DEBUG ("plugin_create_applet_tag return");
1370 return applet_tag;
1373 // plugin_send_message_to_appletviewer must be called while holding
1374 // data->appletviewer_mutex.
1375 static void
1376 plugin_send_message_to_appletviewer (GCJPluginData* data, gchar const* message)
1378 PLUGIN_DEBUG ("plugin_send_message_to_appletviewer");
1380 if (data->appletviewer_alive)
1382 gchar* newline_message = NULL;
1383 gsize bytes_written = 0;
1385 // Send message to appletviewer.
1386 newline_message = g_strdup_printf ("%s\n", message);
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)
1396 if (channel_error)
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;
1403 else
1404 PLUGIN_ERROR ("Failed to write bytes to output channel");
1407 if (g_io_channel_flush (data->out_to_appletviewer, &channel_error)
1408 != G_IO_STATUS_NORMAL)
1410 if (channel_error)
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;
1417 else
1418 PLUGIN_ERROR ("Failed to flush bytes to output channel");
1420 g_free (newline_message);
1421 newline_message = NULL;
1423 g_print (" PIPE: plugin wrote %s\n", message);
1426 PLUGIN_DEBUG ("plugin_send_message_to_appletviewer return");
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
1443 plugin_stop_appletviewer (GCJPluginData* data)
1445 PLUGIN_DEBUG ("plugin_stop_appletviewer");
1447 if (data->appletviewer_alive)
1449 // Shut down the appletviewer.
1450 gsize bytes_written = 0;
1452 if (data->out_to_appletviewer)
1454 if (g_io_channel_write_chars (data->out_to_appletviewer, "shutdown",
1455 -1, &bytes_written, &channel_error)
1456 != G_IO_STATUS_NORMAL)
1458 if (channel_error)
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;
1465 else
1466 PLUGIN_ERROR ("Failed to write shutdown message to");
1469 if (g_io_channel_flush (data->out_to_appletviewer, &channel_error)
1470 != G_IO_STATUS_NORMAL)
1472 if (channel_error)
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;
1479 else
1480 PLUGIN_ERROR ("Failed to write shutdown message to");
1483 if (g_io_channel_shutdown (data->out_to_appletviewer,
1484 TRUE, &channel_error)
1485 != G_IO_STATUS_NORMAL)
1487 if (channel_error)
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;
1494 else
1495 PLUGIN_ERROR ("Failed to shut down appletviewer");
1499 if (data->in_from_appletviewer)
1501 if (g_io_channel_shutdown (data->in_from_appletviewer,
1502 TRUE, &channel_error)
1503 != G_IO_STATUS_NORMAL)
1505 if (channel_error)
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;
1512 else
1513 PLUGIN_ERROR ("Failed to shut down appletviewer");
1518 PLUGIN_DEBUG ("plugin_stop_appletviewer return");
1521 static void
1522 plugin_data_destroy (GCJPluginData** data)
1524 PLUGIN_DEBUG ("plugin_data_destroy");
1526 GCJPluginData* tofree = *data;
1528 tofree->window_handle = NULL;
1529 tofree->window_height = 0;
1530 tofree->window_width = 0;
1532 // Copied from GCJ_New.
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;
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;
1545 // cleanup_out_watch_source:
1546 g_source_remove (tofree->out_watch_source);
1547 tofree->out_watch_source = 0;
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;
1554 // cleanup_out_pipe:
1555 // Delete output pipe.
1556 unlink (tofree->out_pipe_name);
1558 // cleanup_out_pipe_name:
1559 g_free (tofree->out_pipe_name);
1560 tofree->out_pipe_name = NULL;
1562 // cleanup_in_pipe:
1563 // Delete input pipe.
1564 unlink (tofree->in_pipe_name);
1566 // cleanup_in_pipe_name:
1567 g_free (tofree->in_pipe_name);
1568 tofree->in_pipe_name = NULL;
1570 // cleanup_appletviewer_mutex:
1571 g_free (tofree->appletviewer_mutex);
1572 tofree->appletviewer_mutex = NULL;
1574 // cleanup_instance_string:
1575 g_free (tofree->instance_string);
1576 tofree->instance_string = NULL;
1578 // cleanup_data:
1579 // Eliminate back-pointer to plugin instance.
1580 tofree->owner = NULL;
1581 (*browserFunctions.memfree) (tofree);
1582 tofree = NULL;
1584 PLUGIN_DEBUG ("plugin_data_destroy return");
1587 // FACTORY FUNCTIONS
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
1599 NP_Initialize (NPNetscapeFuncs* browserTable, NPPluginFuncs* pluginTable)
1601 PLUGIN_DEBUG ("NP_Initialize");
1603 if (initialized)
1604 return NPERR_NO_ERROR;
1605 else if ((browserTable == NULL) || (pluginTable == NULL))
1607 PLUGIN_ERROR ("Browser or plugin function table is NULL.");
1609 return NPERR_INVALID_FUNCTABLE_ERROR;
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)
1617 PLUGIN_ERROR ("Incompatible version.");
1619 return NPERR_INCOMPATIBLE_VERSION_ERROR;
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))
1626 PLUGIN_ERROR ("Invalid plugin function table.");
1628 return NPERR_INVALID_FUNCTABLE_ERROR;
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))
1635 PLUGIN_ERROR ("Invalid browser function table.");
1637 return NPERR_INVALID_FUNCTABLE_ERROR;
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)))
1647 int file_error = 0;
1649 file_error = g_mkdir (data_directory, 0700);
1650 if (file_error != 0)
1652 PLUGIN_ERROR_THREE ("Failed to create data directory",
1653 data_directory,
1654 strerror (errno));
1655 return NPERR_GENERIC_ERROR;
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;
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
1706 initialized = true;
1708 // Initialize threads (needed for mutexes).
1709 if (!g_thread_supported ())
1710 g_thread_init (NULL);
1712 plugin_instance_mutex = g_mutex_new ();
1714 PLUGIN_DEBUG ("NP_Initialize: using " APPLETVIEWER_EXECUTABLE ".");
1716 PLUGIN_DEBUG ("NP_Initialize return");
1718 return NPERR_NO_ERROR;
1721 // Returns a string describing the MIME type that this plugin
1722 // handles.
1723 char*
1724 NP_GetMIMEDescription (void)
1726 PLUGIN_DEBUG ("NP_GetMIMEDescription");
1728 PLUGIN_DEBUG ("NP_GetMIMEDescription return");
1730 return (char*) PLUGIN_MIME_DESC;
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
1736 NP_GetValue (void* future, NPPVariable variable, void* value)
1738 PLUGIN_DEBUG ("NP_GetValue");
1740 NPError result = NPERR_NO_ERROR;
1741 gchar** char_value = (gchar**) value;
1743 switch (variable)
1745 case NPPVpluginNameString:
1746 PLUGIN_DEBUG ("NP_GetValue: returning plugin name.");
1747 *char_value = g_strdup (PLUGIN_NAME " " PACKAGE_VERSION);
1748 break;
1750 case NPPVpluginDescriptionString:
1751 PLUGIN_DEBUG ("NP_GetValue: returning plugin description.");
1752 *char_value = g_strdup (PLUGIN_DESC);
1753 break;
1755 default:
1756 PLUGIN_ERROR ("Unknown plugin value requested.");
1757 result = NPERR_GENERIC_ERROR;
1758 break;
1761 PLUGIN_DEBUG ("NP_GetValue return");
1763 return result;
1766 // Shuts down the plugin. Called after the last plugin instance is
1767 // destroyed.
1768 NPError
1769 NP_Shutdown (void)
1771 PLUGIN_DEBUG ("NP_Shutdown");
1773 // Free mutex.
1774 if (plugin_instance_mutex)
1776 g_mutex_free (plugin_instance_mutex);
1777 plugin_instance_mutex = NULL;
1780 if (whitelist_file)
1782 g_io_channel_close (whitelist_file);
1783 whitelist_file = NULL;
1786 if (data_directory)
1788 g_free (data_directory);
1789 data_directory = NULL;
1792 if (whitelist_filename)
1794 g_free (whitelist_filename);
1795 whitelist_filename = NULL;
1798 initialized = false;
1800 PLUGIN_DEBUG ("NP_Shutdown return");
1802 return NPERR_NO_ERROR;