1 /* IcedTeaPluginUtils.h
2 
3    Copyright (C) 2009, 2010  Red Hat
4 
5 This file is part of IcedTea.
6 
7 IcedTea is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11 
12 IcedTea is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with IcedTea; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21 
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26 
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38 
39 /**
40  * Utility classes for the IcedTeaPlugin
41  */
42 
43 #ifndef __ICEDTEAPLUGINUTILS_H__
44 #define __ICEDTEAPLUGINUTILS_H__
45 
46 #include <pthread.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <time.h>
50 #include <syslog.h>
51 #include <sys/time.h>
52 #include <sys/stat.h>
53 
54 #include <fcntl.h>
55 #include <cstring>
56 #include <iostream>
57 #include <list>
58 #include <map>
59 #include <queue>
60 #include <sstream>
61 #include <string>
62 #include <vector>
63 #include <queue>
64 
65 #include <npapi.h>
66 #include <glib.h>
67 #include <npruntime.h>
68 
69 #include "IcedTeaParseProperties.h"
70 
71 void *flush_pre_init_messages(void* data);
72 void push_pre_init_messages(char * ldm);
73 void reset_pre_init_messages();
74 
75 // debugging macro.
76 #define initialize_debug()                                                    \
77   do                                                                          \
78   {                                                                           \
79     if (!debug_initiated) {                                                   \
80       debug_initiated = true;                                                 \
81       plugin_debug = getenv ("ICEDTEAPLUGIN_DEBUG") != NULL || is_debug_on(); \
82       plugin_debug_headers = is_debug_header_on();                            \
83       plugin_debug_to_file = is_logging_to_file();                            \
84       plugin_debug_to_streams = is_logging_to_stds();                         \
85       plugin_debug_to_system = is_logging_to_system();                        \
86       plugin_debug_to_console = is_java_console_enabled();                    \
87       if (plugin_debug_to_file) {                                             \
88            IcedTeaPluginUtilities::initFileLog();                             \
89            file_logs_initiated = true;                                        \
90       }                                                                       \
91       if (plugin_debug_to_console) {                                          \
92           /*initialisation done during jvm startup*/                          \
93       }                                                                       \
94       IcedTeaPluginUtilities::printDebugStatus();                             \
95     }                                                                         \
96   } while (0)
97 
98 
99 #define  HEADER_SIZE  500
100 #define  BODY_SIZE  500
101 #define  MESSAGE_SIZE  HEADER_SIZE + BODY_SIZE
102 #define  LDEBUG_MESSAGE_SIZE MESSAGE_SIZE+50
103 
104 //header is destination char array
105 #define CREATE_HEADER(ldebug_header)                   \
106   do                                                   \
107   {                                                    \
108     char times[100];                                   \
109     time_t t = time(NULL);                             \
110     struct tm  p;                                      \
111     localtime_r(&t, &p);                               \
112     strftime(times, 100, "%a %b %d %H:%M:%S %Z %Y", &p);\
113     const char *userNameforDebug = (getenv("USERNAME") == NULL) ? "unknown user" : getenv("USERNAME");  \
114     /*this message is parsed in JavaConsole*/          \
115     snprintf(ldebug_header, HEADER_SIZE, "[%s][ITW-C-PLUGIN][MESSAGE_DEBUG][%s][%s:%d] ITNPP Thread# %ld, gthread %p: ",        \
116     userNameforDebug, times, __FILE__, __LINE__,  pthread_self(), g_thread_self ());                        \
117   } while (0)
118 
119 
120 #define PLUGIN_DEBUG(...)              \
121   do                                   \
122   {                                    \
123     initialize_debug();                \
124     if (plugin_debug)  {               \
125       char ldebug_header[HEADER_SIZE]; \
126       char ldebug_body[BODY_SIZE];     \
127       char ldebug_message[MESSAGE_SIZE];\
128       if (plugin_debug_headers) {      \
129         CREATE_HEADER(ldebug_header);  \
130       } else {                         \
131         sprintf(ldebug_header,"");     \
132       }                                \
133       snprintf(ldebug_body, BODY_SIZE,  __VA_ARGS__);                               \
134       if (plugin_debug_to_streams) {   \
135         snprintf(ldebug_message, MESSAGE_SIZE, "%s%s", ldebug_header, ldebug_body); \
136         fprintf  (stdout, "%s", ldebug_message);\
137       }                                \
138       if (plugin_debug_to_file && file_logs_initiated) {      \
139         snprintf(ldebug_message, MESSAGE_SIZE, "%s%s", ldebug_header, ldebug_body);   \
140         fprintf (plugin_file_log, "%s", ldebug_message);   \
141         fflush(plugin_file_log);       \
142       }                                \
143       if (plugin_debug_to_console) {   \
144         /*headers are always going to console*/            \
145         if (!plugin_debug_headers){      \
146           CREATE_HEADER(ldebug_header);  \
147         }                                \
148         snprintf(ldebug_message, MESSAGE_SIZE, "%s%s", ldebug_header, ldebug_body); \
149         char ldebug_channel_message[LDEBUG_MESSAGE_SIZE];                               \
150         struct timeval current_time;   \
151         gettimeofday (&current_time, NULL);\
152         if (jvm_up) {                  \
153           snprintf(ldebug_channel_message, LDEBUG_MESSAGE_SIZE, "%s %ld %s", "plugindebug", current_time.tv_sec*1000000L+current_time.tv_usec, ldebug_message);   \
154           push_pre_init_messages(ldebug_channel_message);                           \
155         } else {                       \
156           snprintf(ldebug_channel_message, LDEBUG_MESSAGE_SIZE, "%s %ld %s", "preinit_plugindebug", current_time.tv_sec*1000000L+current_time.tv_usec, ldebug_message);   \
157           push_pre_init_messages(ldebug_channel_message);                            \
158         }                              \
159       }                                \
160      if (plugin_debug_to_system){      \
161      /*no debug messages to systemlog*/\
162      }                                 \
163     }                                  \
164   } while (0)
165 
166 
167 #define PLUGIN_ERROR(...)              \
168   do                                   \
169   {                                    \
170     initialize_debug();                \
171     char ldebug_header[HEADER_SIZE];   \
172     char ldebug_body[BODY_SIZE];       \
173     char ldebug_message[MESSAGE_SIZE]; \
174     if (plugin_debug_headers) {        \
175       CREATE_HEADER(ldebug_header);    \
176     } else {                           \
177       sprintf(ldebug_header,"");       \
178     }                                  \
179     snprintf(ldebug_body, BODY_SIZE,  __VA_ARGS__);   \
180     if (plugin_debug_to_streams) {     \
181       snprintf(ldebug_message, MESSAGE_SIZE, "%s%s", ldebug_header, ldebug_body); \
182       fprintf  (stderr, "%s", ldebug_message);                                    \
183     }                                  \
184     if (plugin_debug_to_file && file_logs_initiated) {        \
185       snprintf(ldebug_message, MESSAGE_SIZE, "%s%s", ldebug_header, ldebug_body); \
186       fprintf (plugin_file_log, "%s", ldebug_message);   \
187       fflush(plugin_file_log);         \
188     }                                  \
189     if (plugin_debug_to_console) {     \
190       /*headers are always going to console*/            \
191       if (!plugin_debug_headers){            \
192         CREATE_HEADER(ldebug_header);  \
193       }                                \
194       snprintf(ldebug_message, MESSAGE_SIZE, "%s%s", ldebug_header, ldebug_body); \
195       char ldebug_channel_message[LDEBUG_MESSAGE_SIZE];                               \
196       struct timeval current_time;     \
197       gettimeofday (&current_time, NULL);\
198         if (jvm_up) {                  \
199           snprintf(ldebug_channel_message, LDEBUG_MESSAGE_SIZE, "%s %ld %s", "pluginerror", current_time.tv_sec*1000000L+current_time.tv_usec, ldebug_message);   \
200           push_pre_init_messages(ldebug_channel_message);                         \
201         } else {                       \
202           snprintf(ldebug_channel_message, LDEBUG_MESSAGE_SIZE, "%s %ld %s", "preinit_pluginerror", current_time.tv_sec*1000000L+current_time.tv_usec, ldebug_message);   \
203           push_pre_init_messages(ldebug_channel_message);                         \
204         }                              \
205     }                                  \
206     if (plugin_debug_to_system){      \
207       /*java can not have prefix*/    \
208       openlog("", LOG_NDELAY, LOG_USER);\
209       syslog(LOG_ERR, "%s", "IcedTea-Web c-plugin - for more info see itweb-settings debug options or console. See http://icedtea.classpath.org/wiki/IcedTea-Web#Filing_bugs for help.");\
210       syslog(LOG_ERR, "%s", "IcedTea-Web c-plugin error manual log:");\
211       /*no headers to syslog*/        \
212       syslog(LOG_ERR, "%s", ldebug_body);   \
213       closelog();                     \
214     }                                 \
215    } while (0)
216 
217 
218 #define CHECK_JAVA_RESULT(result_data)                               \
219 {                                                                    \
220     if (((JavaResultData*) result_data)->error_occurred)             \
221     {                                                                \
222         PLUGIN_ERROR("Error: Error occurred on Java side: %s.\n",    \
223                ((JavaResultData*) result_data)->error_msg->c_str()); \
224         return;                                                      \
225     }                                                                \
226 }
227 
228 #define HEX_TO_INT(c) \
229     ((*c >= 'a') ? *c - 'a' + 10 : \
230      (*c >= 'A') ? *c - 'A' + 10 : \
231      *c - '0')
232 
233 #define IS_VALID_HEX(c) \
234     ((*c >= '0' && *c <= '9') || \
235      (*c >= 'a' && *c <= 'f') || \
236      (*c >= 'A' && *c <= 'F'))
237 
238 //long long max ~ 19 chars + terminator
239 //leave some room for converting strings like "<var> = %d"
240 const size_t NUM_STR_BUFFER_SIZE = 32;
241 
242 /*
243  * This struct holds data specific to a Java operation requested by the plugin
244  */
245 typedef struct java_result_data
246 {
247 
248     // Return identifier (if applicable)
249     int return_identifier;
250 
251     // Return string (if applicable)
252     std::string* return_string;
253 
254     // Return wide/mb string (if applicable)
255     std::wstring* return_wstring;
256 
257     // Error message (if an error occurred)
258     std::string* error_msg;
259 
260     // Boolean indicating if an error occurred
261     bool error_occurred;
262 
263 } JavaResultData;
264 
265 /**
266  * This struct holds data to do calls that need to be run in the plugin thread
267  */
268 typedef struct plugin_thread_call
269 {
270    // The plugin instance
271    NPP instance;
272 
273    // The function to call
274    void (*func) (void *);
275 
276    // The data to pass to the function
277    void *userData;
278 
279 } PluginThreadCall;
280 
281 /**
282  * Data structure passed to functions called in a new thread.
283  */
284 
285 typedef struct async_call_thread_data
286 {
287     std::vector<void*> parameters;
288     std::string result;
289     bool result_ready;
290     bool call_successful;
291 } AsyncCallThreadData;
292 
293 /*
294  * Misc. utility functions
295  *
296  * This class is never instantiated and should contain static functions only
297  */
298 
299 /* Function to process all pending async calls */
300 void processAsyncCallQueue(void*);
301 
302 class IcedTeaPluginUtilities
303 {
304 
305     private:
306         static int reference; /* Reference count */
307 
308         /* Mutex lock for updating reference count */
309         static pthread_mutex_t reference_mutex;
310 
311         /* Map holding window pointer<->instance relationships */
312         static std::map<void*, NPP>* instance_map;
313 
314         /* Map holding java-side-obj-key->NPObject relationship  */
315         static std::map<std::string, NPObject*>* object_map;
316 
317         /* Posts a call in the async call queue */
318         static bool postPluginThreadAsyncCall(NPP instance, void (*func) (void *), void* data);
319 
320     public:
321 
322     	/* Constructs message prefix with given context */
323     	static void constructMessagePrefix(int context,
324                                            std::string* result);
325 
326     	/* Constructs message prefix with given context and reference */
327     	static void constructMessagePrefix(int context, int reference,
328                                            std::string* result);
329 
330     	/* Constructs message prefix with given context, reference and src */
331     	static void constructMessagePrefix(int context, int reference,
332                                            std::string address,
333                                            std::string* result);
334 
335     	/* Converts given pointer to a string representation */
336     	static void JSIDToString(void* id, std::string* result);
337 
338     	/* Converts the given string representation to a pointer */
339     	static void* stringToJSID(std::string id_str);
340     	static void* stringToJSID(std::string* id_str);
341 
342     	/* Increments reference count and returns it */
343     	static int getReference();
344 
345     	/* Decrements reference count */
346     	static void releaseReference();
347 
348     	/* Converts the given integer to a string */
349     	static void itoa(int i, std::string* result);
350 
351     	/* Copies a variant data type into a C++ string */
352     	static std::string NPVariantAsString(NPVariant variant);
353 
354         /* This must be freed with browserfunctions.memfree */
355         static NPString NPStringCopy(const std::string& result);
356 
357         /* This must be freed with browserfunctions.releasevariantvalue */
358         static NPVariant NPVariantStringCopy(const std::string& result);
359 
360         /* Returns an std::string represented by the given identifier. */
361         static std::string NPIdentifierAsString(NPIdentifier id);
362 
363     	/* Frees the given vector and the strings that its contents point to */
364     	static void freeStringPtrVector(std::vector<std::string*>* v);
365 
366     	/* Splits the given string based on the delimiter provided */
367     	static std::vector<std::string*>* strSplit(const char* str,
368     			                                  const char* delim);
369 
370     	/* Converts given unicode integer byte array to UTF8 string  */
371     	static void getUTF8String(int length, int begin,
372     			std::vector<std::string*>* unicode_byte_array,
373     			std::string* result_unicode_str);
374 
375     	/* Converts given UTF8 string to unicode integer byte array */
376     	static void convertStringToUTF8(std::string* str,
377     			                        std::string* utf_str);
378 
379     	/* Converts given unicode integer byte array to UTF16LE/UCS-2 string */
380     	static void getUTF16LEString(int length, int begin,
381     			std::vector<std::string*>* unicode_byte_array,
382     			std::wstring* result_unicode_str);
383 
384     	/* Prints contents of given string vector */
385     	static void printStringVector(const char* prefix, std::vector<std::string>* cv);
386 
387     	/* Prints contents of given string pointer vector */
388     	static void printStringPtrVector(const char* prefix, std::vector<std::string*>* cv);
389 
390     	static std::string* variantToClassName(NPVariant variant);
391 
392     	static void printNPVariant(NPVariant variant);
393 
394         static void NPVariantToString(NPVariant variant, std::string* result);
395 
396         static bool javaResultToNPVariant(NPP instance,
397                                           std::string* java_result,
398                                           NPVariant* variant);
399 
400     	static const gchar* getSourceFromInstance(NPP instance);
401 
402     	static void storeInstanceID(void* member_ptr, NPP instance);
403 
404     	static void removeInstanceID(void* member_ptr);
405 
406     	/* Clear object_map. Useful for tests. */
407     	static void clearInstanceIDs();
408 
409     	static NPP getInstanceFromMemberPtr(void* member_ptr);
410 
411     	static NPObject* getNPObjectFromJavaKey(std::string key);
412 
413     	static void storeObjectMapping(std::string key, NPObject* object);
414 
415     	static void removeObjectMapping(std::string key);
416 
417     	/* Clear object_map. Useful for tests. */
418     	static void clearObjectMapping();
419 
420     	static void invalidateInstance(NPP instance);
421 
422     	static bool isObjectJSArray(NPP instance, NPObject* object);
423 
424     	static void decodeURL(const char* url, char** decoded_url);
425 
426     	/* Returns a vector of gchar* pointing to the elements of the vector string passed in*/
427     	static std::vector<gchar*> vectorStringToVectorGchar(const std::vector<std::string>* stringVec);
428 
429     	/* Posts call in async queue and waits till execution completes */
430     	static void callAndWaitForResult(NPP instance, void (*func) (void *), AsyncCallThreadData* data);
431 
432         /*cutting whitespaces from end and start of string*/
433         static void trim(std::string& str);
434         /*Unescape various escaped chars like \\ -> \ or \= -> =  or \: -> \*/
435         static void unescape(std::string& str);
436         static bool file_exists(std::string filename);
437         static bool is_directory(std::string filename);
438         //file-loggers helpers
439         static std::string generateLogFileName();
440         static void initFileLog();
441         static void printDebugStatus();
442         static std::string getTmpPath();
443         static std::string getRuntimePath();
444         static bool create_dir(std::string);
445 };
446 
447 /*
448  * A bus subscriber interface. Implementors must implement the newMessageOnBus
449  * method.
450  */
451 class BusSubscriber
452 {
453     private:
454 
455     public:
BusSubscriber()456     	BusSubscriber() {}
457 
458     	/* Notifies this subscriber that a new message as arrived */
459         virtual bool newMessageOnBus(const char* message) = 0;
460 };
461 
462 /*
463  * This implementation is very simple and is therefore folded into this file
464  * rather than a new one.
465  */
466 class JavaMessageSender : public BusSubscriber
467 {
468     private:
469     public:
470 
471     	/* Sends given message to Java side */
472         virtual bool newMessageOnBus(const char* message);
473 };
474 
475 /*
476  * Represents a message bus.
477  * The bus can also have subscribers who are notified when a new message
478  * arrives.
479  */
480 class MessageBus
481 {
482     private:
483     	/* Mutex for locking the message queue */
484     	pthread_mutex_t msg_queue_mutex;
485 
486     	/* Mutex used when adjusting subscriber list */
487     	pthread_mutex_t subscriber_mutex;
488 
489     	/* Subscriber list */
490         std::list<BusSubscriber*> subscribers;
491 
492         /* Queued messages */
493         std::queue<char*> msgQueue;
494 
495     public:
496     	MessageBus();
497 
498         ~MessageBus();
499 
500         /* subscribe to this bus */
501         void subscribe(BusSubscriber* b);
502 
503         /* unsubscribe from this bus */
504         void unSubscribe(BusSubscriber* b);
505 
506         /* Post a message on to the bus (it is safe to free the message pointer
507            after this function returns) */
508         void post(const char* message);
509 };
510 
511 
512 
513 #endif // __ICEDTEAPLUGINUTILS_H__
514