OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [native/] [plugin/] [gcjwebplugin.cc] - Blame information for rev 774

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 774 jeremybenn
/* 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
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
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
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
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
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
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
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
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
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
808
GCJ_Print (NPP instance, NPPrint* platformPrint)
809
{
810
  PLUGIN_DEBUG ("GCJ_Print");
811
 
812
  PLUGIN_DEBUG ("GCJ_Print return");
813
}
814
 
815
int16
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
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
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
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*
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
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
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
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
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
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
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
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*
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
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
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
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
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*
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
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
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
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.