diff options
Diffstat (limited to 'plugin/icedteanp/IcedTeaNPPlugin.cc')
-rw-r--r-- | plugin/icedteanp/IcedTeaNPPlugin.cc | 2453 |
1 files changed, 2453 insertions, 0 deletions
diff --git a/plugin/icedteanp/IcedTeaNPPlugin.cc b/plugin/icedteanp/IcedTeaNPPlugin.cc new file mode 100644 index 0000000..ac40103 --- /dev/null +++ b/plugin/icedteanp/IcedTeaNPPlugin.cc @@ -0,0 +1,2453 @@ +/* IcedTeaNPPlugin.cc -- web browser plugin to execute Java applets + Copyright (C) 2003, 2004, 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 2009, 2010 Red Hat + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +// System includes. +#include <dlfcn.h> +#include <errno.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +// Liveconnect extension +#include "IcedTeaScriptablePluginObject.h" +#include "IcedTeaNPPlugin.h" + +#if MOZILLA_VERSION_COLLAPSED < 1090100 +// Documentbase retrieval includes. +#include <nsIPluginInstance.h> +#include <nsIPluginInstancePeer.h> +#include <nsIPluginTagInfo2.h> + +// API's into Mozilla +#include <nsCOMPtr.h> +#include <nsICookieService.h> +#include <nsIDNSRecord.h> +#include <nsIDNSService.h> +#include <nsINetUtil.h> +#include <nsIProxyInfo.h> +#include <nsIProtocolProxyService.h> +#include <nsIScriptSecurityManager.h> +#include <nsIIOService.h> +#include <nsIURI.h> +#include <nsNetCID.h> +#include <nsStringAPI.h> +#include <nsServiceManagerUtils.h> +#endif + +// Error reporting macros. +#define PLUGIN_ERROR(message) \ + g_printerr ("%s:%d: thread %p: Error: %s\n", __FILE__, __LINE__, \ + g_thread_self (), message) + +#define PLUGIN_ERROR_TWO(first, second) \ + g_printerr ("%s:%d: thread %p: Error: %s: %s\n", __FILE__, __LINE__, \ + g_thread_self (), first, second) + +#define PLUGIN_ERROR_THREE(first, second, third) \ + g_printerr ("%s:%d: thread %p: Error: %s: %s: %s\n", __FILE__, \ + __LINE__, g_thread_self (), first, second, third) + +// Plugin information passed to about:plugins. +#define PLUGIN_NAME "IcedTea NPR Web Browser Plugin (using " PLUGIN_VERSION ")" +#define PLUGIN_DESC "The " PLUGIN_NAME " executes Java applets." + +#define PLUGIN_MIME_DESC \ + "application/x-java-vm:class,jar:IcedTea;" \ + "application/x-java-applet:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.1.3:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.2.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.2.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.3:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.3.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.4:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.4.1:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.4.2:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.5:class,jar:IcedTea;" \ + "application/x-java-applet;version=1.6:class,jar:IcedTea;" \ + "application/x-java-applet;jpi-version=1.6.0_" JDK_UPDATE_VERSION ":class,jar:IcedTea;" \ + "application/x-java-bean:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.1.3:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.2.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.2.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.3:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.3.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.4:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.4.1:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.4.2:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.5:class,jar:IcedTea;" \ + "application/x-java-bean;version=1.6:class,jar:IcedTea;" \ + "application/x-java-bean;jpi-version=1.6.0_" JDK_UPDATE_VERSION ":class,jar:IcedTea;" \ + "application/x-java-vm-npruntime::IcedTea;" + +#define PLUGIN_URL NS_INLINE_PLUGIN_CONTRACTID_PREFIX NS_JVM_MIME_TYPE +#define PLUGIN_MIME_TYPE "application/x-java-vm" +#define PLUGIN_FILE_EXTS "class,jar,zip" +#define PLUGIN_MIME_COUNT 1 + +#define FAILURE_MESSAGE "icedteanp plugin error: Failed to run %s." \ + " For more detail rerun \"firefox -g\" in a terminal window." + +#if MOZILLA_VERSION_COLLAPSED < 1090100 +// Documentbase retrieval required definition. +static NS_DEFINE_IID (kIPluginTagInfo2IID, NS_IPLUGINTAGINFO2_IID); +#endif + +// Data directory for plugin. +static gchar* data_directory = NULL; + +// Fully-qualified appletviewer executable. +static gchar* appletviewer_executable = NULL; + +// Applet viewer input channel (needs to be static because it is used in plugin_in_pipe_callback) +static GIOChannel* in_from_appletviewer = NULL; + +// Applet viewer input pipe name. +gchar* in_pipe_name; + +// Applet viewer input watch source. +gint in_watch_source; + +// Applet viewer output pipe name. +gchar* out_pipe_name; + +// Applet viewer output watch source. +gint out_watch_source; + +// Applet viewer output channel. +GIOChannel* out_to_appletviewer; + +// Tracks jvm status +gboolean jvm_up = FALSE; + +// Keeps track of initialization. NP_Initialize should only be +// called once. +gboolean initialized = false; + +// browser functions into mozilla +NPNetscapeFuncs browser_functions; + +// Various message buses carrying information to/from Java, and internally +MessageBus* plugin_to_java_bus; +MessageBus* java_to_plugin_bus; +//MessageBus* internal_bus = new MessageBus(); + +// Processor for plugin requests +PluginRequestProcessor* plugin_req_proc; + +// Sends messages to Java over the bus +JavaMessageSender* java_req_proc; + +#if MOZILLA_VERSION_COLLAPSED < 1090100 +// Documentbase retrieval type-punning union. +typedef union +{ + void** void_field; + nsIPluginTagInfo2** info_field; +} info_union; +#endif + +// Static instance helper functions. +// Have the browser allocate a new ITNPPluginData structure. +static void plugin_data_new (ITNPPluginData** data); +// Retrieve the current document's documentbase. +static gchar* plugin_get_documentbase (NPP instance); +// Notify the user that the appletviewer is not installed correctly. +static void plugin_display_failure_dialog (); +// Callback used to monitor input pipe status. +static gboolean plugin_in_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data); +// Callback used to monitor output pipe status. +static gboolean plugin_out_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data); +static NPError plugin_start_appletviewer (ITNPPluginData* data); +static gchar* plugin_create_applet_tag (int16_t argc, char* argn[], + char* argv[]); +static void plugin_stop_appletviewer (); +// Uninitialize ITNPPluginData structure +static void plugin_data_destroy (NPP instance); + +NPError get_cookie_info(const char* siteAddr, char** cookieString, uint32_t* len); +NPError get_proxy_info(const char* siteAddr, char** proxy, uint32_t* len); +void consume_message(gchar* message); +void start_jvm_if_needed(); +static void appletviewer_monitor(GPid pid, gint status, gpointer data); + +// Global instance counter. +// Mutex to protect plugin_instance_counter. +static GMutex* plugin_instance_mutex = NULL; +// A global variable for reporting GLib errors. This must be free'd +// and set to NULL after each use. +static GError* channel_error = NULL; + +static GHashTable* instance_to_id_map = g_hash_table_new(NULL, NULL); +static GHashTable* id_to_instance_map = g_hash_table_new(NULL, NULL); +static gint instance_counter = 1; +static GPid appletviewer_pid = -1; +static guint appletviewer_watch_id = -1; + +int plugin_debug = getenv ("ICEDTEAPLUGIN_DEBUG") != NULL; + +pthread_cond_t cond_message_available = PTHREAD_COND_INITIALIZER; + +// Functions prefixed by ITNP_ are instance functions. They are called +// by the browser and operate on instances of ITNPPluginData. +// Functions prefixed by plugin_ are static helper functions. +// Functions prefixed by NP_ are factory functions. They are called +// by the browser and provide functionality needed to create plugin +// instances. + +// INSTANCE FUNCTIONS + +// Creates a new icedtea np plugin instance. This function creates a +// ITNPPluginData* and stores it in instance->pdata. The following +// ITNPPluginData fiels are initialized: instance_id, in_pipe_name, +// in_from_appletviewer, in_watch_source, out_pipe_name, +// out_to_appletviewer, out_watch_source, appletviewer_mutex, owner, +// appletviewer_alive. In addition two pipe files are created. All +// of those fields must be properly destroyed, and the pipes deleted, +// by ITNP_Destroy. If an error occurs during initialization then this +// function will free anything that's been allocated so far, set +// instance->pdata to NULL and return an error code. +NPError +ITNP_New (NPMIMEType pluginType, NPP instance, uint16_t mode, + int16_t argc, char* argn[], char* argv[], + NPSavedData* saved) +{ + PLUGIN_DEBUG("ITNP_New\n"); + + static NPObject *window_ptr; + NPIdentifier identifier; + NPVariant member_ptr; + browser_functions.getvalue(instance, NPNVWindowNPObject, &window_ptr); + identifier = browser_functions.getstringidentifier("document"); + if (!browser_functions.hasproperty(instance, window_ptr, identifier)) + { + printf("%s not found!\n", "document"); + } + browser_functions.getproperty(instance, window_ptr, identifier, &member_ptr); + + PLUGIN_DEBUG("Got variant %p\n", &member_ptr); + + + NPError np_error = NPERR_NO_ERROR; + ITNPPluginData* data = NULL; + + gchar* documentbase = NULL; + gchar* read_message = NULL; + gchar* applet_tag = NULL; + gchar* cookie_info = NULL; + + NPObject* npPluginObj = NULL; + + if (!instance) + { + PLUGIN_ERROR ("Browser-provided instance pointer is NULL."); + np_error = NPERR_INVALID_INSTANCE_ERROR; + goto cleanup_done; + } + + // data + plugin_data_new (&data); + if (data == NULL) + { + PLUGIN_ERROR ("Failed to allocate plugin data."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_done; + } + + // start the jvm if needed + start_jvm_if_needed(); + + // Initialize data->instance_id. + // + // instance_id should be unique for this process so we use a + // combination of getpid and plugin_instance_counter. + // + // Critical region. Reference and increment plugin_instance_counter + // global. + g_mutex_lock (plugin_instance_mutex); + + // data->instance_id + data->instance_id = g_strdup_printf ("%d", + instance_counter); + + g_mutex_unlock (plugin_instance_mutex); + + // data->appletviewer_mutex + data->appletviewer_mutex = g_mutex_new (); + + g_mutex_lock (data->appletviewer_mutex); + + // Documentbase retrieval. + documentbase = plugin_get_documentbase (instance); + if (documentbase && argc != 0) + { + // Send applet tag message to appletviewer. + applet_tag = plugin_create_applet_tag (argc, argn, argv); + + data->applet_tag = (gchar*) malloc(strlen(applet_tag)*sizeof(gchar) + strlen(documentbase)*sizeof(gchar) + 32); + g_sprintf(data->applet_tag, "tag %s %s", documentbase, applet_tag); + + data->is_applet_instance = true; + } + + if (argc == 0) + { + data->is_applet_instance = false; + } + + g_mutex_unlock (data->appletviewer_mutex); + + // If initialization succeeded entirely then we store the plugin + // data in the instance structure and return. Otherwise we free the + // data we've allocated so far and set instance->pdata to NULL. + + // Set back-pointer to owner instance. + data->owner = instance; + + // source of this instance + // don't use documentbase, it is cleared later + data->source = plugin_get_documentbase(instance); + + instance->pdata = data; + + goto cleanup_done; + + cleanup_appletviewer_mutex: + g_free (data->appletviewer_mutex); + data->appletviewer_mutex = NULL; + + // cleanup_instance_string: + g_free (data->instance_id); + data->instance_id = NULL; + + // cleanup applet tag: + g_free (data->applet_tag); + data->applet_tag = NULL; + + // cleanup_data: + // Eliminate back-pointer to plugin instance. + data->owner = NULL; + (*browser_functions.memfree) (data); + data = NULL; + + // Initialization failed so return a NULL pointer for the browser + // data. + instance->pdata = NULL; + + cleanup_done: + g_free (applet_tag); + applet_tag = NULL; + g_free (read_message); + read_message = NULL; + g_free (documentbase); + documentbase = NULL; + + // store an identifier for this plugin + PLUGIN_DEBUG("Mapping id %d and instance %p\n", instance_counter, instance); + g_hash_table_insert(instance_to_id_map, instance, GINT_TO_POINTER(instance_counter)); + g_hash_table_insert(id_to_instance_map, GINT_TO_POINTER(instance_counter), instance); + instance_counter++; + + PLUGIN_DEBUG ("ITNP_New return\n"); + + return np_error; +} + +// Starts the JVM if it is not already running +void start_jvm_if_needed() +{ + + // This is asynchronized function. It must + // have exclusivity when running. + + GMutex *vm_start_mutex = g_mutex_new(); + g_mutex_lock(vm_start_mutex); + + PLUGIN_DEBUG("Checking JVM status...\n"); + + // If the jvm is already up, do nothing + if (jvm_up) + { + PLUGIN_DEBUG("JVM is up. Returning.\n"); + return; + } + + PLUGIN_DEBUG("No JVM is running. Attempting to start one...\n"); + + NPError np_error = NPERR_NO_ERROR; + ITNPPluginData* data = NULL; + + // Create appletviewer-to-plugin pipe which we refer to as the input + // pipe. + + // in_pipe_name + in_pipe_name = g_strdup_printf ("%s/%d-icedteanp-appletviewer-to-plugin", + data_directory, getpid()); + if (!in_pipe_name) + { + PLUGIN_ERROR ("Failed to create input pipe name."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + // If in_pipe_name is NULL then the g_free at + // cleanup_in_pipe_name will simply return. + goto cleanup_in_pipe_name; + } + + // clean up any older pip + unlink (in_pipe_name); + + PLUGIN_DEBUG ("ITNP_New: creating input fifo: %s\n", in_pipe_name); + if (mkfifo (in_pipe_name, 0600) == -1 && errno != EEXIST) + { + PLUGIN_ERROR_TWO ("Failed to create input pipe", strerror (errno)); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_in_pipe_name; + } + PLUGIN_DEBUG ("ITNP_New: created input fifo: %s\n", in_pipe_name); + + // Create plugin-to-appletviewer pipe which we refer to as the + // output pipe. + + // out_pipe_name + out_pipe_name = g_strdup_printf ("%s/%d-icedteanp-plugin-to-appletviewer", + data_directory, getpid()); + + if (!out_pipe_name) + { + PLUGIN_ERROR ("Failed to create output pipe name."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_out_pipe_name; + } + + // clean up any older pip + unlink (out_pipe_name); + + PLUGIN_DEBUG ("ITNP_New: creating output fifo: %s\n", out_pipe_name); + if (mkfifo (out_pipe_name, 0600) == -1 && errno != EEXIST) + { + PLUGIN_ERROR_TWO ("Failed to create output pipe", strerror (errno)); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_out_pipe_name; + } + PLUGIN_DEBUG ("ITNP_New: created output fifo: %s\n", out_pipe_name); + + // Start a separate appletviewer process for each applet, even if + // there are multiple applets in the same page. We may need to + // change this behaviour if we find pages with multiple applets that + // rely on being run in the same VM. + + np_error = plugin_start_appletviewer (data); + + // Create plugin-to-appletviewer channel. The default encoding for + // the file is UTF-8. + // out_to_appletviewer + out_to_appletviewer = g_io_channel_new_file (out_pipe_name, + "w", &channel_error); + if (!out_to_appletviewer) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to create output channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to create output channel"); + + np_error = NPERR_GENERIC_ERROR; + goto cleanup_out_to_appletviewer; + } + + // Watch for hangup and error signals on the output pipe. + out_watch_source = + g_io_add_watch (out_to_appletviewer, + (GIOCondition) (G_IO_ERR | G_IO_HUP), + plugin_out_pipe_callback, (gpointer) out_to_appletviewer); + + // Create appletviewer-to-plugin channel. The default encoding for + // the file is UTF-8. + // in_from_appletviewer + in_from_appletviewer = g_io_channel_new_file (in_pipe_name, + "r", &channel_error); + if (!in_from_appletviewer) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to create input channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to create input channel"); + + np_error = NPERR_GENERIC_ERROR; + goto cleanup_in_from_appletviewer; + } + + // Watch for hangup and error signals on the input pipe. + in_watch_source = + g_io_add_watch (in_from_appletviewer, + (GIOCondition) (G_IO_IN | G_IO_ERR | G_IO_HUP), + plugin_in_pipe_callback, (gpointer) in_from_appletviewer); + + jvm_up = TRUE; + + goto done; + + // Free allocated data + + cleanup_in_watch_source: + // Removing a source is harmless if it fails since it just means the + // source has already been removed. + g_source_remove (in_watch_source); + in_watch_source = 0; + + cleanup_in_from_appletviewer: + if (in_from_appletviewer) + g_io_channel_unref (in_from_appletviewer); + in_from_appletviewer = NULL; + + // cleanup_out_watch_source: + g_source_remove (out_watch_source); + out_watch_source = 0; + + cleanup_out_to_appletviewer: + if (out_to_appletviewer) + g_io_channel_unref (out_to_appletviewer); + out_to_appletviewer = NULL; + + // cleanup_out_pipe: + // Delete output pipe. + PLUGIN_DEBUG ("ITNP_New: deleting input fifo: %s\n", in_pipe_name); + unlink (out_pipe_name); + PLUGIN_DEBUG ("ITNP_New: deleted input fifo: %s\n", in_pipe_name); + + cleanup_out_pipe_name: + g_free (out_pipe_name); + out_pipe_name = NULL; + + // cleanup_in_pipe: + // Delete input pipe. + PLUGIN_DEBUG ("ITNP_New: deleting output fifo: %s\n", out_pipe_name); + unlink (in_pipe_name); + PLUGIN_DEBUG ("ITNP_New: deleted output fifo: %s\n", out_pipe_name); + + cleanup_in_pipe_name: + g_free (in_pipe_name); + in_pipe_name = NULL; + + done: + + // Now other threads may re-enter.. unlock the mutex + g_mutex_unlock(vm_start_mutex); + +} + +NPError +ITNP_GetValue (NPP instance, NPPVariable variable, void* value) +{ + PLUGIN_DEBUG ("ITNP_GetValue\n"); + + NPError np_error = NPERR_NO_ERROR; + + switch (variable) + { + // This plugin needs XEmbed support. + case NPPVpluginNeedsXEmbed: + { + PLUGIN_DEBUG ("ITNP_GetValue: returning TRUE for NeedsXEmbed.\n"); + bool* bool_value = (bool*) value; + *bool_value = true; + } + break; + case NPPVpluginScriptableNPObject: + { + *(NPObject **)value = get_scriptable_object(instance); + } + break; + default: + PLUGIN_ERROR ("Unknown plugin value requested."); + np_error = NPERR_GENERIC_ERROR; + break; + } + + PLUGIN_DEBUG ("ITNP_GetValue return\n"); + + return np_error; +} + +NPError +ITNP_Destroy (NPP instance, NPSavedData** save) +{ + PLUGIN_DEBUG ("ITNP_Destroy %p\n", instance); + + ITNPPluginData* data = (ITNPPluginData*) instance->pdata; + + int id = get_id_from_instance(instance); + + // Let Java know that this applet needs to be destroyed + gchar* msg = (gchar*) g_malloc(512*sizeof(gchar)); // 512 is more than enough. We need < 100 + g_sprintf(msg, "instance %d destroy", id); + plugin_send_message_to_appletviewer(msg); + g_free(msg); + msg = NULL; + + if (data) + { + // Free plugin data. + plugin_data_destroy (instance); + } + + g_hash_table_remove(instance_to_id_map, instance); + g_hash_table_remove(id_to_instance_map, GINT_TO_POINTER(id)); + + IcedTeaPluginUtilities::invalidateInstance(instance); + + PLUGIN_DEBUG ("ITNP_Destroy return\n"); + + return NPERR_NO_ERROR; +} + +NPError +ITNP_SetWindow (NPP instance, NPWindow* window) +{ + PLUGIN_DEBUG ("ITNP_SetWindow\n"); + + if (instance == NULL) + { + PLUGIN_ERROR ("Invalid instance."); + + return NPERR_INVALID_INSTANCE_ERROR; + } + + gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance); + + gint id = 0; + if (id_ptr) + { + id = GPOINTER_TO_INT(id_ptr); + } + + ITNPPluginData* data = (ITNPPluginData*) instance->pdata; + + // Simply return if we receive a NULL window. + if ((window == NULL) || (window->window == NULL)) + { + PLUGIN_DEBUG ("ITNP_SetWindow: got NULL window.\n"); + + return NPERR_NO_ERROR; + } + + if (data->window_handle) + { + // The window already exists. + if (data->window_handle == window->window) + { + // The parent window is the same as in previous calls. + PLUGIN_DEBUG ("ITNP_SetWindow: window already exists.\n"); + + // Critical region. Read data->appletviewer_mutex and send + // a message to the appletviewer. + g_mutex_lock (data->appletviewer_mutex); + + if (jvm_up) + { + gboolean dim_changed = FALSE; + + // The window is the same as it was for the last + // SetWindow call. + if (window->width != data->window_width) + { + PLUGIN_DEBUG ("ITNP_SetWindow: window width changed.\n"); + // The width of the plugin window has changed. + + // Store the new width. + data->window_width = window->width; + dim_changed = TRUE; + } + + if (window->height != data->window_height) + { + PLUGIN_DEBUG ("ITNP_SetWindow: window height changed.\n"); + // The height of the plugin window has changed. + + // Store the new height. + data->window_height = window->height; + + dim_changed = TRUE; + } + + if (dim_changed) { + gchar* message = g_strdup_printf ("instance %d width %d height %d", + id, window->width, window->height); + plugin_send_message_to_appletviewer (message); + g_free (message); + message = NULL; + } + + + } + else + { + // The appletviewer is not running. + PLUGIN_DEBUG ("ITNP_SetWindow: appletviewer is not running.\n"); + } + + g_mutex_unlock (data->appletviewer_mutex); + } + else + { + // The parent window has changed. This branch does run but + // doing nothing in response seems to be sufficient. + PLUGIN_DEBUG ("ITNP_SetWindow: parent window changed.\n"); + } + } + else + { + // Else this is initialization + PLUGIN_DEBUG ("ITNP_SetWindow: setting window.\n"); + + // Critical region. Send messages to appletviewer. + g_mutex_lock (data->appletviewer_mutex); + + // Store the window handle and dimensions + data->window_handle = window->window; + data->window_width = window->width; + data->window_height = window->height; + + // Now we have everything. Send this data to the Java side + + gchar* instance_msg = g_strdup_printf ("instance %s handle %ld width %d height %d %s", + data->instance_id, + (gulong) data->window_handle, + data->window_width, + data->window_height, + data->applet_tag); + + plugin_send_message_to_appletviewer (instance_msg); + + g_free(instance_msg); + instance_msg = NULL; + + g_mutex_unlock (data->appletviewer_mutex); + + } + + PLUGIN_DEBUG ("ITNP_SetWindow return\n"); + + return NPERR_NO_ERROR; +} + +NPError +ITNP_NewStream (NPP instance, NPMIMEType type, NPStream* stream, + NPBool seekable, uint16_t* stype) +{ + PLUGIN_DEBUG ("ITNP_NewStream\n"); + + PLUGIN_DEBUG ("ITNP_NewStream return\n"); + + return NPERR_GENERIC_ERROR; +} + +void +ITNP_StreamAsFile (NPP instance, NPStream* stream, const char* filename) +{ + PLUGIN_DEBUG ("ITNP_StreamAsFile\n"); + + PLUGIN_DEBUG ("ITNP_StreamAsFile return\n"); +} + +NPError +ITNP_DestroyStream (NPP instance, NPStream* stream, NPReason reason) +{ + PLUGIN_DEBUG ("ITNP_DestroyStream\n"); + + PLUGIN_DEBUG ("ITNP_DestroyStream return\n"); + + return NPERR_NO_ERROR; +} + +int32_t +ITNP_WriteReady (NPP instance, NPStream* stream) +{ + PLUGIN_DEBUG ("ITNP_WriteReady\n"); + + PLUGIN_DEBUG ("ITNP_WriteReady return\n"); + + return 0; +} + +int32_t +ITNP_Write (NPP instance, NPStream* stream, int32_t offset, int32_t len, + void* buffer) +{ + PLUGIN_DEBUG ("ITNP_Write\n"); + + PLUGIN_DEBUG ("ITNP_Write return\n"); + + return 0; +} + +void +ITNP_Print (NPP instance, NPPrint* platformPrint) +{ + PLUGIN_DEBUG ("ITNP_Print\n"); + + PLUGIN_DEBUG ("ITNP_Print return\n"); +} + +int16_t +ITNP_HandleEvent (NPP instance, void* event) +{ + PLUGIN_DEBUG ("ITNP_HandleEvent\n"); + + PLUGIN_DEBUG ("ITNP_HandleEvent return\n"); + + return 0; +} + +void +ITNP_URLNotify (NPP instance, const char* url, NPReason reason, + void* notifyData) +{ + PLUGIN_DEBUG ("ITNP_URLNotify\n"); + + PLUGIN_DEBUG ("ITNP_URLNotify return\n"); +} + +NPError +get_cookie_info(const char* siteAddr, char** cookieString, uint32_t* len) +{ +#if MOZILLA_VERSION_COLLAPSED < 1090100 + nsresult rv; + nsCOMPtr<nsIScriptSecurityManager> sec_man = + do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); + + if (!sec_man) { + return NPERR_GENERIC_ERROR; + } + + nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv); + + if (NS_FAILED(rv) || !io_svc) { + return NPERR_GENERIC_ERROR; + } + + nsCOMPtr<nsIURI> uri; + io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); + + nsCOMPtr<nsICookieService> cookie_svc = do_GetService("@mozilla.org/cookieService;1", &rv); + + if (NS_FAILED(rv) || !cookie_svc) { + return NPERR_GENERIC_ERROR; + } + + rv = cookie_svc->GetCookieString(uri, NULL, cookieString); + + if (NS_FAILED(rv) || !*cookieString) { + return NPERR_GENERIC_ERROR; + } + +#else + + // getvalueforurl needs an NPP instance. Quite frankly, there is no easy way + // to know which instance needs the information, as applets on Java side can + // be multi-threaded and the thread making a proxy.cookie request cannot be + // easily tracked. + + // Fortunately, XULRunner does not care about the instance as long as it is + // valid. So we just pick the first valid one and use it. Proxy/Cookie + // information is not instance specific anyway, it is URL specific. + + if (browser_functions.getvalueforurl) + { + GHashTableIter iter; + gpointer id, instance; + + g_hash_table_iter_init (&iter, instance_to_id_map); + g_hash_table_iter_next (&iter, &instance, &id); + + return browser_functions.getvalueforurl((NPP) instance, NPNURLVCookie, siteAddr, cookieString, len); + } else + { + return NPERR_GENERIC_ERROR; + } + +#endif + + return NPERR_NO_ERROR; +} + +// HELPER FUNCTIONS + +static void +plugin_data_new (ITNPPluginData** data) +{ + PLUGIN_DEBUG ("plugin_data_new\n"); + + *data = (ITNPPluginData*) + (*browser_functions.memalloc) (sizeof (struct ITNPPluginData)); + + // appletviewer_alive is false until the applet viewer is spawned. + if (*data) + memset (*data, 0, sizeof (struct ITNPPluginData)); + + PLUGIN_DEBUG ("plugin_data_new return\n"); +} + + + +// Documentbase retrieval. This function gets the current document's +// documentbase. This function relies on browser-private data so it +// will only work when the plugin is loaded in a Mozilla-based +// browser. We could not find a way to retrieve the documentbase +// using the original Netscape plugin API so we use the XPCOM API +// instead. +#if MOZILLA_VERSION_COLLAPSED < 1090100 +static gchar* +plugin_get_documentbase (NPP instance) +{ + PLUGIN_DEBUG ("plugin_get_documentbase\n"); + + nsIPluginInstance* xpcom_instance = NULL; + nsIPluginInstancePeer* peer = NULL; + nsresult result = 0; + nsIPluginTagInfo2* pluginTagInfo2 = NULL; + info_union u = { NULL }; + char const* documentbase = NULL; + gchar* documentbase_copy = NULL; + + xpcom_instance = (nsIPluginInstance*) (instance->ndata); + if (!xpcom_instance) + { + PLUGIN_ERROR ("xpcom_instance is NULL."); + goto cleanup_done; + } + + xpcom_instance->GetPeer (&peer); + if (!peer) + { + PLUGIN_ERROR ("peer is NULL."); + goto cleanup_done; + } + + u.info_field = &pluginTagInfo2; + + result = peer->QueryInterface (kIPluginTagInfo2IID, + u.void_field); + if (result || !pluginTagInfo2) + { + PLUGIN_ERROR ("pluginTagInfo2 retrieval failed."); + goto cleanup_peer; + } + + pluginTagInfo2->GetDocumentBase (&documentbase); + + if (!documentbase) + { + // NULL => dummy instantiation for LiveConnect + goto cleanup_plugintaginfo2; + } + + documentbase_copy = g_strdup (documentbase); + + // Release references. + cleanup_plugintaginfo2: + NS_RELEASE (pluginTagInfo2); + + cleanup_peer: + NS_RELEASE (peer); + + cleanup_done: + PLUGIN_DEBUG ("plugin_get_documentbase return\n"); + + PLUGIN_DEBUG("plugin_get_documentbase returning: %s\n", documentbase_copy); + return documentbase_copy; +} +#else +static gchar* +plugin_get_documentbase (NPP instance) +{ + PLUGIN_DEBUG ("plugin_get_documentbase\n"); + + char const* documentbase = NULL; + gchar* documentbase_copy = NULL; + + // FIXME: This method is not ideal, but there are no known NPAPI call + // for this. See thread for more information: + // http://www.mail-archive.com/[email protected]/msg04844.html + + // Additionally, since it is insecure, we cannot use it for making + // security decisions. + NPObject* window; + browser_functions.getvalue(instance, NPNVWindowNPObject, &window); + + NPVariant location; + NPIdentifier location_id = browser_functions.getstringidentifier("location"); + browser_functions.getproperty(instance, window, location_id, &location); + + NPVariant href; + NPIdentifier href_id = browser_functions.getstringidentifier("href"); + browser_functions.getproperty(instance, NPVARIANT_TO_OBJECT(location), + href_id, &href); + + // Strip everything after the last "/" +#if MOZILLA_VERSION_COLLAPSED < 1090200 + gchar** parts = g_strsplit (NPVARIANT_TO_STRING(href).utf8characters, "/", -1); +#else + gchar** parts = g_strsplit (NPVARIANT_TO_STRING(href).UTF8Characters, "/", -1); +#endif + guint parts_sz = g_strv_length (parts); + + std::string location_str; + for (int i=0; i < parts_sz - 1; i++) + { + location_str += parts[i]; + location_str += "/"; + } + + documentbase_copy = g_strdup (location_str.c_str()); + + // Release references. + browser_functions.releasevariantvalue(&href); + browser_functions.releasevariantvalue(&location); + cleanup_done: + PLUGIN_DEBUG ("plugin_get_documentbase return\n"); + PLUGIN_DEBUG("plugin_get_documentbase returning: %s\n", documentbase_copy); + + return documentbase_copy; +} +#endif + +// This function displays an error message if the appletviewer has not +// been installed correctly. +static void +plugin_display_failure_dialog () +{ + GtkWidget* dialog = NULL; + + PLUGIN_DEBUG ("plugin_display_failure_dialog\n"); + + dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + FAILURE_MESSAGE, + appletviewer_executable); + gtk_widget_show_all (dialog); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + PLUGIN_DEBUG ("plugin_display_failure_dialog return\n"); +} + + + +// plugin_in_pipe_callback is called when data is available on the +// input pipe, or when the appletviewer crashes or is killed. It may +// be called after data has been destroyed in which case it simply +// returns FALSE to remove itself from the glib main loop. +static gboolean +plugin_in_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data) +{ + PLUGIN_DEBUG ("plugin_in_pipe_callback\n"); + + gboolean keep_installed = TRUE; + + if (condition & G_IO_IN) + { + gchar* message = NULL; + + if (g_io_channel_read_line (in_from_appletviewer, + &message, NULL, NULL, + &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to read line from input channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to read line from input channel"); + } else + { + consume_message(message); + } + + g_free (message); + message = NULL; + + keep_installed = TRUE; + } + + if (condition & (G_IO_ERR | G_IO_HUP)) + { + PLUGIN_DEBUG ("appletviewer has stopped.\n"); + keep_installed = FALSE; + } + + PLUGIN_DEBUG ("plugin_in_pipe_callback return\n"); + + return keep_installed; +} + +void consume_message(gchar* message) { + + PLUGIN_DEBUG (" PIPE: plugin read: %s\n", message); + + if (g_str_has_prefix (message, "instance")) + { + + ITNPPluginData* data; + gchar** parts = g_strsplit (message, " ", -1); + guint parts_sz = g_strv_length (parts); + + int instance_id = atoi(parts[1]); + NPP instance = (NPP) g_hash_table_lookup(id_to_instance_map, + GINT_TO_POINTER(instance_id)); + + if (instance_id > 0 && !instance) + { + PLUGIN_DEBUG("Instance %d is not active. Refusing to consume message \"%s\"\n", instance_id, message); + return; + } + else if (instance) + { + data = (ITNPPluginData*) instance->pdata; + } + + if (g_str_has_prefix (parts[2], "url")) + { + // Open the URL in a new browser window. + gchar* decoded_url = (gchar*) calloc(strlen(parts[3]) + 1, sizeof(gchar)); + IcedTeaPluginUtilities::decodeURL(parts[3], &decoded_url); + + PLUGIN_DEBUG ("plugin_in_pipe_callback: opening URL %s\n", decoded_url); + PLUGIN_DEBUG ("plugin_in_pipe_callback: URL target %s\n", parts[4]); + + NPError np_error = + (*browser_functions.geturl) (data->owner, decoded_url, parts[4]); + + + if (np_error != NPERR_NO_ERROR) + PLUGIN_ERROR ("Failed to load URL."); + + g_free(decoded_url); + decoded_url = NULL; + } + else if (g_str_has_prefix (parts[2], "status")) + { + + // clear the "instance X status" parts + sprintf(parts[0], ""); + sprintf(parts[1], ""); + sprintf(parts[2], ""); + + // join the rest + gchar* status_message = g_strjoinv(" ", parts); + PLUGIN_DEBUG ("plugin_in_pipe_callback: setting status %s\n", status_message); + (*browser_functions.status) (data->owner, status_message); + + g_free(status_message); + status_message = NULL; + } + else if (g_str_has_prefix (parts[1], "internal")) + { + //s->post(message); + } + else + { + // All other messages are posted to the bus, and subscribers are + // expected to take care of them. They better! + + java_to_plugin_bus->post(message); + } + + g_strfreev (parts); + parts = NULL; + } + else if (g_str_has_prefix (message, "context")) + { + java_to_plugin_bus->post(message); + } + else if (g_str_has_prefix (message, "plugin ")) + { + // internal plugin related message + gchar** parts = g_strsplit (message, " ", 5); + if (g_str_has_prefix(parts[1], "PluginProxyInfo")) + { + gchar* proxy; + uint32_t len; + + gchar* decoded_url = (gchar*) calloc(strlen(parts[4]) + 1, sizeof(gchar)); + IcedTeaPluginUtilities::decodeURL(parts[4], &decoded_url); + 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); + + gchar* proxy_info; + +#if MOZILLA_VERSION_COLLAPSED < 1090100 + proxy = (char*) malloc(sizeof(char)*2048); +#endif + + proxy_info = g_strconcat ("plugin PluginProxyInfo reference ", parts[3], " ", NULL); + if (get_proxy_info(decoded_url, &proxy, &len) == NPERR_NO_ERROR) + { + proxy_info = g_strconcat (proxy_info, proxy, NULL); + } + + PLUGIN_DEBUG("Proxy info: %s\n", proxy_info); + plugin_send_message_to_appletviewer(proxy_info); + + g_free(decoded_url); + decoded_url = NULL; + g_free(proxy_info); + proxy_info = NULL; + +#if MOZILLA_VERSION_COLLAPSED < 1090100 + g_free(proxy); + proxy = NULL; +#endif + + } else if (g_str_has_prefix(parts[1], "PluginCookieInfo")) + { + gchar* decoded_url = (gchar*) calloc(strlen(parts[4])+1, sizeof(gchar)); + IcedTeaPluginUtilities::decodeURL(parts[4], &decoded_url); + + gchar* cookie_info = g_strconcat ("plugin PluginCookieInfo reference ", parts[3], " ", NULL); + gchar* cookie_string; + uint32_t len; + if (get_cookie_info(decoded_url, &cookie_string, &len) == NPERR_NO_ERROR) + { + cookie_info = g_strconcat (cookie_info, cookie_string, NULL); + } + + PLUGIN_DEBUG("Cookie info: %s\n", cookie_info); + plugin_send_message_to_appletviewer(cookie_info); + + g_free(decoded_url); + decoded_url = NULL; + g_free(cookie_info); + cookie_info = NULL; + } + } + else + { + g_print (" Unable to handle message: %s\n", message); + } +} + +void get_instance_from_id(int id, NPP& instance) +{ + instance = (NPP) g_hash_table_lookup(id_to_instance_map, + GINT_TO_POINTER(id)); +} + +int get_id_from_instance(NPP instance) +{ + int id = GPOINTER_TO_INT(g_hash_table_lookup(instance_to_id_map, + instance)); + PLUGIN_DEBUG("Returning id %d for instance %p\n", id, instance); + return id; +} + +NPError +get_proxy_info(const char* siteAddr, char** proxy, uint32_t* len) +{ +#if MOZILLA_VERSION_COLLAPSED < 1090100 + nsresult rv; + + // Initialize service variables + nsCOMPtr<nsIProtocolProxyService> proxy_svc = do_GetService("@mozilla.org/network/protocol-proxy-service;1", &rv); + + if (!proxy_svc) { + printf("Cannot initialize proxy service\n"); + return NPERR_GENERIC_ERROR; + } + + nsCOMPtr<nsIIOService> io_svc = do_GetService("@mozilla.org/network/io-service;1", &rv); + + if (NS_FAILED(rv) || !io_svc) { + printf("Cannot initialize io service\n"); + return NPERR_GENERIC_ERROR; + } + + // uri which needs to be accessed + nsCOMPtr<nsIURI> uri; + io_svc->NewURI(nsCString(siteAddr), NULL, NULL, getter_AddRefs(uri)); + + // find the proxy address if any + nsCOMPtr<nsIProxyInfo> info; + proxy_svc->Resolve(uri, 0, getter_AddRefs(info)); + + // if there is no proxy found, return immediately + if (!info) { + PLUGIN_DEBUG("%s does not need a proxy\n", siteAddr); + return NPERR_GENERIC_ERROR; + } + + // if proxy info is available, extract it + nsCString phost; + PRInt32 pport; + nsCString ptype; + + info->GetHost(phost); + info->GetPort(&pport); + info->GetType(ptype); + + // resolve the proxy address to an IP + nsCOMPtr<nsIDNSService> dns_svc = do_GetService("@mozilla.org/network/dns-service;1", &rv); + + if (!dns_svc) { + printf("Cannot initialize DNS service\n"); + return NPERR_GENERIC_ERROR; + } + + nsCOMPtr<nsIDNSRecord> record; + dns_svc->Resolve(phost, 0U, getter_AddRefs(record)); + + // TODO: Add support for multiple ips + nsDependentCString ipAddr; + record->GetNextAddrAsString(ipAddr); + + if (!strcmp(ptype.get(), "http")) + { + snprintf(*proxy, sizeof(char)*1024, "%s %s:%d", "PROXY", ipAddr.get(), pport); + } else + { + snprintf(*proxy, sizeof(char)*1024, "%s %s:%d", "SOCKS", ipAddr.get(), pport); + } + + *len = strlen(*proxy); + + PLUGIN_DEBUG("Proxy info for %s: %s\n", siteAddr, *proxy); + +#else + + if (browser_functions.getvalueforurl) + { + + // As in get_cookie_info, we use the first active instance + GHashTableIter iter; + gpointer id, instance; + + g_hash_table_iter_init (&iter, instance_to_id_map); + g_hash_table_iter_next (&iter, &instance, &id); + + browser_functions.getvalueforurl((NPP) instance, NPNURLVProxy, siteAddr, proxy, len); + } else + { + return NPERR_GENERIC_ERROR; + } +#endif + + return NPERR_NO_ERROR; +} + +// plugin_out_pipe_callback is called when the appletviewer crashes or +// is killed. It may be called after data has been destroyed in which +// case it simply returns FALSE to remove itself from the glib main +// loop. +static gboolean +plugin_out_pipe_callback (GIOChannel* source, + GIOCondition condition, + gpointer plugin_data) +{ + PLUGIN_DEBUG ("plugin_out_pipe_callback\n"); + + ITNPPluginData* data = (ITNPPluginData*) plugin_data; + + PLUGIN_DEBUG ("plugin_out_pipe_callback: appletviewer has stopped.\n"); + + PLUGIN_DEBUG ("plugin_out_pipe_callback return\n"); + + return FALSE; +} + +// remove all components from LD_LIBRARY_PATH, which start with +// MOZILLA_FIVE_HOME; firefox has its own NSS based security provider, +// which conflicts with the one configured in nss.cfg. +static gchar* +plugin_filter_ld_library_path(gchar *path_old) +{ + gchar *moz_home = g_strdup (g_getenv ("MOZILLA_FIVE_HOME")); + gchar *moz_prefix; + gchar *path_new; + gchar** components; + int i1, i2; + + if (moz_home == NULL || path_old == NULL || strlen (path_old) == 0) + return path_old; + if (g_str_has_suffix (moz_home, "/")) + moz_home[strlen (moz_home - 1)] = '\0'; + moz_prefix = g_strconcat (moz_home, "/", NULL); + + components = g_strsplit (path_old, ":", -1); + for (i1 = 0, i2 = 0; components[i1] != NULL; i1++) + { + if (g_strcmp0 (components[i1], moz_home) == 0 + || g_str_has_prefix (components[i1], moz_home)) + components[i2] = components[i1]; + else + components[i2++] = components[i1]; + } + components[i2] = NULL; + + if (i1 > i2) + path_new = g_strjoinv (":", components); + g_strfreev (components); + g_free (moz_home); + g_free (moz_prefix); + g_free (path_old); + + if (path_new == NULL || strlen (path_new) == 0) + { + PLUGIN_DEBUG("Unset LD_LIBRARY_PATH\n"); + return NULL; + } + else + { + PLUGIN_DEBUG ("Set LD_LIBRARY_PATH: %s\n", path_new); + return path_new; + } +} + +// build the environment to pass to the external plugin process +static gchar** +plugin_filter_environment(void) +{ + gchar **var_names = g_listenv(); + gchar **new_env = (gchar**) malloc(sizeof(gchar*) * (g_strv_length (var_names) + 1)); + int i_var, i_env; + + for (i_var = 0, i_env = 0; var_names[i_var] != NULL; i_var++) + { + gchar *env_value = g_strdup (g_getenv (var_names[i_var])); + + if (g_str_has_prefix (var_names[i_var], "LD_LIBRARY_PATH")) + env_value = plugin_filter_ld_library_path (env_value); + if (env_value != NULL) + { + new_env[i_env++] = g_strdup_printf ("%s=%s", var_names[i_var], env_value); + g_free (env_value); + } + } + new_env[i_env] = NULL; + return new_env; +} + +static NPError +plugin_test_appletviewer () +{ + PLUGIN_DEBUG ("plugin_test_appletviewer: %s\n", appletviewer_executable); + NPError error = NPERR_NO_ERROR; + + gchar* command_line[3] = { NULL, NULL, NULL }; + gchar** environment; + + command_line[0] = g_strdup (appletviewer_executable); + command_line[1] = g_strdup("-version"); + command_line[2] = NULL; + + environment = plugin_filter_environment(); + + if (!g_spawn_async (NULL, command_line, environment, + (GSpawnFlags) 0, + NULL, NULL, NULL, &channel_error)) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to spawn applet viewer", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to spawn applet viewer"); + error = NPERR_GENERIC_ERROR; + } + + g_strfreev (environment); + + g_free (command_line[0]); + command_line[0] = NULL; + g_free (command_line[1]); + command_line[1] = NULL; + g_free (command_line[2]); + command_line[2] = NULL; + + PLUGIN_DEBUG ("plugin_test_appletviewer return\n"); + return error; +} + +static NPError +plugin_start_appletviewer (ITNPPluginData* data) +{ + PLUGIN_DEBUG ("plugin_start_appletviewer\n"); + NPError error = NPERR_NO_ERROR; + + gchar** command_line; + gchar** environment; + + if (plugin_debug) + { + command_line = (gchar**) malloc(sizeof(gchar*)*8); + command_line[0] = g_strdup(appletviewer_executable); + command_line[1] = g_strdup("-Xdebug"); + command_line[2] = g_strdup("-Xnoagent"); + command_line[3] = g_strdup("-Xrunjdwp:transport=dt_socket,address=8787,server=y,suspend=n"); + command_line[4] = g_strdup("sun.applet.PluginMain"); + command_line[5] = g_strdup(out_pipe_name); + command_line[6] = g_strdup(in_pipe_name); + command_line[7] = NULL; + } else + { + command_line = (gchar**) malloc(sizeof(gchar*)*5); + command_line[0] = g_strdup(appletviewer_executable); + command_line[1] = g_strdup("sun.applet.PluginMain"); + command_line[2] = g_strdup(out_pipe_name); + command_line[3] = g_strdup(in_pipe_name); + command_line[4] = NULL; + } + + environment = plugin_filter_environment(); + + if (!g_spawn_async (NULL, command_line, environment, + (GSpawnFlags) G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &appletviewer_pid, &channel_error)) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to spawn applet viewer", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to spawn applet viewer"); + error = NPERR_GENERIC_ERROR; + } + + g_strfreev (environment); + + g_free (command_line[0]); + command_line[0] = NULL; + g_free (command_line[1]); + command_line[1] = NULL; + + if (plugin_debug) + { + g_free (command_line[2]); + command_line[2] = NULL; + g_free (command_line[3]); + command_line[3] = NULL; + g_free (command_line[4]); + command_line[4] = NULL; + g_free (command_line[5]); + command_line[5] = NULL; + } + + g_free(command_line); + command_line = NULL; + + if (appletviewer_pid) + { + PLUGIN_DEBUG("Initialized VM with pid=%d\n", appletviewer_pid); + appletviewer_watch_id = g_child_watch_add(appletviewer_pid, (GChildWatchFunc) appletviewer_monitor, (gpointer) appletviewer_pid); + } + + + PLUGIN_DEBUG ("plugin_start_appletviewer return\n"); + return error; +} + +// Build up the applet tag string that we'll send to the applet +// viewer. +static gchar* +plugin_create_applet_tag (int16_t argc, char* argn[], char* argv[]) +{ + PLUGIN_DEBUG ("plugin_create_applet_tag\n"); + + gchar* applet_tag = g_strdup ("<EMBED "); + gchar* parameters = g_strdup (""); + + for (int16_t i = 0; i < argc; i++) + { + if (!g_ascii_strcasecmp (argn[i], "code")) + { + gchar* code = g_strdup_printf ("CODE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, code, NULL); + g_free (code); + code = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "java_code")) + { + gchar* java_code = g_strdup_printf ("JAVA_CODE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, java_code, NULL); + g_free (java_code); + java_code = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "codebase")) + { + gchar* codebase = g_strdup_printf ("CODEBASE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, codebase, NULL); + g_free (codebase); + codebase = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "java_codebase")) + { + gchar* java_codebase = g_strdup_printf ("JAVA_CODEBASE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, java_codebase, NULL); + g_free (java_codebase); + java_codebase = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "classid")) + { + gchar* classid = g_strdup_printf ("CLASSID=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, classid, NULL); + g_free (classid); + classid = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "archive")) + { + gchar* archive = g_strdup_printf ("ARCHIVE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, archive, NULL); + g_free (archive); + archive = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "java_archive")) + { + gchar* java_archive = g_strdup_printf ("JAVA_ARCHIVE=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, java_archive, NULL); + g_free (java_archive); + java_archive = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "width")) + { + gchar* width = g_strdup_printf ("width=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, width, NULL); + g_free (width); + width = NULL; + } + else if (!g_ascii_strcasecmp (argn[i], "height")) + { + gchar* height = g_strdup_printf ("height=\"%s\" ", argv[i]); + applet_tag = g_strconcat (applet_tag, height, NULL); + g_free (height); + height = NULL; + } + else + { + // Escape the parameter value so that line termination + // characters will pass through the pipe. + if (argv[i] != '\0') + { + // worst case scenario -> all characters are newlines or + // returns, each of which translates to 5 substitutions + char* escaped = (char*) calloc(((strlen(argv[i])*5)+1), sizeof(char)); + + strcpy(escaped, ""); + for (int j=0; j < strlen(argv[i]); j++) + { + if (argv[i][j] == '\r') + strcat(escaped, " "); + else if (argv[i][j] == '\n') + strcat(escaped, " "); + else if (argv[i][j] == '>') + strcat(escaped, ">"); + else if (argv[i][j] == '<') + strcat(escaped, "<"); + else if (argv[i][j] == '&') + strcat(escaped, "&"); + else + { + char* orig_char = (char*) calloc(2, sizeof(char)); + orig_char[0] = argv[i][j]; + orig_char[1] = '\0'; + + strcat(escaped, orig_char); + + free(orig_char); + orig_char = NULL; + } + } + + parameters = g_strconcat (parameters, "<PARAM NAME=\"", argn[i], + "\" VALUE=\"", escaped, "\">", NULL); + + free (escaped); + escaped = NULL; + } + } + } + + applet_tag = g_strconcat (applet_tag, ">", parameters, "</EMBED>", NULL); + + g_free (parameters); + parameters = NULL; + + PLUGIN_DEBUG ("plugin_create_applet_tag return\n"); + + return applet_tag; +} + +// plugin_send_message_to_appletviewer must be called while holding +// data->appletviewer_mutex. +void +plugin_send_message_to_appletviewer (gchar const* message) +{ + PLUGIN_DEBUG ("plugin_send_message_to_appletviewer\n"); + + if (jvm_up) + { + gchar* newline_message = NULL; + gsize bytes_written = 0; + + // Send message to appletviewer. + newline_message = g_strdup_printf ("%s\n", message); + + // g_io_channel_write_chars will return something other than + // G_IO_STATUS_NORMAL if not all the data is written. In that + // case we fail rather than retrying. + if (g_io_channel_write_chars (out_to_appletviewer, + newline_message, -1, &bytes_written, + &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to write bytes to output channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to write bytes to output channel"); + } + + if (g_io_channel_flush (out_to_appletviewer, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to flush bytes to output channel", + channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to flush bytes to output channel"); + } + g_free (newline_message); + newline_message = NULL; + + PLUGIN_DEBUG (" PIPE: plugin wrote: %s\n", message); + } + + PLUGIN_DEBUG ("plugin_send_message_to_appletviewer return\n"); +} + +// Stop the appletviewer process. When this is called the +// appletviewer can be in any of three states: running, crashed or +// hung. If the appletviewer is running then sending it "shutdown" +// will cause it to exit. This will cause +// plugin_out_pipe_callback/plugin_in_pipe_callback to be called and +// the input and output channels to be shut down. If the appletviewer +// has crashed then plugin_out_pipe_callback/plugin_in_pipe_callback +// would already have been called and data->appletviewer_alive cleared +// in which case this function simply returns. If the appletviewer is +// hung then this function will be successful and the input and output +// watches will be removed by plugin_data_destroy. +// plugin_stop_appletviewer must be called with +// data->appletviewer_mutex held. +static void +plugin_stop_appletviewer () +{ + PLUGIN_DEBUG ("plugin_stop_appletviewer\n"); + + if (jvm_up) + { + // Shut down the appletviewer. + gsize bytes_written = 0; + + if (out_to_appletviewer) + { + if (g_io_channel_write_chars (out_to_appletviewer, "shutdown", + -1, &bytes_written, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to write shutdown message to" + " appletviewer", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to write shutdown message to"); + } + + if (g_io_channel_flush (out_to_appletviewer, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to write shutdown message to" + " appletviewer", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to write shutdown message to"); + } + + if (g_io_channel_shutdown (out_to_appletviewer, + TRUE, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to shut down appletviewer" + " output channel", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to shut down appletviewer"); + } + } + + if (in_from_appletviewer) + { + if (g_io_channel_shutdown (in_from_appletviewer, + TRUE, &channel_error) + != G_IO_STATUS_NORMAL) + { + if (channel_error) + { + PLUGIN_ERROR_TWO ("Failed to shut down appletviewer" + " input channel", channel_error->message); + g_error_free (channel_error); + channel_error = NULL; + } + else + PLUGIN_ERROR ("Failed to shut down appletviewer"); + } + } + } + + jvm_up = FALSE; + sleep(2); /* Needed to prevent crashes during debug (when JDWP port is not freed by the kernel right away) */ + + PLUGIN_DEBUG ("plugin_stop_appletviewer return\n"); +} + +static void appletviewer_monitor(GPid pid, gint status, gpointer data) +{ + PLUGIN_DEBUG ("appletviewer_monitor\n"); + jvm_up = FALSE; + pid = -1; + PLUGIN_DEBUG ("appletviewer_monitor return\n"); +} + +static void +plugin_data_destroy (NPP instance) +{ + PLUGIN_DEBUG ("plugin_data_destroy\n"); + + ITNPPluginData* tofree = (ITNPPluginData*) instance->pdata; + + // Remove instance from map + gpointer id_ptr = g_hash_table_lookup(instance_to_id_map, instance); + + if (id_ptr) + { + gint id = GPOINTER_TO_INT(id_ptr); + g_hash_table_remove(instance_to_id_map, instance); + g_hash_table_remove(id_to_instance_map, id_ptr); + } + + tofree->window_handle = NULL; + tofree->window_height = 0; + tofree->window_width = 0; + + // cleanup_appletviewer_mutex: + g_free (tofree->appletviewer_mutex); + tofree->appletviewer_mutex = NULL; + + // cleanup_instance_string: + g_free (tofree->instance_id); + tofree->instance_id = NULL; + + // cleanup applet tag + g_free (tofree->applet_tag); + tofree->applet_tag = NULL; + + g_free(tofree->source); + tofree->source = NULL; + + // cleanup_data: + // Eliminate back-pointer to plugin instance. + tofree->owner = NULL; + (*browser_functions.memfree) (tofree); + tofree = NULL; + + PLUGIN_DEBUG ("plugin_data_destroy return\n"); +} + +// FACTORY FUNCTIONS + +// Provides the browser with pointers to the plugin functions that we +// implement and initializes a local table with browser functions that +// we may wish to call. Called once, after browser startup and before +// the first plugin instance is created. +// The field 'initialized' is set to true once this function has +// finished. If 'initialized' is already true at the beginning of +// this function, then it is evident that NP_Initialize has already +// been called. There is no need to call this function more than once and +// this workaround avoids any duplicate calls. +NPError +NP_Initialize (NPNetscapeFuncs* browserTable, NPPluginFuncs* pluginTable) +{ + PLUGIN_DEBUG ("NP_Initialize\n"); + + if (initialized) + return NPERR_NO_ERROR; + else if ((browserTable == NULL) || (pluginTable == NULL)) + { + PLUGIN_ERROR ("Browser or plugin function table is NULL."); + + return NPERR_INVALID_FUNCTABLE_ERROR; + } + + // Ensure that the major version of the plugin API that the browser + // expects is not more recent than the major version of the API that + // we've implemented. + if ((browserTable->version >> 8) > NP_VERSION_MAJOR) + { + PLUGIN_ERROR ("Incompatible version."); + + return NPERR_INCOMPATIBLE_VERSION_ERROR; + } + + // Ensure that the plugin function table we've received is large + // enough to store the number of functions that we may provide. + if (pluginTable->size < sizeof (NPPluginFuncs)) + { + PLUGIN_ERROR ("Invalid plugin function table."); + + return NPERR_INVALID_FUNCTABLE_ERROR; + } + + // Ensure that the browser function table is large enough to store + // the number of browser functions that we may use. + if (browserTable->size < sizeof (NPNetscapeFuncs)) + { + fprintf (stderr, "ERROR: Invalid browser function table. Some functionality may be restricted.\n"); + } + + // Store in a local table the browser functions that we may use. + browser_functions.size = browserTable->size; + browser_functions.version = browserTable->version; + browser_functions.geturlnotify = browserTable->geturlnotify; + browser_functions.geturl = browserTable->geturl; + browser_functions.posturlnotify = browserTable->posturlnotify; + browser_functions.posturl = browserTable->posturl; + browser_functions.requestread = browserTable->requestread; + browser_functions.newstream = browserTable->newstream; + browser_functions.write = browserTable->write; + browser_functions.destroystream = browserTable->destroystream; + browser_functions.status = browserTable->status; + browser_functions.uagent = browserTable->uagent; + browser_functions.memalloc = browserTable->memalloc; + browser_functions.memfree = browserTable->memfree; + browser_functions.memflush = browserTable->memflush; + browser_functions.reloadplugins = browserTable->reloadplugins; + browser_functions.getJavaEnv = browserTable->getJavaEnv; + browser_functions.getJavaPeer = browserTable->getJavaPeer; + browser_functions.getvalue = browserTable->getvalue; + browser_functions.setvalue = browserTable->setvalue; + browser_functions.invalidaterect = browserTable->invalidaterect; + browser_functions.invalidateregion = browserTable->invalidateregion; + browser_functions.forceredraw = browserTable->forceredraw; + browser_functions.getstringidentifier = browserTable->getstringidentifier; + browser_functions.getstringidentifiers = browserTable->getstringidentifiers; + browser_functions.getintidentifier = browserTable->getintidentifier; + browser_functions.identifierisstring = browserTable->identifierisstring; + browser_functions.utf8fromidentifier = browserTable->utf8fromidentifier; + browser_functions.intfromidentifier = browserTable->intfromidentifier; + browser_functions.createobject = browserTable->createobject; + browser_functions.retainobject = browserTable->retainobject; + browser_functions.releaseobject = browserTable->releaseobject; + browser_functions.invoke = browserTable->invoke; + browser_functions.invokeDefault = browserTable->invokeDefault; + browser_functions.evaluate = browserTable->evaluate; + browser_functions.getproperty = browserTable->getproperty; + browser_functions.setproperty = browserTable->setproperty; + browser_functions.removeproperty = browserTable->removeproperty; + browser_functions.hasproperty = browserTable->hasproperty; + browser_functions.hasmethod = browserTable->hasmethod; + browser_functions.releasevariantvalue = browserTable->releasevariantvalue; + browser_functions.setexception = browserTable->setexception; + browser_functions.pluginthreadasynccall = browserTable->pluginthreadasynccall; +#if MOZILLA_VERSION_COLLAPSED >= 1090100 + browser_functions.getvalueforurl = browserTable->getvalueforurl; + browser_functions.setvalueforurl = browserTable->setvalueforurl; +#endif + + // Return to the browser the plugin functions that we implement. + pluginTable->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; + pluginTable->size = sizeof (NPPluginFuncs); + +#if MOZILLA_VERSION_COLLAPSED < 1090100 + pluginTable->newp = NewNPP_NewProc (ITNP_New); + pluginTable->destroy = NewNPP_DestroyProc (ITNP_Destroy); + pluginTable->setwindow = NewNPP_SetWindowProc (ITNP_SetWindow); + pluginTable->newstream = NewNPP_NewStreamProc (ITNP_NewStream); + pluginTable->destroystream = NewNPP_DestroyStreamProc (ITNP_DestroyStream); + pluginTable->asfile = NewNPP_StreamAsFileProc (ITNP_StreamAsFile); + pluginTable->writeready = NewNPP_WriteReadyProc (ITNP_WriteReady); + pluginTable->write = NewNPP_WriteProc (ITNP_Write); + pluginTable->print = NewNPP_PrintProc (ITNP_Print); + pluginTable->urlnotify = NewNPP_URLNotifyProc (ITNP_URLNotify); + pluginTable->getvalue = NewNPP_GetValueProc (ITNP_GetValue); +#else + pluginTable->newp = NPP_NewProcPtr (ITNP_New); + pluginTable->destroy = NPP_DestroyProcPtr (ITNP_Destroy); + pluginTable->setwindow = NPP_SetWindowProcPtr (ITNP_SetWindow); + pluginTable->newstream = NPP_NewStreamProcPtr (ITNP_NewStream); + pluginTable->destroystream = NPP_DestroyStreamProcPtr (ITNP_DestroyStream); + pluginTable->asfile = NPP_StreamAsFileProcPtr (ITNP_StreamAsFile); + pluginTable->writeready = NPP_WriteReadyProcPtr (ITNP_WriteReady); + pluginTable->write = NPP_WriteProcPtr (ITNP_Write); + pluginTable->print = NPP_PrintProcPtr (ITNP_Print); + pluginTable->urlnotify = NPP_URLNotifyProcPtr (ITNP_URLNotify); + pluginTable->getvalue = NPP_GetValueProcPtr (ITNP_GetValue); +#endif + + // Make sure the plugin data directory exists, creating it if + // necessary. + data_directory = g_strconcat (P_tmpdir, NULL); + if (!data_directory) + { + PLUGIN_ERROR ("Failed to create data directory name."); + return NPERR_OUT_OF_MEMORY_ERROR; + } + NPError np_error = NPERR_NO_ERROR; + gchar* filename = NULL; + + // If P_tmpdir does not exist, try /tmp directly + + if (!g_file_test (data_directory, + (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) + { + int file_error = 0; + + data_directory = g_strconcat ("/tmp", NULL); + if (!data_directory) + { + PLUGIN_ERROR ("Failed to create data directory name."); + return NPERR_OUT_OF_MEMORY_ERROR; + } + + } + + data_directory = g_strconcat (data_directory, "/icedteaplugin-", getenv("USER"), NULL); + + if (!data_directory) + { + PLUGIN_ERROR ("Failed to create data directory name."); + return NPERR_OUT_OF_MEMORY_ERROR; + } + + // Now create a icedteaplugin subdir + if (!g_file_test (data_directory, + (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) + { + int file_error = 0; + + file_error = g_mkdir (data_directory, 0700); + if (file_error != 0) + { + PLUGIN_ERROR_THREE ("Failed to create data directory", + data_directory, + strerror (errno)); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_data_directory; + } + } + + + // If data directory doesn't exit by this point, bail + if (!g_file_test (data_directory, + (GFileTest) (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) + { + PLUGIN_ERROR_THREE ("Temp directory does not exist: ", + data_directory, + strerror (errno)); + + np_error = NPERR_GENERIC_ERROR; + goto cleanup_data_directory; + + } + + // Set appletviewer_executable. + Dl_info info; + int filename_size; + if (dladdr ((const void*) ITNP_New, &info) == 0) + { + PLUGIN_ERROR_TWO ("Failed to determine plugin shared object filename", + dlerror ()); + np_error = NPERR_GENERIC_ERROR; + goto cleanup_data_directory; + } + filename = (gchar*) malloc(sizeof(gchar)*1024); + filename_size = readlink(info.dli_fname, filename, 1023); + if (filename_size >= 0) + { + filename[filename_size] = '\0'; + } + + if (!filename) + { + PLUGIN_ERROR ("Failed to create plugin shared object filename."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_data_directory; + } + + if (filename_size <= 0) + { + free(filename); + filename = g_strdup(info.dli_fname); + } + + appletviewer_executable = g_strdup_printf ("%s/../../bin/java", + dirname (filename)); + PLUGIN_DEBUG(".so is located at: %s and the link points to: %s. Executing java from dir %s to run %s\n", info.dli_fname, filename, dirname (filename), appletviewer_executable); + if (!appletviewer_executable) + { + PLUGIN_ERROR ("Failed to create appletviewer executable name."); + np_error = NPERR_OUT_OF_MEMORY_ERROR; + goto cleanup_filename; + } + + np_error = plugin_test_appletviewer (); + if (np_error != NPERR_NO_ERROR) + { + plugin_display_failure_dialog (); + goto cleanup_appletviewer_executable; + } + g_free (filename); + + initialized = true; + + // Initialize threads (needed for mutexes). + if (!g_thread_supported ()) + g_thread_init (NULL); + + plugin_instance_mutex = g_mutex_new (); + + PLUGIN_DEBUG ("NP_Initialize: using %s\n", appletviewer_executable); + + PLUGIN_DEBUG ("NP_Initialize return\n"); + + plugin_req_proc = new PluginRequestProcessor(); + java_req_proc = new JavaMessageSender(); + + java_to_plugin_bus = new MessageBus(); + plugin_to_java_bus = new MessageBus(); + + java_to_plugin_bus->subscribe(plugin_req_proc); + plugin_to_java_bus->subscribe(java_req_proc); + + pthread_create (&plugin_request_processor_thread1, NULL, &queue_processor, (void*) plugin_req_proc); + pthread_create (&plugin_request_processor_thread2, NULL, &queue_processor, (void*) plugin_req_proc); + pthread_create (&plugin_request_processor_thread3, NULL, &queue_processor, (void*) plugin_req_proc); + + return NPERR_NO_ERROR; + + cleanup_appletviewer_executable: + if (appletviewer_executable) + { + g_free (appletviewer_executable); + appletviewer_executable = NULL; + } + + cleanup_filename: + if (filename) + { + g_free (filename); + filename = NULL; + } + + cleanup_data_directory: + if (data_directory) + { + g_free (data_directory); + data_directory = NULL; + } + + + return np_error; +} + +// Returns a string describing the MIME type that this plugin +// handles. +char* +NP_GetMIMEDescription () +{ + PLUGIN_DEBUG ("NP_GetMIMEDescription\n"); + + PLUGIN_DEBUG ("NP_GetMIMEDescription return\n"); + + return (char*) PLUGIN_MIME_DESC; +} + +// Returns a value relevant to the plugin as a whole. The browser +// calls this function to obtain information about the plugin. +NPError +NP_GetValue (void* future, NPPVariable variable, void* value) +{ + PLUGIN_DEBUG ("NP_GetValue\n"); + + NPError result = NPERR_NO_ERROR; + gchar** char_value = (gchar**) value; + + switch (variable) + { + case NPPVpluginNameString: + PLUGIN_DEBUG ("NP_GetValue: returning plugin name.\n"); + *char_value = g_strdup (PLUGIN_NAME); + break; + + case NPPVpluginDescriptionString: + PLUGIN_DEBUG ("NP_GetValue: returning plugin description.\n"); + *char_value = g_strdup (PLUGIN_DESC); + break; + + default: + PLUGIN_ERROR ("Unknown plugin value requested."); + result = NPERR_GENERIC_ERROR; + break; + } + + PLUGIN_DEBUG ("NP_GetValue return\n"); + + return result; +} + +// Shuts down the plugin. Called after the last plugin instance is +// destroyed. +NPError +NP_Shutdown (void) +{ + PLUGIN_DEBUG ("NP_Shutdown\n"); + + // Free mutex. + if (plugin_instance_mutex) + { + g_mutex_free (plugin_instance_mutex); + plugin_instance_mutex = NULL; + } + + if (data_directory) + { + g_free (data_directory); + data_directory = NULL; + } + + if (appletviewer_executable) + { + g_free (appletviewer_executable); + appletviewer_executable = NULL; + } + + // stop the appletviewer + plugin_stop_appletviewer(); + + // remove monitor + if (appletviewer_watch_id != -1) + g_source_remove(appletviewer_watch_id); + + // Removing a source is harmless if it fails since it just means the + // source has already been removed. + g_source_remove (in_watch_source); + in_watch_source = 0; + + // cleanup_in_from_appletviewer: + if (in_from_appletviewer) + g_io_channel_unref (in_from_appletviewer); + in_from_appletviewer = NULL; + + // cleanup_out_watch_source: + g_source_remove (out_watch_source); + out_watch_source = 0; + + // cleanup_out_to_appletviewer: + if (out_to_appletviewer) + g_io_channel_unref (out_to_appletviewer); + out_to_appletviewer = NULL; + + // cleanup_out_pipe: + // Delete output pipe. + PLUGIN_DEBUG ("NP_Shutdown: deleting output fifo: %s\n", out_pipe_name); + unlink (out_pipe_name); + PLUGIN_DEBUG ("NP_Shutdown: deleted output fifo: %s\n", out_pipe_name); + + // cleanup_out_pipe_name: + g_free (out_pipe_name); + out_pipe_name = NULL; + + // cleanup_in_pipe: + // Delete input pipe. + PLUGIN_DEBUG ("NP_Shutdown: deleting input fifo: %s\n", in_pipe_name); + unlink (in_pipe_name); + PLUGIN_DEBUG ("NP_Shutdown: deleted input fifo: %s\n", in_pipe_name); + + // cleanup_in_pipe_name: + g_free (in_pipe_name); + in_pipe_name = NULL; + + initialized = false; + + pthread_cancel(plugin_request_processor_thread1); + pthread_cancel(plugin_request_processor_thread2); + pthread_cancel(plugin_request_processor_thread3); + + java_to_plugin_bus->unSubscribe(plugin_req_proc); + plugin_to_java_bus->unSubscribe(java_req_proc); + //internal_bus->unSubscribe(java_req_proc); + //internal_bus->unSubscribe(plugin_req_proc); + + delete plugin_req_proc; + delete java_req_proc; + delete java_to_plugin_bus; + delete plugin_to_java_bus; + //delete internal_bus; + + PLUGIN_DEBUG ("NP_Shutdown return\n"); + + return NPERR_NO_ERROR; +} + +NPObject* +get_scriptable_object(NPP instance) +{ + NPObject* obj; + ITNPPluginData* data = (ITNPPluginData*) instance->pdata; + + if (data->is_applet_instance) // dummy instance/package? + { + JavaRequestProcessor java_request = JavaRequestProcessor(); + JavaResultData* java_result; + std::string instance_id = std::string(); + std::string applet_class_id = std::string(); + + int id = get_id_from_instance(instance); + gchar* id_str = g_strdup_printf ("%d", id); + + // Some browsers.. (e.g. chromium) don't call NPP_SetWindow + // for 0x0 plugins and therefore require initialization with + // a 0 handle + if (!data->window_handle) + { + data->window_handle = 0; + gchar *window_message = g_strdup_printf ("instance %s handle %d", + id_str, 0); + plugin_send_message_to_appletviewer (window_message); + g_free (window_message); + } + + java_result = java_request.getAppletObjectInstance(id_str); + + g_free(id_str); + + if (java_result->error_occurred) + { + printf("Error: Unable to fetch applet instance id from Java side.\n"); + return NULL; + } + + instance_id.append(*(java_result->return_string)); + + java_result = java_request.getClassID(instance_id); + + if (java_result->error_occurred) + { + printf("Error: Unable to fetch applet instance id from Java side.\n"); + return NULL; + } + + applet_class_id.append(*(java_result->return_string)); + + obj = IcedTeaScriptableJavaPackageObject::get_scriptable_java_object(instance, applet_class_id, instance_id, false); + + } else + { + obj = IcedTeaScriptablePluginObject::get_scriptable_java_package_object(instance, ""); + } + + return obj; +} + +NPObject* +allocate_scriptable_object(NPP npp, NPClass *aClass) +{ + PLUGIN_DEBUG("Allocating new scriptable object\n"); + return new IcedTeaScriptablePluginObject(npp); +} |