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 (¤t_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 (¤t_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