source: trunk/icedtea-web/plugin/icedteanp/IcedTeaNPPlugin.cc@ 429

Last change on this file since 429 was 429, checked in by dmik, 11 years ago

icedtea-web: Merge version 1.5.1 from vendor to trunk.

File size: 77.9 KB
Line 
1/* IcedTeaNPPlugin.cc -- web browser plugin to execute Java applets
2 Copyright (C) 2003, 2004, 2006, 2007 Free Software Foundation, Inc.
3 Copyright (C) 2009, 2010 Red Hat
4
5This file is part of GNU Classpath.
6
7GNU Classpath is free software; you can redistribute it and/or modify
8it under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 2, or (at your option)
10any later version.
11
12GNU Classpath is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along with GNU Classpath; see the file COPYING. If not, write to the
19Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
2002110-1301 USA.
21
22Linking this library statically or dynamically with other modules is
23making a combined work based on this library. Thus, the terms and
24conditions of the GNU General Public License cover the whole
25combination.
26
27As a special exception, the copyright holders of this library give you
28permission to link this library with independent modules to produce an
29executable, regardless of the license terms of these independent
30modules, and to copy and distribute the resulting executable under
31terms of your choice, provided that you also meet, for each linked
32independent module, the terms and conditions of the license of that
33module. An independent module is a module which is not derived from
34or based on this library. If you modify this library, you may extend
35this exception to your version of the library, but you are not
36obligated to do so. If you do not wish to do so, delete this
37exception statement from your version. */
38
39#ifdef __OS2__
40// OS/2 includes.
41#define INCL_DOSPROCESS
42#define INCL_DOSERRORS
43#define INCL_WINDIALOG
44#include <os2.h>
45#include <emx/startup.h>
46#include <sys/socket.h>
47#include <fcntl.h>
48#include <sstream>
49#include "OS_OS2.h"
50#endif
51
52// System includes.
53#include <dlfcn.h>
54#include <unistd.h>
55#include <fcntl.h>
56#include <dirent.h>
57#include <errno.h>
58#include <libgen.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <string>
63#include <sys/stat.h>
64#include <sys/types.h>
65#include <unistd.h>
66#include <new>
67
68//IcedTea-plugin includes
69#include "IcedTeaPluginUtils.h"
70#include "IcedTeaParseProperties.h"
71// Liveconnect extension
72#include "IcedTeaScriptablePluginObject.h"
73#include "IcedTeaNPPlugin.h"
74
75#ifdef __OS2__
76#define DT_SOCKET_DLL "jdtsock"
77#else
78#define DT_SOCKET_DLL "dt_socket"
79#endif
80
81// Plugin information passed to about:plugins.
82#define PLUGIN_FULL_NAME PLUGIN_NAME " (using " PLUGIN_VERSION ")"
83#define PLUGIN_DESC "The <a href=\"" PACKAGE_URL "\">" PLUGIN_NAME "</a> executes Java applets."
84
85#ifdef HAVE_JAVA7
86 #define JPI_VERSION "1.7.0_" JDK_UPDATE_VERSION
87 #define PLUGIN_APPLET_MIME_DESC7 \
88 "application/x-java-applet;version=1.7:class,jar:IcedTea;"
89 #define PLUGIN_BEAN_MIME_DESC7 \
90 "application/x-java-bean;version=1.7:class,jar:IcedTea;"
91#else
92 #define JPI_VERSION "1.6.0_" JDK_UPDATE_VERSION
93 #define PLUGIN_APPLET_MIME_DESC7
94 #define PLUGIN_BEAN_MIME_DESC7
95#endif
96
97#define PLUGIN_MIME_DESC \
98 "application/x-java-vm:class,jar:IcedTea;" \
99 "application/x-java-applet:class,jar:IcedTea;" \
100 "application/x-java-applet;version=1.1:class,jar:IcedTea;" \
101 "application/x-java-applet;version=1.1.1:class,jar:IcedTea;" \
102 "application/x-java-applet;version=1.1.2:class,jar:IcedTea;" \
103 "application/x-java-applet;version=1.1.3:class,jar:IcedTea;" \
104 "application/x-java-applet;version=1.2:class,jar:IcedTea;" \
105 "application/x-java-applet;version=1.2.1:class,jar:IcedTea;" \
106 "application/x-java-applet;version=1.2.2:class,jar:IcedTea;" \
107 "application/x-java-applet;version=1.3:class,jar:IcedTea;" \
108 "application/x-java-applet;version=1.3.1:class,jar:IcedTea;" \
109 "application/x-java-applet;version=1.4:class,jar:IcedTea;" \
110 "application/x-java-applet;version=1.4.1:class,jar:IcedTea;" \
111 "application/x-java-applet;version=1.4.2:class,jar:IcedTea;" \
112 "application/x-java-applet;version=1.5:class,jar:IcedTea;" \
113 "application/x-java-applet;version=1.6:class,jar:IcedTea;" \
114 PLUGIN_APPLET_MIME_DESC7 \
115 "application/x-java-applet;jpi-version=" JPI_VERSION ":class,jar:IcedTea;" \
116 "application/x-java-bean:class,jar:IcedTea;" \
117 "application/x-java-bean;version=1.1:class,jar:IcedTea;" \
118 "application/x-java-bean;version=1.1.1:class,jar:IcedTea;" \
119 "application/x-java-bean;version=1.1.2:class,jar:IcedTea;" \
120 "application/x-java-bean;version=1.1.3:class,jar:IcedTea;" \
121 "application/x-java-bean;version=1.2:class,jar:IcedTea;" \
122 "application/x-java-bean;version=1.2.1:class,jar:IcedTea;" \
123 "application/x-java-bean;version=1.2.2:class,jar:IcedTea;" \
124 "application/x-java-bean;version=1.3:class,jar:IcedTea;" \
125 "application/x-java-bean;version=1.3.1:class,jar:IcedTea;" \
126 "application/x-java-bean;version=1.4:class,jar:IcedTea;" \
127 "application/x-java-bean;version=1.4.1:class,jar:IcedTea;" \
128 "application/x-java-bean;version=1.4.2:class,jar:IcedTea;" \
129 "application/x-java-bean;version=1.5:class,jar:IcedTea;" \
130 "application/x-java-bean;version=1.6:class,jar:IcedTea;" \
131 PLUGIN_BEAN_MIME_DESC7 \
132 "application/x-java-bean;jpi-version=" JPI_VERSION ":class,jar:IcedTea;" \
133 "application/x-java-vm-npruntime::IcedTea;"
134
135#define PLUGIN_URL NS_INLINE_PLUGIN_CONTRACTID_PREFIX NS_JVM_MIME_TYPE
136#define PLUGIN_MIME_TYPE "application/x-java-vm"
137#define PLUGIN_FILE_EXTS "class,jar,zip"
138#define PLUGIN_MIME_COUNT 1
139
140#define FAILURE_MESSAGE "icedteanp plugin error: Failed to run %s." \
141 " For more detail rerun \"firefox -g\" in a terminal window."
142
143// Data directory for plugin.
144static std::string data_directory;
145static DIR *data_directory_descriptor;
146
147// Fully-qualified appletviewer default executable and rt.jar
148static const char* appletviewer_default_executable = ICEDTEA_WEB_JRE "/bin/java";
149static const char* appletviewer_default_rtjar = ICEDTEA_WEB_JRE "/lib/rt.jar";
150
151// Applet viewer input channel (needs to be static because it is used in plugin_in_pipe_callback)
152static GIOChannel* in_from_appletviewer = NULL;
153
154#ifdef __OS2__
155#define CLOSE_FD(fd) do { if (fd != -1) { close (fd); fd = -1; } } while (0)
156#endif
157
158#ifdef __OS2__
159int in_pipe[2] = { -1 };
160#else
161// Applet viewer input pipe name.
162gchar* in_pipe_name;
163#endif
164
165// Applet viewer input watch source.
166gint in_watch_source;
167
168#ifdef __OS2__
169int out_pipe[2] = { -1 };
170#else
171// Applet viewer output pipe name.
172gchar* out_pipe_name;
173#endif
174
175// Applet viewer debug pipe name.
176gchar* debug_pipe_name = NULL;
177
178// Applet viewer output watch source.
179gint out_watch_source;
180
181// Thread ID of plug-in thread
182pthread_t itnp_plugin_thread_id;
183
184// Mutex to lock async call queue
185pthread_mutex_t pluginAsyncCallMutex;
186
187/*to sync pipe to apletviewer console*/
188pthread_mutex_t debug_pipe_lock = PTHREAD_MUTEX_INITIALIZER;
189
190// Applet viewer output channel.
191GIOChannel* out_to_appletviewer;
192
193// Applet viewer debug channel.
194GIOChannel* debug_to_appletviewer = NULL;
195
196// Tracks jvm status
197gboolean jvm_up = FALSE;
198
199// Keeps track of initialization. NP_Initialize should only be
200// called once.
201gboolean initialized = false;
202
203// browser functions into mozilla
204NPNetscapeFuncs browser_functions;
205
206// Various message buses carrying information to/from Java, and internally
207MessageBus* plugin_to_java_bus;
208MessageBus* java_to_plugin_bus;
209//MessageBus* internal_bus = new MessageBus();
210
211// Processor for plugin requests
212PluginRequestProcessor* plugin_req_proc;
213
214// Sends messages to Java over the bus
215JavaMessageSender* java_req_proc;
216
217// Queue processing threads
218static pthread_t plugin_request_processor_thread1;
219static pthread_t plugin_request_processor_thread2;
220static pthread_t plugin_request_processor_thread3;
221
222#ifdef __OS2__
223struct QueueProcessorData
224{
225 PluginRequestProcessor *processor;
226 bool stopRequested;
227};
228
229static QueueProcessorData queue_processor_data1 = { NULL, false };
230static QueueProcessorData queue_processor_data2 = { NULL, false };
231static QueueProcessorData queue_processor_data3 = { NULL, false };
232#endif
233
234// Static instance helper functions.
235// Retrieve the current document's documentbase.
236static std::string plugin_get_documentbase (NPP instance);
237// Callback used to monitor input pipe status.
238static gboolean plugin_in_pipe_callback (GIOChannel* source,
239 GIOCondition condition,
240 gpointer plugin_data);
241// Callback used to monitor output pipe status.
242static gboolean plugin_out_pipe_callback (GIOChannel* source,
243 GIOCondition condition,
244 gpointer plugin_data);
245std::string plugin_parameters_string (int argc, char* argn[], char* argv[]);
246static void plugin_stop_appletviewer ();
247
248NPError get_cookie_info(const char* siteAddr, char** cookieString, uint32_t* len);
249NPError get_proxy_info(const char* siteAddr, char** proxy, uint32_t* len);
250void consume_message(gchar* message);
251static void appletviewer_monitor(GPid pid, gint status, gpointer data);
252void plugin_send_initialization_message(char* instance, gulong handle,
253 int width, int height,
254 char* url);
255/* Returns JVM options set in itw-settings */
256std::vector<std::string*>* get_jvm_args();
257
258// Global instance counter.
259// Mutex to protect plugin_instance_counter.
260static GMutex* plugin_instance_mutex = NULL;
261// A global variable for reporting GLib errors. This must be free'd
262// and set to NULL after each use.
263static GError* channel_error = NULL;
264
265static GHashTable* instance_to_id_map = g_hash_table_new(NULL, NULL);
266static GHashTable* id_to_instance_map = g_hash_table_new(NULL, NULL);
267static gint instance_counter = 1;
268static GPid appletviewer_pid = -1;
269static guint appletviewer_watch_id = -1;
270
271bool debug_initiated = false;
272int plugin_debug = getenv ("ICEDTEAPLUGIN_DEBUG") != NULL;
273bool plugin_debug_headers = false;
274bool plugin_debug_to_file = false ;
275bool plugin_debug_to_streams = true ;
276bool plugin_debug_to_system = false;
277bool plugin_debug_to_console = true;
278FILE * plugin_file_log;
279std::string plugin_file_log_name;
280
281int plugin_debug_suspend = (getenv("ICEDTEAPLUGIN_DEBUG") != NULL) &&
282 (strcmp(getenv("ICEDTEAPLUGIN_DEBUG"), "suspend") == 0);
283
284
285#ifdef LEGACY_GLIB
286// Returns key from first item stored in hashtable
287gboolean
288find_first_item_in_hash_table(gpointer key, gpointer value, gpointer user_data)
289{
290 user_data = key;
291 return (gboolean)TRUE;
292}
293
294int
295g_strcmp0(char *str1, char *str2)
296{
297 if (str1 != NULL)
298 return str2 != NULL ? strcmp(str1, str2) : 1;
299 else // str1 == NULL
300 return str2 != NULL ? 1 : 0;
301}
302
303
304#endif
305
306static std::string get_plugin_executable(){
307 std::string custom_jre;
308 bool custom_jre_defined = find_custom_jre(custom_jre);
309 if (custom_jre_defined) {
310 if (IcedTeaPluginUtilities::file_exists(custom_jre+"/bin/java")){
311 return custom_jre+"/bin/java";
312 } else {
313 PLUGIN_ERROR("Your custom jre (/bin/java check) %s is not valid. Please fix %s in your %s. In attempt to run using default one. \n", custom_jre.c_str(), custom_jre_key.c_str(), default_file_ITW_deploy_props_name.c_str());
314 }
315 }
316 return appletviewer_default_executable;
317}
318
319static std::string get_plugin_rt_jar(){
320 std::string custom_jre;
321 bool custom_jre_defined = find_custom_jre(custom_jre);
322 if (custom_jre_defined) {
323 if (IcedTeaPluginUtilities::file_exists(custom_jre+"/lib/rt.jar")){
324 return custom_jre+"/lib/rt.jar";
325 } else {
326 PLUGIN_ERROR("Your custom jre (/lib/rt.jar check) %s is not valid. Please fix %s in your %s. In attempt to run using default one. \n", custom_jre.c_str(), custom_jre_key.c_str(), default_file_ITW_deploy_props_name.c_str());
327 }
328 }
329 return appletviewer_default_rtjar;
330}
331
332static void cleanUpDir(){
333 //free data_directory descriptor
334 if (data_directory_descriptor != NULL) {
335 closedir(data_directory_descriptor);
336 }
337 //clean up pipes directory
338 PLUGIN_DEBUG ("Removing runtime directory %s \n", data_directory.c_str());
339 int removed = rmdir(data_directory.c_str());
340 if (removed != 0) {
341 PLUGIN_ERROR ("Failed to remove runtime directory %s, because of %s \n", data_directory.c_str(), strerror(errno));
342 } else {
343 PLUGIN_DEBUG ("Removed runtime directory %s \n", data_directory.c_str());
344 }
345 data_directory_descriptor = NULL;
346}
347/*
348 * Find first member in GHashTable* depending on version of glib
349 */
350gpointer getFirstInTableInstance(GHashTable* table)
351{
352 gpointer id, instance;
353 #ifndef LEGACY_GLIB
354 GHashTableIter iter;
355 g_hash_table_iter_init (&iter, table);
356 g_hash_table_iter_next (&iter, &instance, &id);
357 #else
358 g_hash_table_find(table, (GHRFunc)find_first_item_in_hash_table, &instance);
359 #endif
360 return instance;
361}
362
363// Functions prefixed by ITNP_ are instance functions. They are called
364// by the browser and operate on instances of ITNPPluginData.
365// Functions prefixed by plugin_ are static helper functions.
366// Functions prefixed by NP_ are factory functions. They are called
367// by the browser and provide functionality needed to create plugin
368// instances.
369
370// INSTANCE FUNCTIONS
371
372// Creates a new icedtea np plugin instance. This function creates a
373// ITNPPluginData* and stores it in instance->pdata. The following
374// ITNPPluginData fields are initialized: instance_id, in_pipe_name,
375// in_from_appletviewer, in_watch_source, out_pipe_name,
376// out_to_appletviewer, out_watch_source, appletviewer_mutex, owner,
377// appletviewer_alive. In addition two pipe files are created. All
378// of those fields must be properly destroyed, and the pipes deleted,
379// by ITNP_Destroy. If an error occurs during initialization then this
380// function will free anything that's been allocated so far, set
381// instance->pdata to NULL and return an error code.
382NPError
383ITNP_New (NPMIMEType pluginType, NPP instance, uint16_t mode,
384 int16_t argc, char* argn[], char* argv[],
385 NPSavedData* saved)
386{
387 PLUGIN_DEBUG("ITNP_New\n");
388
389 static NPObject *window_ptr;
390 NPIdentifier identifier;
391 NPVariant member_ptr;
392 browser_functions.getvalue(instance, NPNVWindowNPObject, &window_ptr);
393 identifier = browser_functions.getstringidentifier("document");
394 if (!browser_functions.hasproperty(instance, window_ptr, identifier))
395 {
396 PLUGIN_ERROR("%s not found!\n", "document");
397 }
398 browser_functions.getproperty(instance, window_ptr, identifier, &member_ptr);
399
400 PLUGIN_DEBUG("Got variant %p\n", &member_ptr);
401
402 if (!instance)
403 {
404 PLUGIN_ERROR ("Browser-provided instance pointer is NULL.\n");
405 return NPERR_INVALID_INSTANCE_ERROR;
406 }
407
408 // data
409 ITNPPluginData* data = plugin_data_new ();
410 if (data == NULL)
411 {
412 PLUGIN_ERROR ("Failed to allocate plugin data.\n");
413 return NPERR_OUT_OF_MEMORY_ERROR;
414 }
415
416 // start the jvm if needed
417 NPError startup_error = start_jvm_if_needed();
418 if (startup_error != NPERR_NO_ERROR) {
419 PLUGIN_ERROR ("Failed to start JVM\n");
420 return startup_error;
421 }
422
423 // Initialize data->instance_id.
424 //
425 // instance_id should be unique for this process so we use a
426 // combination of getpid and plugin_instance_counter.
427 //
428 // Critical region. Reference and increment plugin_instance_counter
429 // global.
430 g_mutex_lock (plugin_instance_mutex);
431
432 // data->instance_id
433 data->instance_id = g_strdup_printf ("%d",
434 instance_counter);
435
436 g_mutex_unlock (plugin_instance_mutex);
437
438 // data->appletviewer_mutex
439 data->appletviewer_mutex = g_mutex_new ();
440
441 g_mutex_lock (data->appletviewer_mutex);
442
443 std::string documentbase = plugin_get_documentbase (instance);
444 // Documentbase retrieval.
445 if (argc != 0)
446 {
447 // Send parameters to appletviewer.
448 std::string params_string = plugin_parameters_string(argc, argn, argv);
449
450 data->parameters_string = g_strdup_printf("tag %s %s", documentbase.c_str(), params_string.c_str());
451
452 data->is_applet_instance = true;
453 }
454 else
455 {
456 data->is_applet_instance = false;
457 }
458
459 g_mutex_unlock (data->appletviewer_mutex);
460
461 // If initialization succeeded entirely then we store the plugin
462 // data in the instance structure and return. Otherwise we free the
463 // data we've allocated so far and set instance->pdata to NULL.
464
465 // Set back-pointer to owner instance.
466 data->owner = instance;
467
468 // source of this instance
469 // don't use documentbase, it is cleared later
470 data->source = plugin_get_documentbase(instance);
471
472 instance->pdata = data;
473
474 // store an identifier for this plugin
475 PLUGIN_DEBUG("Mapping id %d and instance %p\n", instance_counter, instance);
476 g_hash_table_insert(instance_to_id_map, instance, GINT_TO_POINTER(instance_counter));
477 g_hash_table_insert(id_to_instance_map, GINT_TO_POINTER(instance_counter), instance);
478 instance_counter++;
479
480 PLUGIN_DEBUG ("ITNP_New return\n");
481
482 return NPERR_NO_ERROR;
483}
484
485// Starts the JVM if it is not already running
486NPError start_jvm_if_needed()
487{
488
489 // This is asynchronized function. It must
490 // have exclusivity when running.
491
492 GMutex *vm_start_mutex = g_mutex_new();
493 g_mutex_lock(vm_start_mutex);
494
495 PLUGIN_DEBUG("Checking JVM status...\n");
496
497 // If the jvm is already up, do nothing
498 if (jvm_up)
499 {
500 PLUGIN_DEBUG("JVM is up. Returning.\n");
501 return NPERR_NO_ERROR;
502 }
503
504 PLUGIN_DEBUG("No JVM is running. Attempting to start one...\n");
505
506 NPError np_error = NPERR_NO_ERROR;
507 ITNPPluginData* data = NULL;
508
509 // Create appletviewer-to-plugin pipe which we refer to as the input
510 // pipe.
511
512#ifdef __OS2__
513 if (socketpair (AF_LOCAL, SOCK_STREAM, 0, in_pipe) == -1)
514 {
515 PLUGIN_ERROR_TWO ("Failed to create input pipe", strerror (errno));
516 np_error = NPERR_GENERIC_ERROR;
517 goto cleanup_in_pipe;
518 }
519 PLUGIN_DEBUG ("ITNP_New: created input fifo: %d/%d\n", in_pipe [0], in_pipe [1]);
520#else
521 // in_pipe_name
522 in_pipe_name = g_strdup_printf ("%s/%d-icedteanp-appletviewer-to-plugin",
523 data_directory.c_str(), getpid());
524 if (!in_pipe_name)
525 {
526 PLUGIN_ERROR ("Failed to create input pipe name.\n");
527 np_error = NPERR_OUT_OF_MEMORY_ERROR;
528 // If in_pipe_name is NULL then the g_free at
529 // cleanup_in_pipe_name will simply return.
530 goto cleanup_in_pipe_name;
531 }
532
533 // clean up any older pip
534 unlink (in_pipe_name);
535
536 PLUGIN_DEBUG ("ITNP_New: creating input fifo: %s\n", in_pipe_name);
537 if (mkfifo (in_pipe_name, 0600) == -1 && errno != EEXIST)
538 {
539 PLUGIN_ERROR ("Failed to create input pipe\n", strerror (errno));
540 np_error = NPERR_GENERIC_ERROR;
541 goto cleanup_in_pipe_name;
542 }
543 PLUGIN_DEBUG ("ITNP_New: created input fifo: %s\n", in_pipe_name);
544#endif
545
546 // Create plugin-to-appletviewer pipe which we refer to as the
547 // output pipe.
548
549#ifdef __OS2__
550 if (socketpair (AF_LOCAL, SOCK_STREAM, 0, out_pipe) == -1)
551 {
552 PLUGIN_ERROR_TWO ("Failed to create output pipe", strerror (errno));
553 np_error = NPERR_GENERIC_ERROR;
554 goto cleanup_out_pipe;
555 }
556 PLUGIN_DEBUG ("ITNP_New: created output fifo: %d/%d\n", out_pipe [0], out_pipe [1]);
557#else
558 // out_pipe_name
559 out_pipe_name = g_strdup_printf ("%s/%d-icedteanp-plugin-to-appletviewer",
560 data_directory.c_str(), getpid());
561
562 if (!out_pipe_name)
563 {
564 PLUGIN_ERROR ("Failed to create output pipe name.\n");
565 np_error = NPERR_OUT_OF_MEMORY_ERROR;
566 goto cleanup_out_pipe_name;
567 }
568
569 // clean up any older pip
570 unlink (out_pipe_name);
571
572 PLUGIN_DEBUG ("ITNP_New: creating output fifo: %s\n", out_pipe_name);
573 if (mkfifo (out_pipe_name, 0600) == -1 && errno != EEXIST)
574 {
575 PLUGIN_ERROR ("Failed to create output pipe\n", strerror (errno));
576 np_error = NPERR_GENERIC_ERROR;
577 goto cleanup_out_pipe_name;
578 }
579 PLUGIN_DEBUG ("ITNP_New: created output fifo: %s\n", out_pipe_name);
580#endif
581
582 // Create plugin-debug-to-appletviewer pipe which we refer to as the
583 // debug pipe.
584 initialize_debug();//should be already initialized, but...
585 if (plugin_debug_to_console){
586 // debug_pipe_name
587 debug_pipe_name = g_strdup_printf ("%s/%d-icedteanp-plugin-debug-to-appletviewer",
588 data_directory.c_str(), getpid());
589
590 if (!debug_pipe_name)
591 {
592 PLUGIN_ERROR ("Failed to create debug pipe name.\n");
593 np_error = NPERR_OUT_OF_MEMORY_ERROR;
594 goto cleanup_debug_pipe_name;
595 }
596
597 // clean up any older pip
598 unlink (debug_pipe_name);
599
600 PLUGIN_DEBUG ("ITNP_New: creating debug fifo: %s\n", debug_pipe_name);
601 if (mkfifo (debug_pipe_name, 0600) == -1 && errno != EEXIST)
602 {
603 PLUGIN_ERROR ("Failed to create debug pipe\n", strerror (errno));
604 np_error = NPERR_GENERIC_ERROR;
605 goto cleanup_debug_pipe_name;
606 }
607 PLUGIN_DEBUG ("ITNP_New: created debug fifo: %s\n", debug_pipe_name);
608 }
609
610 // Start a separate appletviewer process for each applet, even if
611 // there are multiple applets in the same page. We may need to
612 // change this behaviour if we find pages with multiple applets that
613 // rely on being run in the same VM.
614
615#ifdef __OS2__
616 // make sure parent ends are not inherited by the child (otherwise read() on
617 // the child's side will not be aborted when the parent closes its end)
618 fcntl (in_pipe [0], F_SETFD, FD_CLOEXEC);
619 fcntl (out_pipe [0], F_SETFD, FD_CLOEXEC);
620#endif
621
622 np_error = plugin_start_appletviewer (data);
623
624#ifdef __OS2__
625 // close child ends of the pipes (not needed)
626 CLOSE_FD (in_pipe [1]);
627 CLOSE_FD (out_pipe [1]);
628#endif
629
630 // Create plugin-to-appletviewer channel. The default encoding for
631 // the file is UTF-8.
632 // out_to_appletviewer
633#ifdef __OS2__
634 out_to_appletviewer = g_io_channel_unix_new (out_pipe [0]);
635#else
636 out_to_appletviewer = g_io_channel_new_file (out_pipe_name,
637 "w", &channel_error);
638#endif
639 if (!out_to_appletviewer)
640 {
641 if (channel_error)
642 {
643 PLUGIN_ERROR ("Failed to create output channel, '%s'\n",
644 channel_error->message);
645 g_error_free (channel_error);
646 channel_error = NULL;
647 }
648 else
649 PLUGIN_ERROR ("Failed to create output channel\n");
650
651 np_error = NPERR_GENERIC_ERROR;
652 goto cleanup_out_to_appletviewer;
653 }
654
655 // Watch for hangup and error signals on the output pipe.
656 out_watch_source =
657 g_io_add_watch (out_to_appletviewer,
658 (GIOCondition) (G_IO_ERR | G_IO_HUP),
659 plugin_out_pipe_callback, (gpointer) out_to_appletviewer);
660
661 // Create appletviewer-to-plugin channel. The default encoding for
662 // the file is UTF-8.
663 // in_from_appletviewer
664#ifdef __OS2__
665 in_from_appletviewer = g_io_channel_unix_new (in_pipe [0]);
666#else
667 in_from_appletviewer = g_io_channel_new_file (in_pipe_name,
668 "r", &channel_error);
669#endif
670 if (!in_from_appletviewer)
671 {
672 if (channel_error)
673 {
674 PLUGIN_ERROR ("Failed to create input channel, '%s'\n",
675 channel_error->message);
676 g_error_free (channel_error);
677 channel_error = NULL;
678 }
679 else
680 PLUGIN_ERROR ("Failed to create input channel\n");
681
682 np_error = NPERR_GENERIC_ERROR;
683 goto cleanup_in_from_appletviewer;
684 }
685
686 // Watch for hangup and error signals on the input pipe.
687 in_watch_source =
688 g_io_add_watch (in_from_appletviewer,
689 (GIOCondition) (G_IO_IN | G_IO_ERR | G_IO_HUP),
690 plugin_in_pipe_callback, (gpointer) in_from_appletviewer);
691
692 // Create plugin-to-appletviewer console debug channel. The default encoding for
693 // the file is UTF-8.
694 // debug_to_appletviewer
695 if (plugin_debug_to_console){
696 debug_to_appletviewer = g_io_channel_new_file (debug_pipe_name,
697 "w", &channel_error);
698 if (!debug_to_appletviewer)
699 {
700 if (channel_error)
701 {
702 PLUGIN_ERROR ("Failed to debug output channel, '%s'\n",
703 channel_error->message);
704 g_error_free (channel_error);
705 channel_error = NULL;
706 }
707 else
708 PLUGIN_ERROR ("Failed to create debug channel\n");
709
710 np_error = NPERR_GENERIC_ERROR;
711 goto cleanup_debug_to_appletviewer;
712 }
713 }
714
715 jvm_up = TRUE;
716
717 if (plugin_debug_to_console){
718 //jvm is up, we can start console producer thread
719 pthread_t debug_to_console_consumer;
720 pthread_create(&debug_to_console_consumer,NULL,&flush_pre_init_messages,NULL);
721 }
722 goto done;
723
724 // Free allocated data in case of error
725 cleanup_debug_to_appletviewer:
726 if (plugin_debug_to_console){
727 if (debug_to_appletviewer)
728 g_io_channel_unref (debug_to_appletviewer);
729 debug_to_appletviewer = NULL;
730 }
731
732 cleanup_in_watch_source:
733 // Removing a source is harmless if it fails since it just means the
734 // source has already been removed.
735 g_source_remove (in_watch_source);
736 in_watch_source = 0;
737
738 cleanup_in_from_appletviewer:
739 if (in_from_appletviewer)
740 g_io_channel_unref (in_from_appletviewer);
741 in_from_appletviewer = NULL;
742
743 // cleanup_out_watch_source:
744 g_source_remove (out_watch_source);
745 out_watch_source = 0;
746
747 cleanup_out_to_appletviewer:
748 if (out_to_appletviewer)
749 g_io_channel_unref (out_to_appletviewer);
750 out_to_appletviewer = NULL;
751
752 if (plugin_debug_to_console){
753 // cleanup_debug_pipe:
754 // Delete output pipe.
755 PLUGIN_DEBUG ("ITNP_New: deleting debug fifo: %s\n", debug_pipe_name);
756 unlink (debug_pipe_name);
757 PLUGIN_DEBUG ("ITNP_New: deleted debug fifo: %s\n", debug_pipe_name);
758 }
759 cleanup_debug_pipe_name:
760 if (plugin_debug_to_console){
761 g_free (debug_pipe_name);
762 debug_pipe_name = NULL;
763 }
764
765
766
767 // cleanup_out_pipe:
768#ifdef __OS2__
769 cleanup_out_pipe:
770 CLOSE_FD (out_pipe [0]);
771 CLOSE_FD (out_pipe [1]);
772#else
773 // Delete output pipe.
774 PLUGIN_DEBUG ("ITNP_New: deleting output fifo: %s\n", out_pipe_name);
775 unlink (out_pipe_name);
776 PLUGIN_DEBUG ("ITNP_New: deleted output fifo: %s\n", out_pipe_name);
777
778 cleanup_out_pipe_name:
779 g_free (out_pipe_name);
780 out_pipe_name = NULL;
781#endif
782
783 // cleanup_in_pipe:
784#ifdef __OS2__
785 cleanup_in_pipe:
786 CLOSE_FD (in_pipe [0]);
787 CLOSE_FD (in_pipe [1]);
788#else
789 // Delete input pipe.
790 PLUGIN_DEBUG ("ITNP_New: deleting input fifo: %s\n", in_pipe_name);
791 unlink (in_pipe_name);
792 PLUGIN_DEBUG ("ITNP_New: deleted input fifo: %s\n", in_pipe_name);
793
794 cleanup_in_pipe_name:
795 g_free (in_pipe_name);
796 in_pipe_name = NULL;
797#endif
798
799 cleanUpDir();
800 done:
801
802 IcedTeaPluginUtilities::printDebugStatus();
803 // Now other threads may re-enter.. unlock the mutex
804 g_mutex_unlock(vm_start_mutex);
805 return np_error;
806
807}
808
809NPError
810ITNP_GetValue (NPP instance, NPPVariable variable, void* value)
811{
812 PLUGIN_DEBUG ("ITNP_GetValue\n");
813
814 NPError np_error = NPERR_NO_ERROR;
815
816 switch (variable)
817 {
818 // This plugin needs XEmbed support.
819 case NPPVpluginNeedsXEmbed:
820 {
821 PLUGIN_DEBUG ("ITNP_GetValue: returning TRUE for NeedsXEmbed.\n");
822 bool* bool_value = (bool*) value;
823 *bool_value = true;
824 }
825 break;
826 case NPPVpluginScriptableNPObject:
827 {
828 *(NPObject **)value = get_scriptable_object(instance);
829 }
830 break;
831 default:
832 PLUGIN_ERROR ("Unknown plugin value requested.\n");
833 np_error = NPERR_GENERIC_ERROR;
834 break;
835 }
836
837 PLUGIN_DEBUG ("ITNP_GetValue return\n");
838
839 return np_error;
840}
841
842NPError
843ITNP_Destroy (NPP instance, NPSavedData** save)
844{
845 PLUGIN_DEBUG ("ITNP_Destroy %p\n", instance);
846
847 ITNPPluginData* data = (ITNPPluginData*) instance->pdata;
848
849 int id = get_id_from_instance(instance);
850
851 // Let Java know that this applet needs to be destroyed
852 gchar* msg = (gchar*) g_malloc(512*sizeof(gchar)); // 512 is more than enough. We need < 100
853 g_sprintf(msg, "instance %d destroy", id);
854 plugin_send_message_to_appletviewer(msg);
855 g_free(msg);
856 msg = NULL;
857
858 if (data)
859 {
860 // Free plugin data.
861 plugin_data_destroy (instance);
862 }
863
864 g_hash_table_remove(instance_to_id_map, instance);
865 g_hash_table_remove(id_to_instance_map, GINT_TO_POINTER(id));
866
867 IcedTeaPluginUtilities::invalidateInstance(instance);
868
869 PLUGIN_DEBUG ("ITNP_Destroy return\n");
870
871 return NPERR_NO_ERROR;
872}
873
874NPError
875ITNP_SetWindow (NPP instance, NPWindow* window)
876{
877 PLUGIN_DEBUG ("ITNP_SetWindow\n");
878
879 if (instance == NULL)
880 {
881 PLUGIN_ERROR ("Invalid instance.\n");
882
883 return NPERR_INVALID_INSTANCE_ERROR;
884 }
885
886 gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance);
887
888 gint id = 0;
889 if (id_ptr)
890 {
891 id = GPOINTER_TO_INT(id_ptr);
892 }
893
894 ITNPPluginData* data = (ITNPPluginData*) instance->pdata;
895
896 // Simply return if we receive a NULL window.
897 if ((window == NULL) || (window->window == NULL))
898 {
899 PLUGIN_DEBUG ("ITNP_SetWindow: got NULL window.\n");
900
901 return NPERR_NO_ERROR;
902 }
903
904#ifdef __OS2__
905 void *wnd = wrap_window_handle (window->window);
906 if (!wnd)
907 return NPERR_GENERIC_ERROR;
908
909 PLUGIN_DEBUG ("ITNP_SetWindow: wrapped window handle %d (%x) in %d (%x)\n",
910 window->window, window->window, wnd, wnd);
911#endif
912
913 if (data->window_handle)
914 {
915 // The window already exists.
916 if (data->window_handle == wnd)
917 {
918 // The parent window is the same as in previous calls.
919 PLUGIN_DEBUG ("ITNP_SetWindow: window already exists.\n");
920
921 // Critical region. Read data->appletviewer_mutex and send
922 // a message to the appletviewer.
923 g_mutex_lock (data->appletviewer_mutex);
924
925 if (jvm_up)
926 {
927 gboolean dim_changed = FALSE;
928
929 // The window is the same as it was for the last
930 // SetWindow call.
931 if (window->width != data->window_width)
932 {
933 PLUGIN_DEBUG ("ITNP_SetWindow: window width changed.\n");
934 // The width of the plugin window has changed.
935
936 // Store the new width.
937 data->window_width = window->width;
938 dim_changed = TRUE;
939 }
940
941 if (window->height != data->window_height)
942 {
943 PLUGIN_DEBUG ("ITNP_SetWindow: window height changed.\n");
944 // The height of the plugin window has changed.
945
946 // Store the new height.
947 data->window_height = window->height;
948
949 dim_changed = TRUE;
950 }
951
952 if (dim_changed) {
953 gchar* message = g_strdup_printf ("instance %d width %d height %d",
954 id, window->width, window->height);
955 plugin_send_message_to_appletviewer (message);
956 g_free (message);
957 message = NULL;
958 }
959
960
961 }
962 else
963 {
964 // The appletviewer is not running.
965 PLUGIN_DEBUG ("ITNP_SetWindow: appletviewer is not running.\n");
966 }
967
968 g_mutex_unlock (data->appletviewer_mutex);
969 }
970 else
971 {
972 // The parent window has changed. This branch does run but
973 // doing nothing in response seems to be sufficient.
974 PLUGIN_DEBUG ("ITNP_SetWindow: parent window changed.\n");
975 }
976 }
977 else
978 {
979
980 // Else this is initialization
981 PLUGIN_DEBUG ("ITNP_SetWindow: setting window.\n");
982
983 // Critical region. Send messages to appletviewer.
984 g_mutex_lock (data->appletviewer_mutex);
985
986 // Store the window handle and dimensions
987 data->window_handle = wnd;
988 data->window_width = window->width;
989 data->window_height = window->height;
990
991 // Now we have everything. Send this data to the Java side
992 plugin_send_initialization_message(
993 data->instance_id, (gulong) data->window_handle,
994 data->window_width, data->window_height, data->parameters_string);
995
996 g_mutex_unlock (data->appletviewer_mutex);
997
998 }
999
1000 PLUGIN_DEBUG ("ITNP_SetWindow return\n");
1001
1002 return NPERR_NO_ERROR;
1003}
1004
1005NPError
1006ITNP_NewStream (NPP instance, NPMIMEType type, NPStream* stream,
1007 NPBool seekable, uint16_t* stype)
1008{
1009 PLUGIN_DEBUG ("ITNP_NewStream\n");
1010
1011 PLUGIN_DEBUG ("ITNP_NewStream return\n");
1012
1013 return NPERR_GENERIC_ERROR;
1014}
1015
1016void
1017ITNP_StreamAsFile (NPP instance, NPStream* stream, const char* filename)
1018{
1019 PLUGIN_DEBUG ("ITNP_StreamAsFile\n");
1020
1021 PLUGIN_DEBUG ("ITNP_StreamAsFile return\n");
1022}
1023
1024NPError
1025ITNP_DestroyStream (NPP instance, NPStream* stream, NPReason reason)
1026{
1027 PLUGIN_DEBUG ("ITNP_DestroyStream\n");
1028
1029 PLUGIN_DEBUG ("ITNP_DestroyStream return\n");
1030
1031 return NPERR_NO_ERROR;
1032}
1033
1034int32_t
1035ITNP_WriteReady (NPP instance, NPStream* stream)
1036{
1037 PLUGIN_DEBUG ("ITNP_WriteReady\n");
1038
1039 PLUGIN_DEBUG ("ITNP_WriteReady return\n");
1040
1041 return 0;
1042}
1043
1044int32_t
1045ITNP_Write (NPP instance, NPStream* stream, int32_t offset, int32_t len,
1046 void* buffer)
1047{
1048 PLUGIN_DEBUG ("ITNP_Write\n");
1049
1050 PLUGIN_DEBUG ("ITNP_Write return\n");
1051
1052 return 0;
1053}
1054
1055void
1056ITNP_Print (NPP instance, NPPrint* platformPrint)
1057{
1058 PLUGIN_DEBUG ("ITNP_Print\n");
1059
1060 PLUGIN_DEBUG ("ITNP_Print return\n");
1061}
1062
1063int16_t
1064ITNP_HandleEvent (NPP instance, void* event)
1065{
1066 PLUGIN_DEBUG ("ITNP_HandleEvent\n");
1067
1068 PLUGIN_DEBUG ("ITNP_HandleEvent return\n");
1069
1070 return 0;
1071}
1072
1073void
1074ITNP_URLNotify (NPP instance, const char* url, NPReason reason,
1075 void* notifyData)
1076{
1077 PLUGIN_DEBUG ("ITNP_URLNotify\n");
1078
1079 PLUGIN_DEBUG ("ITNP_URLNotify return\n");
1080}
1081
1082NPError
1083get_cookie_info(const char* siteAddr, char** cookieString, uint32_t* len)
1084{
1085 // Only attempt to perform this operation if there is a valid plugin instance
1086 if (g_hash_table_size(instance_to_id_map) <= 0)
1087 {
1088 return NPERR_GENERIC_ERROR;
1089 }
1090 // getvalueforurl needs an NPP instance. Quite frankly, there is no easy way
1091 // to know which instance needs the information, as applets on Java side can
1092 // be multi-threaded and the thread making a proxy.cookie request cannot be
1093 // easily tracked.
1094
1095 // Fortunately, XULRunner does not care about the instance as long as it is
1096 // valid. So we just pick the first valid one and use it. Proxy/Cookie
1097 // information is not instance specific anyway, it is URL specific.
1098
1099 if (browser_functions.getvalueforurl)
1100 {
1101 gpointer instance=getFirstInTableInstance(instance_to_id_map);
1102 return browser_functions.getvalueforurl((NPP) instance, NPNURLVCookie, siteAddr, cookieString, len);
1103 } else
1104 {
1105 return NPERR_GENERIC_ERROR;
1106 }
1107
1108 return NPERR_NO_ERROR;
1109}
1110
1111static NPError
1112set_cookie_info(const char* siteAddr, const char* cookieString, uint32_t len)
1113{
1114 // Only attempt to perform this operation if there is a valid plugin instance
1115 if (g_hash_table_size(instance_to_id_map) > 0 && browser_functions.getvalueforurl)
1116 {
1117 // We arbitrarily use the first valid instance we can grab
1118 // For an explanation of the logic behind this, see get_cookie_info
1119 gpointer instance = getFirstInTableInstance(instance_to_id_map);
1120 return browser_functions.setvalueforurl((NPP) instance, NPNURLVCookie, siteAddr, cookieString, len);
1121 }
1122
1123 return NPERR_GENERIC_ERROR;;
1124}
1125
1126// HELPER FUNCTIONS
1127
1128ITNPPluginData*
1129plugin_data_new ()
1130{
1131 PLUGIN_DEBUG ("plugin_data_new\n");
1132
1133 ITNPPluginData* data = (ITNPPluginData*)browser_functions.memalloc(sizeof (struct ITNPPluginData));
1134
1135 if (data)
1136 {
1137 // Call constructor on allocated data
1138 new (data) ITNPPluginData();
1139 }
1140 PLUGIN_DEBUG ("plugin_data_new return\n");
1141
1142 return data;
1143}
1144
1145
1146
1147// Documentbase retrieval. This function gets the current document's
1148// documentbase. This function relies on browser-private data so it
1149// will only work when the plugin is loaded in a Mozilla-based
1150// browser.
1151static std::string
1152plugin_get_documentbase (NPP instance)
1153{
1154 PLUGIN_DEBUG ("plugin_get_documentbase\n");
1155
1156 // FIXME: This method is not ideal, but there are no known NPAPI call
1157 // for this. See thread for more information:
1158 // http://www.mail-archive.com/chromium-dev@googlegroups.com/msg04844.html
1159
1160 // Additionally, since it is insecure, we cannot use it for making
1161 // security decisions.
1162 NPObject* window;
1163 browser_functions.getvalue(instance, NPNVWindowNPObject, &window);
1164
1165 NPVariant location;
1166 NPIdentifier location_id = browser_functions.getstringidentifier("location");
1167 browser_functions.getproperty(instance, window, location_id, &location);
1168
1169 NPVariant href;
1170 NPIdentifier href_id = browser_functions.getstringidentifier("href");
1171 browser_functions.getproperty(instance, NPVARIANT_TO_OBJECT(location),
1172 href_id, &href);
1173
1174 std::string href_str = IcedTeaPluginUtilities::NPVariantAsString(href);
1175
1176 // Release references.
1177 browser_functions.releasevariantvalue(&href);
1178 browser_functions.releasevariantvalue(&location);
1179
1180 PLUGIN_DEBUG ("plugin_get_documentbase return\n");
1181 PLUGIN_DEBUG("plugin_get_documentbase returning: %s\n", href_str.c_str());
1182
1183 return href_str;
1184}
1185
1186// plugin_in_pipe_callback is called when data is available on the
1187// input pipe, or when the appletviewer crashes or is killed. It may
1188// be called after data has been destroyed in which case it simply
1189// returns FALSE to remove itself from the glib main loop.
1190static gboolean
1191plugin_in_pipe_callback (GIOChannel* source,
1192 GIOCondition condition,
1193 gpointer plugin_data)
1194{
1195 PLUGIN_DEBUG ("plugin_in_pipe_callback\n");
1196
1197 gboolean keep_installed = TRUE;
1198
1199 if (condition & G_IO_IN)
1200 {
1201 gchar* message = NULL;
1202
1203 if (g_io_channel_read_line (in_from_appletviewer,
1204 &message, NULL, NULL,
1205 &channel_error)
1206 != G_IO_STATUS_NORMAL)
1207 {
1208 if (channel_error)
1209 {
1210 PLUGIN_ERROR ("Failed to read line from input channel, %s\n",
1211 channel_error->message);
1212 g_error_free (channel_error);
1213 channel_error = NULL;
1214 }
1215 else
1216 PLUGIN_ERROR ("Failed to read line from input channel\n");
1217#ifdef __OS2__
1218 // G_IO_ERR/HUP is not reported on file/pipe handles, simulate it
1219 condition = (GIOCondition) (condition | G_IO_ERR);
1220#endif
1221 } else
1222 {
1223 consume_message(message);
1224 }
1225
1226 g_free (message);
1227 message = NULL;
1228
1229 keep_installed = TRUE;
1230 }
1231
1232 if (condition & (G_IO_ERR | G_IO_HUP))
1233 {
1234 PLUGIN_DEBUG ("appletviewer has stopped.\n");
1235 keep_installed = FALSE;
1236 }
1237
1238 PLUGIN_DEBUG ("plugin_in_pipe_callback return\n");
1239
1240 return keep_installed;
1241}
1242
1243static
1244void consume_plugin_message(gchar* message) {
1245 // internal plugin related message
1246 gchar** parts = g_strsplit (message, " ", 5);
1247 if (g_str_has_prefix(parts[1], "PluginProxyInfo"))
1248 {
1249 gchar* proxy = NULL;
1250 uint32_t len;
1251
1252 gchar* decoded_url = (gchar*) calloc(strlen(parts[4]) + 1, sizeof(gchar));
1253 IcedTeaPluginUtilities::decodeURL(parts[4], &decoded_url);
1254 PLUGIN_DEBUG("parts[0]=%s, parts[1]=%s, reference, parts[3]=%s, parts[4]=%s -- decoded_url=%s\n", parts[0], parts[1], parts[3], parts[4], decoded_url);
1255
1256 gchar* proxy_info;
1257
1258 proxy_info = g_strconcat ("plugin PluginProxyInfo reference ", parts[3], " ", NULL);
1259 if (get_proxy_info(decoded_url, &proxy, &len) == NPERR_NO_ERROR)
1260 {
1261 proxy_info = g_strconcat (proxy_info, proxy, NULL);
1262 }
1263
1264 PLUGIN_DEBUG("Proxy info: %s\n", proxy_info);
1265 plugin_send_message_to_appletviewer(proxy_info);
1266
1267 free(decoded_url);
1268 decoded_url = NULL;
1269 g_free(proxy_info);
1270 proxy_info = NULL;
1271
1272 g_free(proxy);
1273 proxy = NULL;
1274
1275 } else if (g_str_has_prefix(parts[1], "PluginCookieInfo"))
1276 {
1277 gchar* decoded_url = (gchar*) calloc(strlen(parts[4])+1, sizeof(gchar));
1278 IcedTeaPluginUtilities::decodeURL(parts[4], &decoded_url);
1279
1280 gchar* cookie_info = g_strconcat ("plugin PluginCookieInfo reference ", parts[3], " ", NULL);
1281 gchar* cookie_string = NULL;
1282 uint32_t len;
1283 if (get_cookie_info(decoded_url, &cookie_string, &len) == NPERR_NO_ERROR)
1284 {
1285 cookie_info = g_strconcat (cookie_info, cookie_string, NULL);
1286 }
1287
1288 PLUGIN_DEBUG("Cookie info: %s\n", cookie_info);
1289 plugin_send_message_to_appletviewer(cookie_info);
1290
1291 free(decoded_url);
1292 decoded_url = NULL;
1293 g_free(cookie_info);
1294 cookie_info = NULL;
1295 g_free(cookie_string);
1296 cookie_string = NULL;
1297 } else if (g_str_has_prefix(parts[1], "PluginSetCookie"))
1298 {
1299 // Message structure: plugin PluginSetCookie reference -1 <url> <cookie>
1300 gchar** cookie_parts = g_strsplit (message, " ", 6);
1301
1302 if (g_strv_length(cookie_parts) < 6)
1303 {
1304 g_strfreev (parts);
1305 g_strfreev (cookie_parts);
1306 return; // Defensive, message _should_ be properly formatted
1307 }
1308
1309 gchar* decoded_url = (gchar*) calloc(strlen(cookie_parts[4])+1, sizeof(gchar));
1310 IcedTeaPluginUtilities::decodeURL(cookie_parts[4], &decoded_url);
1311
1312 gchar* cookie_string = cookie_parts[5];
1313 uint32_t len = strlen(cookie_string);
1314 if (set_cookie_info(decoded_url, cookie_string, len) == NPERR_NO_ERROR)
1315 {
1316 PLUGIN_DEBUG("Setting cookie for URL %s to %s\n", decoded_url, cookie_string);
1317 } else
1318 {
1319 PLUGIN_DEBUG("Not able to set cookie for URL %s to %s\n", decoded_url, cookie_string);
1320 }
1321
1322 free(decoded_url);
1323 decoded_url = NULL;
1324 g_strfreev (cookie_parts);
1325 cookie_parts = NULL;
1326 }
1327
1328 g_strfreev (parts);
1329 parts = NULL;
1330}
1331
1332void consume_message(gchar* message) {
1333
1334 PLUGIN_DEBUG (" PIPE: plugin read: %s\n", message);
1335
1336 if (g_str_has_prefix (message, "instance"))
1337 {
1338
1339 ITNPPluginData* data;
1340 gchar** parts = g_strsplit (message, " ", -1);
1341 guint parts_sz = g_strv_length (parts);
1342
1343 int instance_id = atoi(parts[1]);
1344 NPP instance = (NPP) g_hash_table_lookup(id_to_instance_map,
1345 GINT_TO_POINTER(instance_id));
1346
1347 if (instance_id > 0 && !instance)
1348 {
1349 PLUGIN_DEBUG("Instance %d is not active. Refusing to consume message \"%s\"\n", instance_id, message);
1350 return;
1351 }
1352 else if (instance)
1353 {
1354 data = (ITNPPluginData*) instance->pdata;
1355 }
1356
1357 if (g_str_has_prefix (parts[2], "status"))
1358 {
1359
1360 // clear the "instance X status" parts
1361 strcpy(parts[0], "");
1362 strcpy(parts[1], "");
1363 strcpy(parts[2], "");
1364
1365 // join the rest
1366 gchar* status_message = g_strjoinv(" ", parts);
1367 PLUGIN_DEBUG ("plugin_in_pipe_callback: setting status %s\n", status_message);
1368 (*browser_functions.status) (data->owner, status_message);
1369
1370 g_free(status_message);
1371 status_message = NULL;
1372 }
1373 else if (g_str_has_prefix (parts[1], "internal"))
1374 {
1375 //s->post(message);
1376 }
1377 else
1378 {
1379 // All other messages are posted to the bus, and subscribers are
1380 // expected to take care of them. They better!
1381
1382 java_to_plugin_bus->post(message);
1383 }
1384
1385 g_strfreev (parts);
1386 parts = NULL;
1387 }
1388 else if (g_str_has_prefix (message, "context"))
1389 {
1390 java_to_plugin_bus->post(message);
1391 }
1392 else if (g_str_has_prefix (message, "plugin "))
1393 {
1394 consume_plugin_message(message);
1395 }
1396 else
1397 {
1398 g_print (" Unable to handle message: %s\n", message);
1399 }
1400
1401}
1402
1403void get_instance_from_id(int id, NPP& instance)
1404{
1405 instance = (NPP) g_hash_table_lookup(id_to_instance_map,
1406 GINT_TO_POINTER(id));
1407}
1408
1409int get_id_from_instance(NPP instance)
1410{
1411 int id = GPOINTER_TO_INT(g_hash_table_lookup(instance_to_id_map,
1412 instance));
1413 PLUGIN_DEBUG("Returning id %d for instance %p\n", id, instance);
1414 return id;
1415}
1416
1417NPError
1418get_proxy_info(const char* siteAddr, char** proxy, uint32_t* len)
1419{
1420 // Only attempt to perform this operation if there is a valid plugin instance
1421 if (g_hash_table_size(instance_to_id_map) <= 0)
1422 {
1423 return NPERR_GENERIC_ERROR;
1424 }
1425 if (browser_functions.getvalueforurl)
1426 {
1427
1428 // As in get_cookie_info, we use the first active instance
1429 gpointer instance=getFirstInTableInstance(instance_to_id_map);
1430 browser_functions.getvalueforurl((NPP) instance, NPNURLVProxy, siteAddr, proxy, len);
1431 } else
1432 {
1433 return NPERR_GENERIC_ERROR;
1434 }
1435
1436 return NPERR_NO_ERROR;
1437}
1438
1439// plugin_out_pipe_callback is called when the appletviewer crashes or
1440// is killed. It may be called after data has been destroyed in which
1441// case it simply returns FALSE to remove itself from the glib main
1442// loop.
1443static gboolean
1444plugin_out_pipe_callback (GIOChannel* source,
1445 GIOCondition condition,
1446 gpointer plugin_data)
1447{
1448 PLUGIN_DEBUG ("plugin_out_pipe_callback\n");
1449
1450 ITNPPluginData* data = (ITNPPluginData*) plugin_data;
1451
1452 PLUGIN_DEBUG ("plugin_out_pipe_callback: appletviewer has stopped.\n");
1453
1454 PLUGIN_DEBUG ("plugin_out_pipe_callback return\n");
1455
1456 return FALSE;
1457}
1458
1459// remove all components from LD_LIBRARY_PATH, which start with
1460// MOZILLA_FIVE_HOME; firefox has its own NSS based security provider,
1461// which conflicts with the one configured in nss.cfg.
1462static gchar*
1463plugin_filter_ld_library_path(gchar *path_old)
1464{
1465 gchar *moz_home = g_strdup (g_getenv ("MOZILLA_FIVE_HOME"));
1466 gchar *moz_prefix;
1467 gchar *path_new;
1468 gchar** components;
1469 int i1, i2;
1470
1471 if (moz_home == NULL || path_old == NULL || strlen (path_old) == 0)
1472 return path_old;
1473 if (g_str_has_suffix (moz_home, "/"))
1474 moz_home[strlen (moz_home - 1)] = '\0';
1475 moz_prefix = g_strconcat (moz_home, "/", NULL);
1476
1477 components = g_strsplit (path_old, ":", -1);
1478 for (i1 = 0, i2 = 0; components[i1] != NULL; i1++)
1479 {
1480 if (g_strcmp0 (components[i1], moz_home) == 0
1481 || g_str_has_prefix (components[i1], moz_home))
1482 components[i2] = components[i1];
1483 else
1484 components[i2++] = components[i1];
1485 }
1486 components[i2] = NULL;
1487
1488 if (i1 > i2)
1489 path_new = g_strjoinv (":", components);
1490 g_strfreev (components);
1491 g_free (moz_home);
1492 g_free (moz_prefix);
1493 g_free (path_old);
1494
1495 if (path_new == NULL || strlen (path_new) == 0)
1496 {
1497 PLUGIN_DEBUG("Unset LD_LIBRARY_PATH\n");
1498 return NULL;
1499 }
1500 else
1501 {
1502 PLUGIN_DEBUG ("Set LD_LIBRARY_PATH: %s\n", path_new);
1503 return path_new;
1504 }
1505}
1506
1507// build the environment to pass to the external plugin process
1508static gchar**
1509plugin_filter_environment(void)
1510{
1511 gchar **var_names = g_listenv();
1512 gchar **new_env = (gchar**) malloc(sizeof(gchar*) * (g_strv_length (var_names) + 1));
1513 int i_var, i_env;
1514
1515 for (i_var = 0, i_env = 0; var_names[i_var] != NULL; i_var++)
1516 {
1517 gchar *env_value = g_strdup (g_getenv (var_names[i_var]));
1518
1519 if (g_str_has_prefix (var_names[i_var], "LD_LIBRARY_PATH"))
1520 env_value = plugin_filter_ld_library_path (env_value);
1521 if (env_value != NULL)
1522 {
1523 new_env[i_env++] = g_strdup_printf ("%s=%s", var_names[i_var], env_value);
1524 g_free (env_value);
1525 }
1526 }
1527 new_env[i_env] = NULL;
1528 return new_env;
1529}
1530
1531static NPError
1532plugin_test_appletviewer ()
1533{
1534
1535 PLUGIN_DEBUG ("plugin_test_appletviewer: %s\n", get_plugin_executable().c_str());
1536 NPError error = NPERR_NO_ERROR;
1537
1538 gchar* command_line[3] = { NULL, NULL, NULL };
1539 gchar** environment;
1540
1541 command_line[0] = g_strdup (get_plugin_executable().c_str());
1542 command_line[1] = g_strdup("-version");
1543 command_line[2] = NULL;
1544
1545 environment = plugin_filter_environment();
1546
1547 if (!g_spawn_async (NULL, command_line, environment,
1548 (GSpawnFlags) 0,
1549 NULL, NULL, NULL, &channel_error))
1550 {
1551 if (channel_error)
1552 {
1553 PLUGIN_ERROR ("Failed to spawn applet viewer %s\n",
1554 channel_error->message);
1555 g_error_free (channel_error);
1556 channel_error = NULL;
1557 }
1558 else
1559 PLUGIN_ERROR ("Failed to spawn applet viewer\n");
1560 error = NPERR_GENERIC_ERROR;
1561 }
1562
1563 g_strfreev (environment);
1564
1565 g_free (command_line[0]);
1566 command_line[0] = NULL;
1567 g_free (command_line[1]);
1568 command_line[1] = NULL;
1569 g_free (command_line[2]);
1570 command_line[2] = NULL;
1571
1572 PLUGIN_DEBUG ("plugin_test_appletviewer return\n");
1573 return error;
1574}
1575
1576NPError
1577plugin_start_appletviewer (ITNPPluginData* data)
1578{
1579 PLUGIN_DEBUG ("plugin_start_appletviewer\n");
1580 NPError error = NPERR_NO_ERROR;
1581
1582 std::vector<std::string> command_line;
1583 gchar** environment = NULL;
1584 std::vector<std::string*>* jvm_args = get_jvm_args();
1585
1586 // Construct command line parameters
1587
1588 command_line.push_back(get_plugin_executable());
1589
1590 //Add JVM args to command_line
1591 for (int i = 0; i < jvm_args->size(); i++)
1592 {
1593 command_line.push_back(*jvm_args->at(i));
1594 }
1595
1596 command_line.push_back(PLUGIN_BOOTCLASSPATH);
1597 // set the classpath to avoid using the default (cwd).
1598 command_line.push_back("-classpath");
1599 command_line.push_back(get_plugin_rt_jar());
1600
1601 // Enable coverage agent if we are running instrumented plugin
1602#ifdef COVERAGE_AGENT
1603 command_line.push_back(COVERAGE_AGENT);
1604#endif
1605
1606 if (plugin_debug)
1607 {
1608 command_line.push_back("-Xdebug");
1609 command_line.push_back("-Xnoagent");
1610
1611 //Debug flags
1612 std::string debug_flags = "-Xrunjdwp:transport="DT_SOCKET_DLL",address=8787,server=y,";
1613 debug_flags += plugin_debug_suspend ? "suspend=y" : "suspend=n";
1614 command_line.push_back(debug_flags);
1615 }
1616
1617 command_line.push_back("sun.applet.PluginMain");
1618#ifdef __OS2__
1619 command_line.push_back((std::ostrstream() << out_pipe[1]).str());
1620 command_line.push_back((std::ostrstream() << in_pipe[1]).str());
1621#else
1622 command_line.push_back(out_pipe_name);
1623 command_line.push_back(in_pipe_name);
1624#endif
1625 if (plugin_debug_to_console){
1626 command_line.push_back(debug_pipe_name);
1627 }
1628
1629 // Finished command line parameters
1630
1631 environment = plugin_filter_environment();
1632 std::vector<gchar*> vector_gchar = IcedTeaPluginUtilities::vectorStringToVectorGchar(&command_line);
1633 gchar **command_line_args = &vector_gchar[0];
1634
1635 if (!g_spawn_async (NULL, command_line_args, environment,
1636#ifdef __OS2__
1637 (GSpawnFlags) (G_SPAWN_LEAVE_DESCRIPTORS_OPEN | G_SPAWN_DO_NOT_REAP_CHILD),
1638#else
1639 (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD,
1640#endif
1641 NULL, NULL, &appletviewer_pid, &channel_error))
1642 {
1643 if (channel_error)
1644 {
1645 PLUGIN_ERROR ("Failed to spawn applet viewer %s\n",
1646 channel_error->message);
1647 g_error_free (channel_error);
1648 channel_error = NULL;
1649 }
1650 else
1651 PLUGIN_ERROR ("Failed to spawn applet viewer\n");
1652 error = NPERR_GENERIC_ERROR;
1653 }
1654
1655 // make sure we get an error rather than crash the browser when talking to the
1656 // jvm process if it dies unexpectedly (we do it here since g_spawn() is known
1657 // to reset SIGPIPE handler to SIG_DFL)
1658 signal (SIGPIPE, SIG_IGN);
1659
1660 //Free memory
1661 g_strfreev(environment);
1662 IcedTeaPluginUtilities::freeStringPtrVector(jvm_args);
1663 jvm_args = NULL;
1664 command_line_args = NULL;
1665
1666 if (appletviewer_pid)
1667 {
1668 PLUGIN_DEBUG("Initialized VM with pid=%d\n", appletviewer_pid);
1669 appletviewer_watch_id = g_child_watch_add(appletviewer_pid, (GChildWatchFunc) appletviewer_monitor, (gpointer) appletviewer_pid);
1670 }
1671
1672
1673 PLUGIN_DEBUG ("plugin_start_appletviewer return\n");
1674 return error;
1675}
1676
1677/*
1678 * Returns JVM options set in itw-settings
1679 */
1680std::vector<std::string*>*
1681get_jvm_args()
1682{
1683 std::string output;
1684 std::vector<std::string*>* tokenOutput = NULL;
1685 bool args_defined = read_deploy_property_value("deployment.plugin.jvm.arguments", output);
1686 if (!args_defined){
1687 return new std::vector<std::string*>();
1688 }
1689 tokenOutput = IcedTeaPluginUtilities::strSplit(output.c_str(), " \n");
1690 return tokenOutput;
1691}
1692
1693
1694/*
1695 * Escape characters for passing to Java.
1696 * "\n" for new line, "\\" for "\", "\:" for ";"
1697 */
1698std::string
1699escape_parameter_string(const char* to_encode) {
1700 std::string encoded;
1701
1702 if (to_encode == NULL)
1703 {
1704 return encoded;
1705 }
1706
1707 size_t length = strlen(to_encode);
1708 for (int i = 0; i < length; i++)
1709 {
1710 if (to_encode[i] == '\n')
1711 encoded += "\\n";
1712 else if (to_encode[i] == '\\')
1713 encoded += "\\\\";
1714 else if (to_encode[i] == ';')
1715 encoded += "\\:";
1716 else
1717 encoded += to_encode[i];
1718 }
1719
1720 return encoded;
1721}
1722
1723/*
1724 * Build a string containing an encoded list of parameters to send to the applet viewer.
1725 * The parameters are separated as 'key1;value1;key2;value2;'. As well, they are
1726 * separated and escaped as:
1727 * "\n" for new line, "\\" for "\", "\:" for ";"
1728 */
1729std::string
1730plugin_parameters_string (int argc, char* argn[], char* argv[])
1731{
1732 PLUGIN_DEBUG ("plugin_parameters_string\n");
1733
1734 std::string parameters;
1735
1736 for (int i = 0; i < argc; i++)
1737 {
1738 if (argv[i] != NULL)
1739 {
1740 std::string name_escaped = escape_parameter_string(argn[i]);
1741 std::string value_escaped = escape_parameter_string(argv[i]);
1742
1743 //Encode parameters and send as 'key1;value1;key2;value2;' etc
1744 parameters += name_escaped;
1745 parameters += ';';
1746 parameters += value_escaped;
1747 parameters += ';';
1748 }
1749 }
1750
1751 PLUGIN_DEBUG ("plugin_parameters_string return\n");
1752
1753 return parameters;
1754}
1755
1756// plugin_send_message_to_appletviewer must be called while holding
1757// data->appletviewer_mutex.
1758void
1759plugin_send_message_to_appletviewer (gchar const* message)
1760{
1761 PLUGIN_DEBUG ("plugin_send_message_to_appletviewer\n");
1762
1763 if (jvm_up)
1764 {
1765 gchar* newline_message = NULL;
1766 gsize bytes_written = 0;
1767
1768 // Send message to appletviewer.
1769 newline_message = g_strdup_printf ("%s\n", message);
1770
1771 // g_io_channel_write_chars will return something other than
1772 // G_IO_STATUS_NORMAL if not all the data is written. In that
1773 // case we fail rather than retrying.
1774 if (g_io_channel_write_chars (out_to_appletviewer,
1775 newline_message, -1, &bytes_written,
1776 &channel_error)
1777 != G_IO_STATUS_NORMAL)
1778 {
1779 if (channel_error)
1780 {
1781 PLUGIN_ERROR ("Failed to write bytes to output channel '%s' \n",
1782 channel_error->message);
1783 g_error_free (channel_error);
1784 channel_error = NULL;
1785 }
1786 else
1787 PLUGIN_ERROR ("Failed to write bytes to output channel for %s", newline_message);
1788 }
1789
1790 if (g_io_channel_flush (out_to_appletviewer, &channel_error)
1791 != G_IO_STATUS_NORMAL)
1792 {
1793 if (channel_error)
1794 {
1795 PLUGIN_ERROR ("Failed to flush bytes to output channel '%s'\n",
1796 channel_error->message);
1797 g_error_free (channel_error);
1798 channel_error = NULL;
1799 }
1800 else
1801 PLUGIN_ERROR ("Failed to flush bytes to output channel for %s", newline_message);
1802 }
1803 g_free (newline_message);
1804 newline_message = NULL;
1805
1806 PLUGIN_DEBUG (" PIPE: plugin wrote(?): %s\n", message);
1807 }
1808
1809 PLUGIN_DEBUG ("plugin_send_message_to_appletviewer return\n");
1810}
1811
1812// unlike like plugin_send_message_to_appletviewer
1813// do not debug
1814// do not error
1815// do not have its own line end
1816// is accesed by only one thread
1817// have own pipe
1818// jvm must be up
1819void
1820plugin_send_message_to_appletviewer_console (gchar const* newline_message)
1821{
1822 gsize bytes_written = 0;
1823 if (g_io_channel_write_chars (debug_to_appletviewer,
1824 newline_message, -1, &bytes_written,
1825 &channel_error) != G_IO_STATUS_NORMAL) {
1826 if (channel_error) {
1827 //error must be freed
1828 g_error_free (channel_error);
1829 channel_error = NULL;
1830 }
1831 }
1832}
1833//flush only when its full
1834void flush_plugin_send_message_to_appletviewer_console (){
1835 if (g_io_channel_flush (debug_to_appletviewer, &channel_error)
1836 != G_IO_STATUS_NORMAL) {
1837 if (channel_error) {
1838 g_error_free (channel_error);
1839 channel_error = NULL;
1840 }
1841 }
1842}
1843
1844/*
1845 * Sends the initialization message (handle/size/url) to the plugin
1846 */
1847void
1848plugin_send_initialization_message(char* instance, gulong handle,
1849 int width, int height, char* url)
1850{
1851 PLUGIN_DEBUG ("plugin_send_initialization_message\n");
1852
1853 gchar *window_message = g_strdup_printf ("instance %s handle %ld width %d height %d %s",
1854 instance, handle, width, height, url);
1855 plugin_send_message_to_appletviewer (window_message);
1856 g_free (window_message);
1857 window_message = NULL;
1858
1859 PLUGIN_DEBUG ("plugin_send_initialization_message return\n");
1860}
1861
1862
1863// Stop the appletviewer process. When this is called the
1864// appletviewer can be in any of three states: running, crashed or
1865// hung. If the appletviewer is running then sending it "shutdown"
1866// will cause it to exit. This will cause
1867// plugin_out_pipe_callback/plugin_in_pipe_callback to be called and
1868// the input and output channels to be shut down. If the appletviewer
1869// has crashed then plugin_out_pipe_callback/plugin_in_pipe_callback
1870// would already have been called and data->appletviewer_alive cleared
1871// in which case this function simply returns. If the appletviewer is
1872// hung then this function will be successful and the input and output
1873// watches will be removed by plugin_data_destroy.
1874// plugin_stop_appletviewer must be called with
1875// data->appletviewer_mutex held.
1876static void
1877plugin_stop_appletviewer ()
1878{
1879 PLUGIN_DEBUG ("plugin_stop_appletviewer\n");
1880
1881 if (jvm_up)
1882 {
1883 // Shut down the appletviewer.
1884 gsize bytes_written = 0;
1885
1886 if (out_to_appletviewer)
1887 {
1888 if (g_io_channel_write_chars (out_to_appletviewer, "shutdown",
1889 -1, &bytes_written, &channel_error)
1890 != G_IO_STATUS_NORMAL)
1891 {
1892 if (channel_error)
1893 {
1894 PLUGIN_ERROR ("Failed to write shutdown message to "
1895 " appletviewer, %s \n", channel_error->message);
1896 g_error_free (channel_error);
1897 channel_error = NULL;
1898 }
1899 else
1900 PLUGIN_ERROR ("Failed to write shutdown message to\n");
1901 }
1902
1903 if (g_io_channel_flush (out_to_appletviewer, &channel_error)
1904 != G_IO_STATUS_NORMAL)
1905 {
1906 if (channel_error)
1907 {
1908 PLUGIN_ERROR ("Failed to write shutdown message to"
1909 " appletviewer %s \n", channel_error->message);
1910 g_error_free (channel_error);
1911 channel_error = NULL;
1912 }
1913 else
1914 PLUGIN_ERROR ("Failed to write shutdown message to\n");
1915 }
1916
1917 if (g_io_channel_shutdown (out_to_appletviewer,
1918 TRUE, &channel_error)
1919 != G_IO_STATUS_NORMAL)
1920 {
1921 if (channel_error)
1922 {
1923 PLUGIN_ERROR ("Failed to shut down appletviewer"
1924 " output channel %s \n", channel_error->message);
1925 g_error_free (channel_error);
1926 channel_error = NULL;
1927 }
1928 else
1929 PLUGIN_ERROR ("Failed to shut down appletviewer\n");
1930 }
1931 }
1932
1933 if (in_from_appletviewer)
1934 {
1935 if (g_io_channel_shutdown (in_from_appletviewer,
1936 TRUE, &channel_error)
1937 != G_IO_STATUS_NORMAL)
1938 {
1939 if (channel_error)
1940 {
1941 PLUGIN_ERROR ("Failed to shut down appletviewer"
1942 " input channel %s \n", channel_error->message);
1943 g_error_free (channel_error);
1944 channel_error = NULL;
1945 }
1946 else
1947 PLUGIN_ERROR ("Failed to shut down appletviewer\n");
1948 }
1949 }
1950 }
1951
1952 jvm_up = FALSE;
1953 sleep(2); /* Needed to prevent crashes during debug (when JDWP port is not freed by the kernel right away) */
1954
1955 PLUGIN_DEBUG ("plugin_stop_appletviewer return\n");
1956}
1957
1958static void appletviewer_monitor(GPid pid, gint status, gpointer data)
1959{
1960 PLUGIN_DEBUG ("appletviewer_monitor\n");
1961 jvm_up = FALSE;
1962 pid = -1;
1963 PLUGIN_DEBUG ("appletviewer_monitor return\n");
1964}
1965
1966void
1967plugin_data_destroy (NPP instance)
1968{
1969 PLUGIN_DEBUG ("plugin_data_destroy\n");
1970
1971 ITNPPluginData* tofree = (ITNPPluginData*) instance->pdata;
1972
1973 // Remove instance from map
1974 gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance);
1975
1976 if (id_ptr)
1977 {
1978 gint id = GPOINTER_TO_INT(id_ptr);
1979 g_hash_table_remove(instance_to_id_map, instance);
1980 g_hash_table_remove(id_to_instance_map, id_ptr);
1981 }
1982
1983 /* Explicitly call destructor */
1984 tofree->~ITNPPluginData();
1985
1986 (*browser_functions.memfree) (tofree);
1987
1988 PLUGIN_DEBUG ("plugin_data_destroy return\n");
1989}
1990
1991static bool
1992initialize_browser_functions(const NPNetscapeFuncs* browserTable)
1993{
1994#define NPNETSCAPEFUNCS_LAST_FIELD_USED (browserTable->setvalueforurl)
1995
1996 //Determine the size in bytes, as a difference of the address past the last used field
1997 //And the browser table address
1998 size_t usedSize = (char*)(1 + &NPNETSCAPEFUNCS_LAST_FIELD_USED) - (char*)browserTable;
1999
2000 // compare the reported size versus the size we required
2001 if (browserTable->size < usedSize)
2002 {
2003 return false;
2004 }
2005
2006 //Ensure any unused fields are NULL
2007 memset(&browser_functions, 0, sizeof(NPNetscapeFuncs));
2008
2009 //browserTable->size can be larger than sizeof(NPNetscapeFuncs) (PR1106)
2010 size_t copySize = browserTable->size < sizeof(NPNetscapeFuncs) ?
2011 browserTable->size : sizeof(NPNetscapeFuncs);
2012
2013 //Copy fields according to given size
2014 memcpy(&browser_functions, browserTable, copySize);
2015
2016 return true;
2017}
2018
2019/* Set the plugin table to the correct contents, taking care not to write past
2020 * the provided object space */
2021static bool
2022initialize_plugin_table(NPPluginFuncs* pluginTable)
2023{
2024#define NPPLUGINFUNCS_LAST_FIELD_USED (pluginTable->getvalue)
2025
2026 //Determine the size in bytes, as a difference of the address past the last used field
2027 //And the browser table address
2028 size_t usedSize = (char*)(1 + &NPPLUGINFUNCS_LAST_FIELD_USED) - (char*)pluginTable;
2029
2030 // compare the reported size versus the size we required
2031 if (pluginTable->size < usedSize)
2032 return false;
2033
2034 pluginTable->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR;
2035 pluginTable->size = sizeof (NPPluginFuncs);
2036 pluginTable->newp = NPP_NewProcPtr (ITNP_New);
2037 pluginTable->destroy = NPP_DestroyProcPtr (ITNP_Destroy);
2038 pluginTable->setwindow = NPP_SetWindowProcPtr (ITNP_SetWindow);
2039 pluginTable->newstream = NPP_NewStreamProcPtr (ITNP_NewStream);
2040 pluginTable->destroystream = NPP_DestroyStreamProcPtr (ITNP_DestroyStream);
2041 pluginTable->asfile = NPP_StreamAsFileProcPtr (ITNP_StreamAsFile);
2042 pluginTable->writeready = NPP_WriteReadyProcPtr (ITNP_WriteReady);
2043 pluginTable->write = NPP_WriteProcPtr (ITNP_Write);
2044 pluginTable->print = NPP_PrintProcPtr (ITNP_Print);
2045 pluginTable->urlnotify = NPP_URLNotifyProcPtr (ITNP_URLNotify);
2046 pluginTable->getvalue = NPP_GetValueProcPtr (ITNP_GetValue);
2047
2048 return true;
2049}
2050
2051// Make sure the plugin data directory exists, creating it if necessary.
2052NPError
2053initialize_data_directory()
2054{
2055
2056 data_directory = IcedTeaPluginUtilities::getRuntimePath() + "/icedteaplugin-";
2057 if (getenv("USER") != NULL) {
2058 data_directory = data_directory + getenv("USER") + "-";
2059 }
2060 data_directory += "XXXXXX";
2061 // Now create a icedteaplugin subdir
2062 char fileNameX[data_directory.length()+1];
2063 std::strcpy (fileNameX, data_directory.c_str());
2064 char * fileName = mkdtemp(fileNameX);
2065 if (fileName == NULL) {
2066 PLUGIN_ERROR ("Failed to create data directory %s, %s\n",
2067 data_directory.c_str(),
2068 strerror (errno));
2069 return NPERR_GENERIC_ERROR;
2070 }
2071 data_directory = std::string(fileName);
2072
2073 //open uniques icedteaplugin subdir for one single run
2074 data_directory_descriptor = opendir(data_directory.c_str());
2075 if (data_directory_descriptor == NULL) {
2076 PLUGIN_ERROR ("Failed to open data directory %s %s\n",
2077 data_directory.c_str(), strerror (errno));
2078 return NPERR_GENERIC_ERROR;
2079 }
2080
2081 return NPERR_NO_ERROR;
2082}
2083
2084// FACTORY FUNCTIONS
2085
2086// Provides the browser with pointers to the plugin functions that we
2087// implement and initializes a local table with browser functions that
2088// we may wish to call. Called once, after browser startup and before
2089// the first plugin instance is created.
2090// The field 'initialized' is set to true once this function has
2091// finished. If 'initialized' is already true at the beginning of
2092// this function, then it is evident that NP_Initialize has already
2093// been called. There is no need to call this function more than once and
2094// this workaround avoids any duplicate calls.
2095__attribute__ ((visibility ("default")))
2096NPError
2097#if defined(_WIN32) || defined (__OS2__)
2098OSCALL NP_Initialize (NPNetscapeFuncs* browserTable)
2099#else
2100OSCALL NP_Initialize (NPNetscapeFuncs* browserTable, NPPluginFuncs* pluginTable)
2101#endif
2102{
2103 PLUGIN_DEBUG ("NP_Initialize\n");
2104
2105#if defined(_WIN32) || defined (__OS2__)
2106 if (browserTable == NULL)
2107 {
2108 PLUGIN_ERROR ("Browser function table is NULL.");
2109
2110 return NPERR_INVALID_FUNCTABLE_ERROR;
2111 }
2112#else
2113 if ((browserTable == NULL) || (pluginTable == NULL))
2114 {
2115 PLUGIN_ERROR ("Browser or plugin function table is NULL.\n");
2116
2117 return NPERR_INVALID_FUNCTABLE_ERROR;
2118 }
2119#endif
2120
2121 // Ensure that the major version of the plugin API that the browser
2122 // expects is not more recent than the major version of the API that
2123 // we've implemented.
2124 if ((browserTable->version >> 8) > NP_VERSION_MAJOR)
2125 {
2126 PLUGIN_ERROR ("Incompatible version.\n");
2127
2128 return NPERR_INCOMPATIBLE_VERSION_ERROR;
2129 }
2130
2131 // Copy into a global table (browser_functions) the browser functions that we may use.
2132 // If the browser functions needed change, update NPNETSCAPEFUNCS_LAST_FIELD_USED
2133 // within this function
2134 bool browser_functions_supported = initialize_browser_functions(browserTable);
2135
2136 // Check if everything we rely on is supported
2137 if ( !browser_functions_supported )
2138 {
2139 PLUGIN_ERROR ("Invalid browser function table.\n");
2140
2141 return NPERR_INVALID_FUNCTABLE_ERROR;
2142 }
2143
2144#if !defined(_WIN32) && !defined (__OS2__)
2145 // Return to the browser the plugin functions that we implement.
2146 // If the plugin functions needed change, update NPPLUGINFUNCS_LAST_FIELD_USED
2147 // within this function
2148 bool plugin_functions_supported = initialize_plugin_table(pluginTable);
2149
2150 // Check if everything we rely on is supported
2151 if ( !plugin_functions_supported )
2152 {
2153 PLUGIN_ERROR ("Invalid plugin function table.\n");
2154
2155 return NPERR_INVALID_FUNCTABLE_ERROR;
2156 }
2157#endif
2158
2159 // Re-setting the above tables multiple times is OK (as the
2160 // browser may change its function locations). However
2161 // anything beyond this point should only run once.
2162 if (initialized)
2163 return NPERR_NO_ERROR;
2164
2165#ifdef __OS2__
2166 // perform OS-specific initialization
2167 if (!init_os())
2168 {
2169 PLUGIN_ERROR ("Failed to perform OS-specific initialization.");
2170 return NPERR_GENERIC_ERROR;
2171 }
2172#endif
2173
2174 NPError np_error = NPERR_NO_ERROR;
2175#ifndef __OS2__
2176 // create directory for pipes
2177 np_error = initialize_data_directory();
2178 if (np_error != NPERR_NO_ERROR)
2179 {
2180 PLUGIN_ERROR("Unable to create data directory %s\n", data_directory.c_str());
2181 return np_error;
2182 }
2183#endif
2184
2185 // Set appletviewer_executable.
2186 PLUGIN_DEBUG("Executing java at %s\n", get_plugin_executable().c_str());
2187 np_error = plugin_test_appletviewer ();
2188 if (np_error != NPERR_NO_ERROR)
2189 {
2190 PLUGIN_ERROR("Unable to find java executable %s\n", get_plugin_executable().c_str());
2191 return np_error;
2192 }
2193
2194 // Initialize threads (needed for mutexes).
2195 if (!g_thread_supported ())
2196 g_thread_init (NULL);
2197
2198#ifdef __OS2__
2199 if (!g_main_context_os2_start_pm_integration (NULL))
2200 {
2201 PLUGIN_DEBUG ("Failed to integrate with PM");
2202 return NPERR_GENERIC_ERROR;
2203 }
2204#endif
2205
2206 plugin_instance_mutex = g_mutex_new ();
2207
2208 PLUGIN_DEBUG ("NP_Initialize: using %s\n", get_plugin_executable().c_str());
2209
2210 plugin_req_proc = new PluginRequestProcessor();
2211 java_req_proc = new JavaMessageSender();
2212
2213 java_to_plugin_bus = new MessageBus();
2214 plugin_to_java_bus = new MessageBus();
2215
2216 java_to_plugin_bus->subscribe(plugin_req_proc);
2217 plugin_to_java_bus->subscribe(java_req_proc);
2218
2219#ifdef __OS2__
2220 queue_processor_data1.processor = plugin_req_proc;
2221 queue_processor_data2.processor = plugin_req_proc;
2222 queue_processor_data3.processor = plugin_req_proc;
2223 pthread_create (&plugin_request_processor_thread1, NULL, &queue_processor, (void*) &queue_processor_data1);
2224 pthread_create (&plugin_request_processor_thread2, NULL, &queue_processor, (void*) &queue_processor_data2);
2225 pthread_create (&plugin_request_processor_thread3, NULL, &queue_processor, (void*) &queue_processor_data3);
2226#else
2227 pthread_create (&plugin_request_processor_thread1, NULL, &queue_processor, (void*) plugin_req_proc);
2228 pthread_create (&plugin_request_processor_thread2, NULL, &queue_processor, (void*) plugin_req_proc);
2229 pthread_create (&plugin_request_processor_thread3, NULL, &queue_processor, (void*) plugin_req_proc);
2230#endif
2231
2232 itnp_plugin_thread_id = pthread_self();
2233
2234 pthread_mutexattr_t attribute;
2235 pthread_mutexattr_init(&attribute);
2236 pthread_mutexattr_settype(&attribute, PTHREAD_MUTEX_RECURSIVE);
2237 pthread_mutex_init(&pluginAsyncCallMutex, &attribute);
2238 pthread_mutexattr_destroy(&attribute);
2239
2240 initialized = true;
2241
2242 PLUGIN_DEBUG ("NP_Initialize return\n");
2243
2244 return NPERR_NO_ERROR;
2245}
2246
2247#if defined(_WIN32) || defined (__OS2__)
2248NPError
2249OSCALL NP_GetEntryPoints (NPPluginFuncs* pluginTable)
2250{
2251 PLUGIN_DEBUG ("NP_GetEntryPoints\n");
2252
2253 if (pluginTable == NULL)
2254 {
2255 PLUGIN_ERROR ("Plugin function table is NULL.");
2256
2257 return NPERR_INVALID_FUNCTABLE_ERROR;
2258 }
2259
2260 // Return to the browser the plugin functions that we implement.
2261 // If the plugin functions needed change, update NPPLUGINFUNCS_LAST_FIELD_USED
2262 // within this function
2263 bool plugin_functions_supported = initialize_plugin_table(pluginTable);
2264
2265 // Check if everything we rely on is supported
2266 if ( !plugin_functions_supported )
2267 {
2268 PLUGIN_ERROR ("Invalid plugin function table.");
2269
2270 return NPERR_INVALID_FUNCTABLE_ERROR;
2271 }
2272
2273 return NPERR_NO_ERROR;
2274}
2275#endif
2276
2277// Returns a string describing the MIME type that this plugin
2278// handles.
2279__attribute__ ((visibility ("default")))
2280#ifdef LEGACY_XULRUNNERAPI
2281 char*
2282#else
2283 const char*
2284#endif
2285OSCALL NP_GetMIMEDescription ()
2286{
2287 //this function is called severaltimes between lunches
2288 PLUGIN_DEBUG ("NP_GetMIMEDescription\n");
2289
2290 PLUGIN_DEBUG ("NP_GetMIMEDescription return\n");
2291
2292 return PLUGIN_MIME_DESC;
2293}
2294
2295// Returns a value relevant to the plugin as a whole. The browser
2296// calls this function to obtain information about the plugin.
2297__attribute__ ((visibility ("default")))
2298NPError
2299OSCALL NP_GetValue (void* future, NPPVariable variable, void* value)
2300{
2301 PLUGIN_DEBUG ("NP_GetValue\n");
2302
2303 NPError result = NPERR_NO_ERROR;
2304 gchar** char_value = (gchar**) value;
2305
2306 switch (variable)
2307 {
2308 case NPPVpluginNameString:
2309 PLUGIN_DEBUG ("NP_GetValue: returning plugin name.\n");
2310 *char_value = g_strdup (PLUGIN_FULL_NAME);
2311 break;
2312
2313 case NPPVpluginDescriptionString:
2314 PLUGIN_DEBUG ("NP_GetValue: returning plugin description.\n");
2315 *char_value = g_strdup (PLUGIN_DESC);
2316 break;
2317
2318 default:
2319 PLUGIN_ERROR ("Unknown plugin value requested.\n");
2320 result = NPERR_GENERIC_ERROR;
2321 break;
2322 }
2323
2324 PLUGIN_DEBUG ("NP_GetValue return\n");
2325
2326 return result;
2327}
2328
2329// Shuts down the plugin. Called after the last plugin instance is
2330// destroyed.
2331__attribute__ ((visibility ("default")))
2332NPError
2333OSCALL NP_Shutdown (void)
2334{
2335 PLUGIN_DEBUG ("NP_Shutdown\n");
2336
2337#ifdef __OS2__
2338 g_main_context_os2_stop_pm_integration (NULL);
2339#endif
2340
2341 // Free mutex.
2342 if (plugin_instance_mutex)
2343 {
2344 g_mutex_free (plugin_instance_mutex);
2345 plugin_instance_mutex = NULL;
2346 }
2347
2348 // stop the appletviewer
2349 plugin_stop_appletviewer();
2350
2351 // remove monitor
2352 if (appletviewer_watch_id != -1)
2353 g_source_remove(appletviewer_watch_id);
2354
2355 // Removing a source is harmless if it fails since it just means the
2356 // source has already been removed.
2357 g_source_remove (in_watch_source);
2358 in_watch_source = 0;
2359
2360 // cleanup_in_from_appletviewer:
2361 if (in_from_appletviewer)
2362 g_io_channel_unref (in_from_appletviewer);
2363 in_from_appletviewer = NULL;
2364
2365 // cleanup_out_watch_source:
2366 g_source_remove (out_watch_source);
2367 out_watch_source = 0;
2368
2369 // cleanup_out_to_appletviewer:
2370 if (out_to_appletviewer)
2371 g_io_channel_unref (out_to_appletviewer);
2372 out_to_appletviewer = NULL;
2373
2374 // cleanup_out_pipe:
2375#ifdef __OS2__
2376 CLOSE_FD (out_pipe [0]);
2377 CLOSE_FD (out_pipe [1]);
2378#else
2379 // Delete output pipe.
2380 PLUGIN_DEBUG ("NP_Shutdown: deleting output fifo: %s\n", out_pipe_name);
2381 unlink (out_pipe_name);
2382 PLUGIN_DEBUG ("NP_Shutdown: deleted output fifo: %s\n", out_pipe_name);
2383
2384 // cleanup_out_pipe_name:
2385 g_free (out_pipe_name);
2386 out_pipe_name = NULL;
2387#endif
2388
2389 // cleanup_in_pipe:
2390#ifdef __OS2__
2391 CLOSE_FD (in_pipe [0]);
2392 CLOSE_FD (in_pipe [1]);
2393#else
2394 // Delete input pipe.
2395 PLUGIN_DEBUG ("NP_Shutdown: deleting input fifo: %s\n", in_pipe_name);
2396 unlink (in_pipe_name);
2397 PLUGIN_DEBUG ("NP_Shutdown: deleted input fifo: %s\n", in_pipe_name);
2398
2399 // cleanup_in_pipe_name:
2400 g_free (in_pipe_name);
2401 in_pipe_name = NULL;
2402#endif
2403
2404 if (plugin_debug_to_console){
2405 //jvm_up is now false
2406 if (g_io_channel_shutdown (debug_to_appletviewer,
2407 TRUE, &channel_error)
2408 != G_IO_STATUS_NORMAL)
2409 {
2410 if (channel_error)
2411 {
2412 PLUGIN_ERROR ("Failed to shut down appletviewer"
2413 " debug channel\n", channel_error->message);
2414 g_error_free (channel_error);
2415 channel_error = NULL;
2416 }
2417 else
2418 PLUGIN_ERROR ("Failed to shut down debug to appletviewer\n");
2419 }
2420 // cleanup_out_to_appletviewer:
2421 if (debug_to_appletviewer)
2422 g_io_channel_unref (debug_to_appletviewer);
2423 out_to_appletviewer = NULL;
2424 // cleanup_debug_pipe:
2425 // Delete debug pipe.
2426 PLUGIN_DEBUG ("NP_Shutdown: deleting debug fifo: %s\n", debug_pipe_name);
2427 unlink (debug_pipe_name);
2428 PLUGIN_DEBUG ("NP_Shutdown: deleted debug fifo: %s\n", debug_pipe_name);
2429 // cleanup_out_pipe_name:
2430 g_free (debug_pipe_name);
2431 debug_pipe_name = NULL;
2432 }
2433
2434 // Destroy the call queue mutex
2435 pthread_mutex_destroy(&pluginAsyncCallMutex);
2436
2437#ifdef __OS2__
2438 // perform OS-specific uninitialization
2439 done_os();
2440#endif
2441
2442 initialized = false;
2443
2444#ifdef __OS2__
2445 // pthread_cancel() isn't implemented on OS?2, so use an old good flag
2446 queue_processor_data1.stopRequested = true;
2447 queue_processor_data2.stopRequested = true;
2448 queue_processor_data3.stopRequested = true;
2449 pthread_cond_broadcast(&cond_message_available);
2450#else
2451 pthread_cancel(plugin_request_processor_thread1);
2452 pthread_cancel(plugin_request_processor_thread2);
2453 pthread_cancel(plugin_request_processor_thread3);
2454#endif
2455
2456 pthread_join(plugin_request_processor_thread1, NULL);
2457 pthread_join(plugin_request_processor_thread2, NULL);
2458 pthread_join(plugin_request_processor_thread3, NULL);
2459
2460 java_to_plugin_bus->unSubscribe(plugin_req_proc);
2461 plugin_to_java_bus->unSubscribe(java_req_proc);
2462 //internal_bus->unSubscribe(java_req_proc);
2463 //internal_bus->unSubscribe(plugin_req_proc);
2464
2465 delete plugin_req_proc;
2466 delete java_req_proc;
2467 delete java_to_plugin_bus;
2468 delete plugin_to_java_bus;
2469 //delete internal_bus;
2470
2471 cleanUpDir();
2472
2473 PLUGIN_DEBUG ("NP_Shutdown return\n");
2474
2475 if (plugin_debug_to_file){
2476 fflush (plugin_file_log);
2477 //fclose (plugin_file_log);
2478 //keep writing untill possible!
2479 }
2480
2481 return NPERR_NO_ERROR;
2482}
2483
2484NPObject*
2485get_scriptable_object(NPP instance)
2486{
2487 NPObject* obj;
2488 ITNPPluginData* data = (ITNPPluginData*) instance->pdata;
2489
2490 if (data->is_applet_instance) // dummy instance/package?
2491 {
2492 JavaRequestProcessor java_request = JavaRequestProcessor();
2493 JavaResultData* java_result;
2494 std::string instance_id = std::string();
2495 std::string applet_class_id = std::string();
2496
2497 int id = get_id_from_instance(instance);
2498 gchar* id_str = g_strdup_printf ("%d", id);
2499
2500 // Some browsers.. (e.g. chromium) don't call NPP_SetWindow
2501 // for 0x0 plugins and therefore require initialization with
2502 // a 0 handle
2503 if (!data->window_handle)
2504 {
2505 plugin_send_initialization_message(data->instance_id, 0, 0, 0, data->parameters_string);
2506 }
2507
2508 java_result = java_request.getAppletObjectInstance(id_str);
2509
2510 g_free(id_str);
2511
2512 if (java_result->error_occurred)
2513 {
2514 PLUGIN_ERROR("Error: Unable to fetch applet instance id from Java side.\n");
2515 return NULL;
2516 }
2517
2518 instance_id.append(*(java_result->return_string));
2519
2520 java_result = java_request.getClassID(instance_id);
2521
2522 if (java_result->error_occurred)
2523 {
2524 PLUGIN_ERROR("Error: Unable to fetch applet instance id from Java side.\n");
2525 return NULL;
2526 }
2527
2528 applet_class_id.append(*(java_result->return_string));
2529
2530 obj = IcedTeaScriptableJavaObject::get_scriptable_java_object(instance, applet_class_id, instance_id, false);
2531
2532 } else
2533 {
2534 obj = IcedTeaScriptableJavaPackageObject::get_scriptable_java_package_object(instance, "");
2535 }
2536
2537 return obj;
2538}
2539
2540NPObject*
2541allocate_scriptable_object(NPP npp, NPClass *aClass)
2542{
2543 PLUGIN_DEBUG("Allocating new scriptable object\n");
2544 return new IcedTeaScriptablePluginObject(npp);
2545}
2546
2547#ifdef __OS2__
2548
2549// Make sure static initializers in the plugin DLL are called
2550
2551static APIENTRY void cleanup(ULONG ulReason)
2552{
2553 __ctordtorTerm();
2554 _CRT_term();
2555 DosExitList(EXLST_EXIT, cleanup);
2556}
2557
2558unsigned long _System _DLL_InitTerm(unsigned long hModule,
2559 unsigned long ulFlag)
2560{
2561 APIRET arc;
2562
2563 if (ulFlag == 0)
2564 {
2565 arc = DosExitList (EXLST_ADD, cleanup);
2566 if (arc != NO_ERROR)
2567 return 0;
2568
2569 if (_CRT_init() != 0) // failure?
2570 return 0;
2571 __ctordtorInit();
2572 }
2573
2574 return 1;
2575}
2576
2577#endif /* __OS2__ */
Note: See TracBrowser for help on using the repository browser.