1 /*
2  * mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2013-2014, Peter Olsson <peter@olssononline.se>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is ported from FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Anthony Minessale II <anthm@freeswitch.org>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  * Peter Olsson <peter@olssononline.se>
26  * Anthony Minessale II <anthm@freeswitch.org>
27  * William King <william.king@quentustech.com>
28  * Andrey Volk <andywolk@gmail.com>
29  *
30  * mod_v8.cpp -- JavaScript FreeSWITCH module
31  *
32  */
33 
34 /*
35  * This module executes JavaScript using Google's V8 JavaScript engine.
36  *
37  * It extends the available JavaScript classes with the following FS related classes;
38  * CoreDB		Adds features to access the core DB (SQLite) in FreeSWITCH. (on request only)
39  * DBH			Database Handler. Makes use of connection pooling provided by FreeSWITCH. (on request only)
40  * CURL			Adds some extra methods for CURL access. (on request only)
41  * DTMF			Object that holds information about a DTMF event.
42  * Event		Object that holds information about a FreeSWITCH event.
43  * EventHandler	Features for handling FS events.
44  * File			Class to reflect the Spidermonkey built-in class "File". Not yet implemented! (on request only)
45  * FileIO		Simple class for basic file IO.
46  * ODBC			Adds features to access any ODBC available database in the system. (on request only)
47  * PCRE			Adds features to do regexp using the PCRE implementeation.
48  * Request		Class for extra features during API call from FS (using 'jsapi' function). This class cannot be constructed from JS code!
49  *				The Request class is only availble when started from 'jsapi' FS command, and only inside the predefined variable 'request'.
50  * Session		Main FS class, includes all functions to handle a session.
51  * Socket		Class for communicating over a TCP/IP socket. (on request only)
52  * TeleTone		Class used to play tones to a FS channel. (on request only)
53  * XML			XML parsing class, using the features from switch_xml. (on request only)
54  *
55  * Some of the classes above are available on request only, using the command [use('Class');] before using the class for the first time.
56  *
57  * It also adds quite a few global functions, directly available for the user (see fsglobal.cpp for the implementation).
58  *
59  * Depedning on how the script was started from FreeSWITCH, some variables might be defined already;
60  * session   If the script is started from the dialplan, the variable 'session' holds the session for the current call.
61  * request   If the script is started using 'jsapi' function, the variable 'request' is an instance of the Request class.
62  * message   If the script is started as a chat application, the actual FreeSWITCH event will be available in the variable 'message'.
63  *
64  * All classes are implemented in a pair of hpp/cpp files, named after the class. For instance; class "File" is implemented in fsfile.cpp.
65  *
66  */
67 
68 #include "mod_v8.h"
69 #include <fstream>
70 
71 #ifdef V8_ENABLE_DEBUGGING
72 #include <v8-debug.h>
73 #endif
74 
75 /* Global JavaScript functions */
76 #include "fsglobal.hpp"
77 
78 /* Common JavaScript classes */
79 #include "fsrequest.hpp" /* Only loaded during 'jsapi' and 'jsjson' call */
80 #include "fspcre.hpp"
81 #include "fsevent.hpp"
82 #include "fssession.hpp"
83 #include "fsdtmf.hpp"
84 #include "fsfileio.hpp"
85 
86 /* Optional JavaScript classes (loaded on demand) */
87 #include "fscoredb.hpp"
88 #include "fsdbh.hpp"
89 #include "fscurl.hpp"
90 #include "fsteletone.hpp"
91 #include "fssocket.hpp"
92 #include "fsodbc.hpp"
93 #include "fsxml.hpp"
94 #include "fsfile.hpp"
95 #include "fseventhandler.hpp"
96 
97 #include <set>
98 
99 using namespace std;
100 using namespace v8;
101 
102 SWITCH_BEGIN_EXTERN_C
103 
104 /* FreeSWITCH module load definitions */
105 SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load);
106 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_v8_shutdown);
107 SWITCH_MODULE_DEFINITION_EX(mod_v8, mod_v8_load, mod_v8_shutdown, NULL, SMODF_GLOBAL_SYMBOLS);
108 
109 /* API interfaces */
110 static switch_api_interface_t *jsrun_interface = NULL;
111 static switch_api_interface_t *jsapi_interface = NULL;
112 static switch_api_interface_t *jsmon_interface = NULL;
113 
114 /* Module manager for loadable modules */
115 module_manager_t module_manager = { 0 };
116 
117 /* Global data for this module */
118 typedef struct {
119 	switch_memory_pool_t *pool;
120 	switch_mutex_t *event_mutex;
121 	switch_event_node_t *event_node;
122 	set<FSEventHandler *> *event_handlers;
123 	char *xml_handler;
124 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
125 	v8::Platform *v8platform;
126 	switch_hash_t *compiled_script_hash;
127 	switch_mutex_t *compiled_script_hash_mutex;
128 	map<string, Isolate *> *task_manager;
129 	switch_mutex_t *task_manager_mutex;
130 	char *script_caching;
131 	switch_time_t cache_expires_seconds;
132 	bool performance_monitor;
133 	switch_mutex_t *mutex;
134 #endif
135 } mod_v8_global_t;
136 
137 static mod_v8_global_t globals = { 0 };
138 
139 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
140 /* Struct to store cached script data */
141 typedef struct {
142 	std::shared_ptr<uint8_t> data;
143 	int length;
144 	switch_time_t compile_time;
145 } v8_compiled_script_cache_t;
146 #endif
147 
148 /* Loadable module struct, used for external extension modules */
149 typedef struct {
150 	char *filename;
151 	void *lib;
152 	const v8_mod_interface_t *module_interface;
153 	v8_mod_init_t v8_mod_init;
154 } v8_loadable_module_t;
155 
156 #ifdef V8_ENABLE_DEBUGGING
157 static bool debug_enable_callback = false;
158 static int debug_listen_port = 9999;
159 static bool debug_wait_for_connection = true;
160 static bool debug_manual_break = true;
161 #endif
162 
163 static void v8_thread_launch(const char *text);
164 static void v8_event_handler(switch_event_t *event);
165 static switch_xml_t v8_fetch(const char *section, const char *tag_name, const char *key_name, const char *key_value, switch_event_t *params, void *user_data);
166 
167 using namespace v8;
168 
v8_mod_init_built_in(const v8_mod_interface_t * mod_interface)169 static switch_status_t v8_mod_init_built_in(const v8_mod_interface_t *mod_interface)
170 {
171 	switch_assert(mod_interface);
172 
173 	switch_core_hash_insert(module_manager.load_hash, (char *) mod_interface->name, mod_interface);
174 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Successfully Loaded [%s]\n", mod_interface->name);
175 
176 	return SWITCH_STATUS_SUCCESS;
177 }
178 
v8_mod_load_file(const char * filename)179 static switch_status_t v8_mod_load_file(const char *filename)
180 {
181 	v8_loadable_module_t *module = NULL;
182 	switch_dso_lib_t dso = NULL;
183 	switch_status_t status = SWITCH_STATUS_SUCCESS;
184 	switch_loadable_module_function_table_t *function_handle = NULL;
185 	v8_mod_init_t v8_mod_init = NULL;
186 	const v8_mod_interface_t *module_interface = NULL, *mp;
187 	char *derr = NULL;
188 	const char *err = NULL;
189 
190 	switch_assert(filename != NULL);
191 
192 	if (!(dso = switch_dso_open(filename, 1, &derr))) {
193 		status = SWITCH_STATUS_FALSE;
194 	}
195 
196 	if (derr || status != SWITCH_STATUS_SUCCESS) {
197 		err = derr;
198 		goto err;
199 	}
200 
201 	function_handle = (switch_loadable_module_function_table_t *)switch_dso_data_sym(dso, "v8_mod_init", &derr);
202 
203 	if (!function_handle || derr) {
204 		err = derr;
205 		goto err;
206 	}
207 
208 	v8_mod_init = (v8_mod_init_t) (intptr_t) function_handle;
209 
210 	if (v8_mod_init(&module_interface) != SWITCH_STATUS_SUCCESS) {
211 		err = "Module load routine returned an error";
212 		goto err;
213 	}
214 
215 	if (!(module = (v8_loadable_module_t *) switch_core_permanent_alloc(sizeof(*module)))) {
216 		err = "Could not allocate memory\n";
217 	}
218 
219   err:
220 
221 	if (err || !module) {
222 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Loading module %s\n**%s**\n", filename, switch_str_nil(err));
223 		switch_safe_free(derr);
224 		return SWITCH_STATUS_GENERR;
225 	}
226 
227 	module->filename = switch_core_permanent_strdup(filename);
228 	module->v8_mod_init = v8_mod_init;
229 	module->module_interface = module_interface;
230 
231 	module->lib = dso;
232 
233 	if ((mp = module->module_interface)) {
234 		switch_core_hash_insert(module_manager.load_hash, (char *) mp->name, (void *) mp);
235 	}
236 
237 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Successfully Loaded [%s]\n", module->filename);
238 
239 	return SWITCH_STATUS_SUCCESS;
240 }
241 
v8_load_module(const char * dir,const char * fname)242 static switch_status_t v8_load_module(const char *dir, const char *fname)
243 {
244 	switch_size_t len = 0;
245 	char *path;
246 	char *file;
247 
248 #ifdef WIN32
249 	const char *ext = ".dll";
250 #else
251 	const char *ext = ".so";
252 #endif
253 
254 	if ((file = switch_core_strdup(module_manager.pool, fname)) == 0) {
255 		return SWITCH_STATUS_FALSE;
256 	}
257 
258 	if (*file == '/') {
259 		path = switch_core_strdup(module_manager.pool, file);
260 	} else {
261 		if (strchr(file, '.')) {
262 			len = strlen(dir);
263 			len += strlen(file);
264 			len += 4;
265 			path = (char *) switch_core_alloc(module_manager.pool, len);
266 			switch_snprintf(path, len, "%s%s%s", dir, SWITCH_PATH_SEPARATOR, file);
267 		} else {
268 			len = strlen(dir);
269 			len += strlen(file);
270 			len += 8;
271 			path = (char *) switch_core_alloc(module_manager.pool, len);
272 			switch_snprintf(path, len, "%s%s%s%s", dir, SWITCH_PATH_SEPARATOR, file, ext);
273 		}
274 	}
275 
276 	return v8_mod_load_file(path);
277 }
278 
279 SWITCH_END_EXTERN_C
280 
load_modules(void)281 static switch_status_t load_modules(void)
282 {
283 	const char *cf = "v8.conf";
284 	switch_xml_t cfg, xml;
285 	unsigned int count = 0;
286 
287 #ifdef WIN32
288 	const char *ext = ".dll";
289 	const char *EXT = ".DLL";
290 #elif defined (MACOSX) || defined (DARWIN)
291 	const char *ext = ".dylib";
292 	const char *EXT = ".DYLIB";
293 #else
294 	const char *ext = ".so";
295 	const char *EXT = ".SO";
296 #endif
297 
298 	switch_core_new_memory_pool(&module_manager.pool);
299 
300 	switch_core_hash_init(&module_manager.load_hash);
301 
302 	if ((xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
303 		switch_xml_t mods, ld;
304 
305 		if ((mods = switch_xml_child(cfg, "modules"))) {
306 			for (ld = switch_xml_child(mods, "load"); ld; ld = ld->next) {
307 				const char *val = switch_xml_attr_soft(ld, "module");
308 				if (!zstr(val) && strchr(val, '.') && !strstr(val, ext) && !strstr(val, EXT)) {
309 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Invalid extension for %s\n", val);
310 					continue;
311 				}
312 				v8_load_module(SWITCH_GLOBAL_dirs.mod_dir, val);
313 				count++;
314 			}
315 		}
316 		switch_xml_free(xml);
317 
318 	} else {
319 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Open of %s failed\n", cf);
320 	}
321 
322 	return SWITCH_STATUS_SUCCESS;
323 }
324 
load_configuration(void)325 static void load_configuration(void)
326 {
327 	const char *cf = "v8.conf";
328 	switch_xml_t cfg, xml;
329 
330 	if ((xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
331 		switch_xml_t settings, param, hook;
332 
333 		if ((settings = switch_xml_child(cfg, "settings"))) {
334 			for (param = switch_xml_child(settings, "param"); param; param = param->next) {
335 				char *var = (char *)switch_xml_attr_soft(param, "name");
336 				char *val = (char *)switch_xml_attr_soft(param, "value");
337 
338 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
339 				if (!strcmp(var, "script-caching")) {
340 					globals.script_caching = switch_core_strdup(globals.pool, val);
341 				} else if (!strcmp(var, "cache-expires-sec")) {
342 					int v = atoi(val);
343 					globals.cache_expires_seconds = (v > 0) ? v : 0;
344 				} else
345 #endif
346 				if (!strcmp(var, "xml-handler-script")) {
347 					globals.xml_handler = switch_core_strdup(globals.pool, val);
348 				}
349 				else if (!strcmp(var, "xml-handler-bindings")) {
350 					if (!zstr(globals.xml_handler)) {
351 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "binding '%s' to '%s'\n", globals.xml_handler, val);
352 						switch_xml_bind_search_function(v8_fetch, switch_xml_parse_section_string(val), NULL);
353 					}
354 				}
355 				else if (!strcmp(var, "startup-script")) {
356 					if (val) {
357 						v8_thread_launch(val);
358 					}
359 				}
360 			}
361 
362 			for (hook = switch_xml_child(settings, "hook"); hook; hook = hook->next) {
363 				char *event = (char *)switch_xml_attr_soft(hook, "event");
364 				char *subclass = (char *)switch_xml_attr_soft(hook, "subclass");
365 				char *script = (char *)switch_xml_attr_soft(hook, "script");
366 				switch_event_types_t evtype;
367 
368 				if (!zstr(script)) {
369 					script = switch_core_strdup(globals.pool, script);
370 				}
371 
372 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "hook params: '%s' | '%s' | '%s'\n", event, subclass, script);
373 
374 				if (switch_name_event(event, &evtype) == SWITCH_STATUS_SUCCESS) {
375 					if (!zstr(script)) {
376 						if (switch_event_bind(modname, evtype, !zstr(subclass) ? subclass : SWITCH_EVENT_SUBCLASS_ANY,
377 							v8_event_handler, script) == SWITCH_STATUS_SUCCESS) {
378 							switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "event handler for '%s' set to '%s'\n", switch_event_name(evtype), script);
379 						}
380 						else {
381 							switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "cannot set event handler: unsuccessful bind\n");
382 						}
383 					}
384 					else {
385 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "cannot set event handler: no script name for event type '%s'\n", event);
386 					}
387 				}
388 				else {
389 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "cannot set event handler: unknown event type '%s'\n", event);
390 				}
391 			}
392 		}
393 
394 		switch_xml_free(xml);
395 
396 	}
397 	else {
398 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Open of %s failed\n", cf);
399 	}
400 }
401 
env_init(JSMain * js)402 static int env_init(JSMain *js)
403 {
404 	/* Init all "global" functions first */
405 	const js_function_t *fs_proc = FSGlobal::GetFunctionDefinitions();
406 	for (int i = 0; fs_proc[i].name && fs_proc[i].func; i++) {
407 		js->AddJSExtenderFunction(fs_proc[i].func, fs_proc[i].name);
408 	}
409 
410 	/* Init all basic classes made available from FreeSWITCH */
411 	js->AddJSExtenderClass(FSSession::GetClassDefinition());
412 	js->AddJSExtenderClass(FSFileIO::GetClassDefinition());
413 	js->AddJSExtenderClass(FSEvent::GetClassDefinition());
414 	js->AddJSExtenderClass(FSDTMF::GetClassDefinition());
415 	js->AddJSExtenderClass(FSPCRE::GetClassDefinition());
416 	/* To add a class that will always be available inside JS, add the definition here */
417 
418 	return 1;
419 }
420 
v8_error(Isolate * isolate,TryCatch * try_catch)421 static void v8_error(Isolate* isolate, TryCatch* try_catch)
422 {
423 	HandleScope handle_scope(isolate);
424 	String::Utf8Value exception(try_catch->Exception());
425 	const char *exception_string = js_safe_str(*exception);
426 	Handle<Message> message = try_catch->Message();
427 	const char *msg = "";
428 	string filename = __FILE__;
429 	int line = __LINE__;
430 	string text = "";
431 	JSMain *js = JSMain::GetScriptInstanceFromIsolate(isolate);
432 
433 	if (js && js->GetForcedTermination()) {
434 		js->ResetForcedTermination();
435 		switch_log_printf(SWITCH_CHANNEL_ID_LOG, js->GetForcedTerminationScriptFile(), modname, js->GetForcedTerminationLineNumber(), NULL, SWITCH_LOG_NOTICE, "Script exited with info [%s]\n", js->GetForcedTerminationMessage());
436 		return;
437 	}
438 
439 	if (!message.IsEmpty()) {
440 		String::Utf8Value fname(message->GetScriptResourceName());
441 
442 		if (*fname) {
443 			filename = *fname;
444 		}
445 
446 		line = message->GetLineNumber();
447 		msg = exception_string;
448 
449 		String::Utf8Value sourceline(message->GetSourceLine());
450 		if (*sourceline) {
451 			text = *sourceline;
452 		}
453 	} else {
454 		msg = exception_string;
455 	}
456 
457 	if (!msg) {
458 		msg = "";
459 	}
460 
461 	if (text.length() > 0) {
462 		switch_log_printf(SWITCH_CHANNEL_ID_LOG, filename.c_str(), modname, line, NULL, SWITCH_LOG_ERROR, "Exception: %s (near: \"%s\")\n", msg, text.c_str());
463 	} else {
464 		switch_log_printf(SWITCH_CHANNEL_ID_LOG, filename.c_str(), modname, line, NULL, SWITCH_LOG_ERROR, "Exception: %s\n", msg);
465 	}
466 }
467 
v8_get_script_path(const char * script_file)468 static char *v8_get_script_path(const char *script_file)
469 {
470 	const char *p;
471 	char *ret = NULL;
472 	const char delims[] = "/\\";
473 	const char *i;
474 
475 	if (script_file) {
476 		for (i = delims; *i; i++) {
477 			if ((p = strrchr(script_file, *i))) {
478 				js_strdup(ret, script_file);
479 				*(ret + (p - script_file)) = '\0';
480 				return ret;
481 			}
482 		}
483 		js_strdup(ret, ".");
484 		return ret;
485 	} else {
486 		return NULL;
487 	}
488 }
489 
490 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
perf_log(const char * fmt,...)491 void perf_log(const char *fmt, ...)
492 {
493 	va_list ap;
494 	va_start(ap, fmt);
495 
496 	switch_mutex_lock(globals.mutex);
497 	if (globals.performance_monitor) {
498 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, fmt, ap);
499 	}
500 	switch_mutex_unlock(globals.mutex);
501 
502 	va_end(ap);
503 }
504 
505 template< typename T >
506 struct array_deleter
507 {
operator ()array_deleter508 	void operator ()(T const * p)
509 	{
510 		delete[] p;
511 	}
512 };
513 
destructor(void * ptr)514 static void destructor(void *ptr)
515 {
516 	delete (v8_compiled_script_cache_t*)ptr;
517 }
518 
LoadScript(MaybeLocal<v8::Script> * v8_script,Isolate * isolate,const char * script_data,const char * script_file)519 void LoadScript(MaybeLocal<v8::Script> *v8_script, Isolate *isolate, const char *script_data, const char *script_file)
520 {
521 	switch_time_t start = switch_time_now();
522 
523 	ScriptCompiler::CachedData *cached_data = 0;
524 	v8_compiled_script_cache_t *stored_compiled_script_cache = NULL;
525 	ScriptCompiler::CompileOptions options;
526 
527 	/*
528 		Do not cache if the caching is disabled
529 		Do not cache inline scripts
530 	*/
531 	if (!switch_true(globals.script_caching) || !strcasecmp(script_file, "inline") || zstr(script_file)) {
532 		options = ScriptCompiler::kNoCompileOptions;
533 		perf_log("Javascript caching is disabled.\n", script_file);
534 	} else {
535 		options = ScriptCompiler::kConsumeCodeCache;
536 
537 		switch_mutex_lock(globals.compiled_script_hash_mutex);
538 
539 		void *hash_found = switch_core_hash_find(globals.compiled_script_hash, script_file);
540 		if (hash_found)
541 		{
542 			stored_compiled_script_cache = new v8_compiled_script_cache_t;
543 			*stored_compiled_script_cache = *((v8_compiled_script_cache_t *)hash_found);
544 		}
545 
546 		switch_mutex_unlock(globals.compiled_script_hash_mutex);
547 
548 		if (stored_compiled_script_cache)
549 		{
550 			switch_time_t time_left_since_compile_sec = (switch_time_now() - stored_compiled_script_cache->compile_time) / 1000000;
551 			if (time_left_since_compile_sec <= globals.cache_expires_seconds || globals.cache_expires_seconds == 0) {
552 				cached_data = new ScriptCompiler::CachedData(stored_compiled_script_cache->data.get(), stored_compiled_script_cache->length, ScriptCompiler::CachedData::BufferNotOwned);
553 			} else {
554 				perf_log("Javascript ['%s'] cache expired.\n", script_file);
555 				switch_core_hash_delete_locked(globals.compiled_script_hash, script_file, globals.compiled_script_hash_mutex);
556 			}
557 
558 		}
559 
560 		if (!cached_data) options = ScriptCompiler::kProduceCodeCache;
561 
562 	}
563 
564 	ScriptCompiler::Source source(String::NewFromUtf8(isolate, script_data), cached_data);
565 	*v8_script = ScriptCompiler::Compile(isolate->GetCurrentContext(), &source, options);
566 
567 	if (!v8_script->IsEmpty()) {
568 
569 		if (options == ScriptCompiler::kProduceCodeCache && !source.GetCachedData()->rejected) {
570 			int length = source.GetCachedData()->length;
571 			uint8_t* raw_cached_data = new uint8_t[length];
572 			v8_compiled_script_cache_t *compiled_script_cache = new v8_compiled_script_cache_t;
573 			memcpy(raw_cached_data, source.GetCachedData()->data, static_cast<size_t>(length));
574 			compiled_script_cache->data.reset(raw_cached_data, array_deleter<uint8_t>());
575 			compiled_script_cache->length = length;
576 			compiled_script_cache->compile_time = switch_time_now();
577 
578 			switch_mutex_lock(globals.compiled_script_hash_mutex);
579 			switch_core_hash_insert_destructor(globals.compiled_script_hash, script_file, compiled_script_cache, destructor);
580 			switch_mutex_unlock(globals.compiled_script_hash_mutex);
581 
582 			perf_log("Javascript ['%s'] cache was produced.\n", script_file);
583 
584 		} else if (options == ScriptCompiler::kConsumeCodeCache) {
585 
586 			if (source.GetCachedData()->rejected) {
587 				perf_log("Javascript ['%s'] cache was rejected.\n", script_file);
588 				switch_core_hash_delete_locked(globals.compiled_script_hash, script_file, globals.compiled_script_hash_mutex);
589 			} else {
590 				perf_log("Javascript ['%s'] execution using cache.\n", script_file);
591 			}
592 
593 		}
594 	}
595 
596 	if (stored_compiled_script_cache)
597 		delete stored_compiled_script_cache;
598 
599 	switch_time_t end = switch_time_now();
600 	perf_log("Javascript ['%s'] loaded in %u microseconds.\n", script_file, (end - start));
601 
602 }
603 #endif
604 
v8_parse_and_execute(switch_core_session_t * session,const char * input_code,switch_stream_handle_t * api_stream,v8_event_t * v8_event,v8_xml_handler_t * xml_handler)605 static int v8_parse_and_execute(switch_core_session_t *session, const char *input_code, switch_stream_handle_t *api_stream, v8_event_t *v8_event, v8_xml_handler_t* xml_handler)
606 {
607 	string res;
608 	JSMain *js;
609 	Isolate *isolate;
610 	char *arg, *argv[512];
611 	int argc = 0;
612 	unsigned int flags = 0;
613 	char *path = NULL;
614 	string result_string;
615 	int result = 0;
616 
617 	if (zstr(input_code)) {
618 		return -1;
619 	}
620 
621 	js = new JSMain();
622 	isolate = js->GetIsolate();
623 
624 	env_init(js);
625 
626 	/* Try to read lock the session first */
627 	if (session) {
628 		if (switch_core_session_read_lock_hangup(session) != SWITCH_STATUS_SUCCESS) {
629 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Read Lock Failure.\n");
630 			session = NULL;
631 		}
632 	}
633 
634 	/* Execute the actual script */
635 	//isolate->Enter();
636 	{
637 		Locker lock(isolate);
638 		Isolate::Scope iscope(isolate);
639 		{
640 			const char *script;
641 
642 			// Create a stack-allocated handle scope.
643 			HandleScope scope(isolate);
644 
645 			// Store our object internally
646 			isolate->SetData(ISOLATE_DATA_OBJECT, js);
647 
648 			// Set isolate related data.
649 			switch_uuid_t task_id;
650 			switch_uuid_get(&task_id);
651 			char str_task_id[SWITCH_UUID_FORMATTED_LENGTH + 1];
652 			switch_uuid_format(str_task_id, &task_id);
653 
654 			js_isolate_private_data_t *private_data = new js_isolate_private_data_t();
655 			private_data->str_task_id = str_task_id;
656 			private_data->input_code = input_code;
657 			private_data->start_time = switch_time_now();
658 
659 			// Store private data internally
660 			isolate->SetData(ISOLATE_DATA_PRIVATE, private_data);
661 
662 			// Add isolate to the task manager
663 			switch_mutex_lock(globals.task_manager_mutex);
664 			(*globals.task_manager)[str_task_id] = isolate;
665 			switch_mutex_unlock(globals.task_manager_mutex);
666 
667 			// New global template
668 			Handle<ObjectTemplate> global = ObjectTemplate::New(isolate);
669 
670 			if (global.IsEmpty()) {
671 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create JS global object template\n");
672 			} else {
673 				/* Add all global functions */
674 				for (size_t i = 0; i < js->GetExtenderFunctions().size(); i++) {
675 					js_function_t *proc = js->GetExtenderFunctions()[i];
676 					global->Set(String::NewFromUtf8(isolate, proc->name), FunctionTemplate::New(isolate, proc->func));
677 				}
678 
679 				// Create a new context.
680 				Local<Context> context = Context::New(isolate, NULL, global);
681 
682 				if (context.IsEmpty()) {
683 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to create JS context\n");
684 				} else {
685 					// Enter the created context for compiling and running the script.
686 					Context::Scope context_scope(context);
687 
688 #ifdef V8_ENABLE_DEBUGGING
689 					Persistent<Context> *debug_context = new Persistent<Context>();
690 					isolate->SetData(ISOLATE_DATA_DEBUG, debug_context);
691 					debug_context->Reset(isolate, context);
692 
693 					//v8::Locker lck(isolate);
694 
695 					if (debug_enable_callback) {
696 						Debug::SetDebugMessageDispatchHandler(V8DispatchDebugMessages, true);
697 					}
698 
699 					if (debug_listen_port > 0) {
700 						char *name = switch_mprintf("mod_v8-%d", (int)switch_thread_self());
701 						Debug::EnableAgent(name, debug_listen_port, debug_wait_for_connection);
702 						switch_safe_free(name);
703 					}
704 #endif
705 
706 					/* Register all plugin classes */
707 					for (size_t i = 0; i < js->GetExtenderClasses().size(); i++) {
708 						JSBase::Register(isolate, js->GetExtenderClasses()[i]);
709 					}
710 
711 					/* Register all instances of specific plugin classes */
712 					for (size_t i = 0; i < js->GetExtenderInstances().size(); i++) {
713 						registered_instance_t *inst = js->GetExtenderInstances()[i];
714 						inst->obj->RegisterInstance(isolate, inst->name, inst->auto_destroy);
715 					}
716 
717 					/* Emaculent conception of session object into the script if one is available */
718 					if (session) {
719 						FSSession *obj = new FSSession(js);
720 						obj->Init(session, flags);
721 						obj->RegisterInstance(isolate, "session", true);
722 					} else {
723 						/* Add a session object as a boolean instead, just to make it safe to check if it exists as expected */
724 						context->Global()->Set(String::NewFromUtf8(isolate, "session"), Boolean::New(isolate, false));
725 					}
726 
727 					if (v8_event) {
728 						if (v8_event->event && v8_event->var_name)
729 						    FSEvent::New(v8_event->event, v8_event->var_name, js);
730 					}
731 
732 					if (api_stream) {
733 						/* The JS class "Request" is only needed when a api_stream object is passed here */
734 						JSBase::Register(isolate, FSRequest::GetClassDefinition());
735 
736 						FSRequest *ptr = new FSRequest(js);
737 						ptr->Init(input_code, api_stream);
738 						ptr->RegisterInstance(isolate, "request", true);
739 					}
740 
741 					if (xml_handler)
742 					{
743 						/* Add xml handler global variables */
744 
745 						Handle<Array> XML_REQUEST = Array::New(isolate, 4);
746 
747 						XML_REQUEST->Set(String::NewFromUtf8(isolate, "key_name"), String::NewFromUtf8(isolate, js_safe_str(xml_handler->key_name)));
748 						XML_REQUEST->Set(String::NewFromUtf8(isolate, "key_value"), String::NewFromUtf8(isolate, js_safe_str(xml_handler->key_value)));
749 						XML_REQUEST->Set(String::NewFromUtf8(isolate, "section"), String::NewFromUtf8(isolate, js_safe_str(xml_handler->section)));
750 						XML_REQUEST->Set(String::NewFromUtf8(isolate, "tag_name"), String::NewFromUtf8(isolate, js_safe_str(xml_handler->tag_name)));
751 
752 						context->Global()->Set(String::NewFromUtf8(isolate, "XML_REQUEST"), XML_REQUEST);
753 
754 						if (xml_handler->params) {
755 							FSEvent::New(xml_handler->params, "params", js);
756 						}
757 					}
758 
759 					script = input_code;
760 
761 					if (*script != '~') {
762 						if ((arg = (char *)strchr(script, ' '))) {
763 							*arg++ = '\0';
764 							argc = switch_separate_string(arg, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
765 						}
766 
767 						// Add arguments before running script.
768 						Local<Array> arguments = Array::New(isolate, argc);
769 						for (int y = 0; y < argc; y++) {
770 							arguments->Set(Integer::New(isolate, y), String::NewFromUtf8(isolate, argv[y]));
771 						}
772 						context->Global()->Set(String::NewFromUtf8(isolate, "argv"), arguments);
773 						context->Global()->Set(String::NewFromUtf8(isolate, "argc"), Integer::New(isolate, argc));
774 					}
775 
776 					const char *script_data = NULL;
777 					const char *script_file = NULL;
778 					string s;
779 
780 					if (*script == '~') {
781 						script_data = script + 1;
782 						script_file = "inline";
783 					} else {
784 						const char *script_name = NULL;
785 
786 						if (switch_is_file_path(script)) {
787 							script_name = script;
788 						} else if ((path = switch_mprintf("%s%s%s", SWITCH_GLOBAL_dirs.script_dir, SWITCH_PATH_SEPARATOR, script))) {
789 							script_name = path;
790 						}
791 
792 						if (script_name) {
793 							if (JSMain::FileExists(script_name)) {
794 								s = JSMain::LoadFileToString(script_name);
795 								script_data = s.c_str();
796 								script_file = script_name;
797 							} else {
798 								switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Open File: %s\n", script_name);
799 							}
800 						}
801 					}
802 
803 					if (!script_data) {
804 						switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No script to execute!\n");
805 					} else {
806 						/* Store our base directory in variable 'scriptPath' */
807 						char *scriptPath = v8_get_script_path(script_file);
808 						if (scriptPath) {
809 							context->Global()->Set(String::NewFromUtf8(isolate, "scriptPath"), String::NewFromUtf8(isolate, scriptPath));
810 							switch_safe_free(scriptPath);
811 						}
812 
813 						TryCatch try_catch(isolate);
814 
815 						// Compile the source code.
816 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
817 						switch_time_t start = switch_time_now();
818 						MaybeLocal<v8::Script> v8_script;
819 						LoadScript(&v8_script, isolate, script_data, script_file);
820 #else
821 						// Create a string containing the JavaScript source code.
822 						Handle<String> source = String::NewFromUtf8(isolate, script_data);
823 						Handle<Script> v8_script = Script::Compile(source, Local<Value>::New(isolate, String::NewFromUtf8(isolate, script_file)));
824 #endif
825 
826 						if (try_catch.HasCaught()) {
827 							v8_error(isolate, &try_catch);
828 						} else {
829 							// Run the script
830 #ifdef V8_ENABLE_DEBUGGING
831 							/* Break before we actually start executing the script */
832 							if (debug_manual_break) {
833 								Debug::DebugBreak(isolate);
834 							}
835 #endif
836 
837 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
838 							Handle<Value> script_result;
839 
840 							if (!v8_script.IsEmpty()) {
841 								script_result = v8_script.ToLocalChecked()->Run();
842 							}
843 
844 							switch_mutex_lock(globals.mutex);
845 							if (globals.performance_monitor) {
846 								switch_time_t end = switch_time_now();
847 								unsigned int delay = (end - start);
848 								switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Javascript execution time: %u microseconds\n", delay);
849 							}
850 							switch_mutex_unlock(globals.mutex);
851 #else
852 							Handle<Value> result = v8_script->Run();
853 #endif
854 							if (try_catch.HasCaught()) {
855 								v8_error(isolate, &try_catch);
856 							} else {
857 								if (js->GetForcedTermination()) {
858 									js->ResetForcedTermination();
859 									switch_log_printf(SWITCH_CHANNEL_ID_LOG, js->GetForcedTerminationScriptFile(), modname, js->GetForcedTerminationLineNumber(), NULL, SWITCH_LOG_NOTICE, "Script exited with info [%s]\n", js->GetForcedTerminationMessage());
860 								}
861 
862 								if (!script_result.IsEmpty()) {
863 									// Return result as string
864 									String::Utf8Value ascii(script_result);
865 									if (*ascii) {
866 										res = *ascii;
867 									}
868 								}
869 
870 								if (xml_handler)
871 								{
872 									Local<Value> value = context->Global()->Get(String::NewFromUtf8(isolate, "XML_STRING"));
873 									String::Utf8Value str(value);
874 									if (strcmp(js_safe_str(*str), "undefined"))
875 									{
876 										xml_handler->XML_STRING = strdup(js_safe_str(*str));
877 									}
878 								}
879 							}
880 						}
881 
882 #ifndef V8_FORCE_GC_AFTER_EXECUTION
883 						/* Clear up all destroyable C++ instances */
884 						js->DisposeActiveInstances();
885 #endif
886 					}
887 #ifdef V8_ENABLE_DEBUGGING
888 					isolate->SetData(ISOLATE_DATA_DEBUG, NULL);
889 					if (debug_listen_port > 0) {
890 						Debug::DisableAgent();
891 					}
892 					debug_context->Reset();
893 					delete debug_context;
894 #endif
895 				}
896 			}
897 
898 			// Remove isolate from the task manager
899 			switch_mutex_lock(globals.task_manager_mutex);
900 			globals.task_manager->erase(str_task_id);
901 			switch_mutex_unlock(globals.task_manager_mutex);
902 
903 			isolate->SetData(ISOLATE_DATA_PRIVATE, NULL);
904 			isolate->SetData(ISOLATE_DATA_OBJECT, NULL);
905 
906 			delete private_data;
907 		}
908 
909 #ifdef V8_FORCE_GC_AFTER_EXECUTION
910 		/* Force GC to run. This should not be needed, but is left here for reference */
911 		V8::ContextDisposedNotification();
912 		while (!V8::IdleNotification()) {}
913 		js->DisposeActiveInstances();
914 #endif
915 	}
916 	//isolate->Exit();
917 
918 	if (res.length() == 0) {
919 		result = -1;
920 	} else {
921 		result = 0;
922 
923 		if (strcasecmp(res.c_str(), "undefined")) {
924 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Javascript result: [%s]\n", res.c_str());
925 		}
926 	}
927 
928 	switch_safe_free(path);
929 	delete js;
930 
931 	return result;
932 }
933 
v8_fetch(const char * section,const char * tag_name,const char * key_name,const char * key_value,switch_event_t * params,void * user_data)934 static switch_xml_t v8_fetch(const char *section,
935 	const char *tag_name, const char *key_name, const char *key_value, switch_event_t *params, void *user_data)
936 {
937 	switch_xml_t xml = NULL;
938 	char *mycmd = NULL;
939 
940 	if (!zstr(globals.xml_handler)) {
941 
942 		mycmd = strdup(globals.xml_handler);
943 		switch_assert(mycmd);
944 
945 		v8_xml_handler_t xml_handler;
946 		xml_handler.section = section;
947 		xml_handler.tag_name = tag_name;
948 		xml_handler.key_name = key_name;
949 		xml_handler.key_value = key_value;
950 		xml_handler.params = params;
951 		xml_handler.user_data = user_data;
952 		xml_handler.XML_STRING = NULL; //Init as NULL. A script's global var XML_STRING will be duplicated to it!
953 
954 		v8_parse_and_execute(NULL, mycmd, NULL, NULL, &xml_handler);
955 
956 		if (xml_handler.XML_STRING) {
957 			if (zstr(xml_handler.XML_STRING)) {
958 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Result\n");
959 			}
960 			else if (!(xml = switch_xml_parse_str_dynamic(xml_handler.XML_STRING, SWITCH_TRUE))) {
961 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Parsing XML Result!\n");
962 			}
963 		}
964 
965 		// Don't forget to free XML_STRING
966 		switch_safe_free(xml_handler.XML_STRING);
967 
968 	}
969 
970 	switch_safe_free(mycmd);
971 
972 	return xml;
973 }
974 
v8_event_handler(switch_event_t * event)975 static void v8_event_handler(switch_event_t *event)
976 {
977 	char *script = NULL;
978 
979 	if (event->bind_user_data) {
980 		script = strdup((char *)event->bind_user_data);
981 	}
982 
983 	v8_event_t v8_event;
984 
985 	v8_event.event = event;
986 	v8_event.var_name = "event";
987 
988 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "v8 event hook: execute '%s'\n", (char *)script);
989 	v8_parse_and_execute(NULL, script, NULL, &v8_event, NULL);
990 
991 	switch_safe_free(script);
992 }
993 
994 SWITCH_BEGIN_EXTERN_C
995 
SWITCH_STANDARD_APP(v8_dp_function)996 SWITCH_STANDARD_APP(v8_dp_function)
997 {
998 	v8_parse_and_execute(session, data, NULL, NULL, NULL);
999 }
1000 
SWITCH_STANDARD_CHAT_APP(v8_chat_function)1001 SWITCH_STANDARD_CHAT_APP(v8_chat_function)
1002 {
1003 	v8_event_t v8_event;
1004 
1005 	v8_event.event = message;
1006 	v8_event.var_name = "message";
1007 
1008 	v8_parse_and_execute(NULL, data, NULL, &v8_event, NULL);
1009 
1010 	return SWITCH_STATUS_SUCCESS;
1011 }
1012 
1013 SWITCH_END_EXTERN_C
1014 
1015 typedef struct {
1016 	switch_memory_pool_t *pool;
1017 	char *code;
1018 } v8_task_t;
1019 
v8_thread_run(switch_thread_t * thread,void * obj)1020 static void *SWITCH_THREAD_FUNC v8_thread_run(switch_thread_t *thread, void *obj)
1021 {
1022 	v8_task_t *task = (v8_task_t *) obj;
1023 	switch_memory_pool_t *pool;
1024 
1025 	v8_parse_and_execute(NULL, task->code, NULL, NULL, NULL);
1026 
1027 	if ((pool = task->pool)) {
1028 		switch_core_destroy_memory_pool(&pool);
1029 	}
1030 
1031 	return NULL;
1032 }
1033 
v8_thread_launch(const char * text)1034 static void v8_thread_launch(const char *text)
1035 {
1036 	switch_thread_t *thread;
1037 	switch_threadattr_t *thd_attr = NULL;
1038 	v8_task_t *task;
1039 	switch_memory_pool_t *pool;
1040 
1041 	if (zstr(text)) {
1042 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "missing required input!\n");
1043 		return;
1044 	}
1045 
1046 	if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
1047 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n");
1048 		return;
1049 	}
1050 
1051 	task = (v8_task_t *)switch_core_alloc(pool, sizeof(*task));
1052 	task->pool = pool;
1053 	task->code = switch_core_strdup(pool, text);
1054 
1055 	switch_threadattr_create(&thd_attr, pool);
1056 	switch_threadattr_detach_set(thd_attr, 1);
1057 	switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
1058 	switch_thread_create(&thread, thd_attr, v8_thread_run, task, pool);
1059 }
1060 
v8_add_event_handler(void * event_handler)1061 void v8_add_event_handler(void *event_handler)
1062 {
1063 	FSEventHandler *eh = static_cast<FSEventHandler *>(event_handler);
1064 
1065 	if (eh) {
1066 		switch_mutex_lock(globals.event_mutex);
1067 		globals.event_handlers->insert(eh);
1068 		switch_mutex_unlock(globals.event_mutex);
1069 	}
1070 }
1071 
v8_remove_event_handler(void * event_handler)1072 void v8_remove_event_handler(void *event_handler)
1073 {
1074 	FSEventHandler *eh = static_cast<FSEventHandler *>(event_handler);
1075 
1076 	if (eh) {
1077 		switch_mutex_lock(globals.event_mutex);
1078 
1079 		set<FSEventHandler *>::iterator it = globals.event_handlers->find(eh);
1080 
1081 		if (it != globals.event_handlers->end()) {
1082 			globals.event_handlers->erase(it);
1083 		}
1084 
1085 		switch_mutex_unlock(globals.event_mutex);
1086 	}
1087 }
1088 
1089 SWITCH_BEGIN_EXTERN_C
1090 
event_handler(switch_event_t * event)1091 static void event_handler(switch_event_t *event)
1092 {
1093 	if (event) {
1094 		switch_mutex_lock(globals.event_mutex);
1095 
1096 		set<FSEventHandler *>::iterator it;
1097 
1098 		for (it = globals.event_handlers->begin(); it != globals.event_handlers->end(); ++it) {
1099 			if (*it) {
1100 				(*it)->QueueEvent(event);
1101 			}
1102 		}
1103 
1104 		switch_mutex_unlock(globals.event_mutex);
1105 	}
1106 }
1107 
SWITCH_STANDARD_API(jsapi_function)1108 SWITCH_STANDARD_API(jsapi_function)
1109 {
1110 	char *path_info = NULL;
1111 
1112 	if (stream->param_event) {
1113 		path_info = switch_event_get_header(stream->param_event, "http-path-info");
1114 	}
1115 
1116 	if (zstr(cmd) && path_info) {
1117 		cmd = path_info;
1118 	}
1119 
1120 	if (zstr(cmd)) {
1121 		stream->write_function(stream, "USAGE: %s\n", jsapi_interface->syntax);
1122 		return SWITCH_STATUS_SUCCESS;
1123 	}
1124 
1125 	v8_parse_and_execute(session, (char *) cmd, stream, NULL, NULL);
1126 
1127 	return SWITCH_STATUS_SUCCESS;
1128 }
1129 
1130 
SWITCH_STANDARD_JSON_API(json_function)1131 SWITCH_STANDARD_JSON_API(json_function)
1132 {
1133 	char *json_text = NULL;
1134 	cJSON *path = NULL, *data = NULL;
1135 	switch_stream_handle_t stream = { 0 };
1136 
1137 	if ((data = cJSON_GetObjectItem(json, "data"))) {
1138 		path = cJSON_GetObjectItem(data, "path");
1139 	}
1140 
1141 	if (!(path && data)) {
1142 		goto end;
1143 	}
1144 
1145 	SWITCH_STANDARD_STREAM(stream);
1146 
1147 	json_text = cJSON_PrintUnformatted(data);
1148 	switch_event_create(&stream.param_event, SWITCH_EVENT_REQUEST_PARAMS);
1149 	switch_event_add_header_string(stream.param_event, SWITCH_STACK_BOTTOM, "JSON", json_text);
1150 	switch_safe_free(json_text);
1151 
1152 	v8_parse_and_execute(session, (char *) path->valuestring, &stream, NULL, NULL);
1153 
1154 	*json_reply = cJSON_Parse((char *)stream.data);
1155 
1156  end:
1157 
1158 	if (!*json_reply) {
1159 		*json_reply = cJSON_CreateObject();
1160 		cJSON_AddItemToObject(*json_reply, "error", cJSON_CreateString("parse error in return val or invalid data supplied"));
1161 	}
1162 
1163 	switch_event_destroy(&stream.param_event);
1164 	switch_safe_free(stream.data);
1165 
1166 	return SWITCH_STATUS_SUCCESS;
1167 }
1168 
SWITCH_STANDARD_API(launch_async)1169 SWITCH_STANDARD_API(launch_async)
1170 {
1171 	if (zstr(cmd)) {
1172 		stream->write_function(stream, "USAGE: %s\n", jsrun_interface->syntax);
1173 		return SWITCH_STATUS_SUCCESS;
1174 	}
1175 
1176 	v8_thread_launch(cmd);
1177 	stream->write_function(stream, "+OK\n");
1178 	return SWITCH_STATUS_SUCCESS;
1179 }
1180 
SWITCH_STANDARD_API(jsmon_function)1181 SWITCH_STANDARD_API(jsmon_function)
1182 {
1183 	if (zstr(cmd)) {
1184 		stream->write_function(stream, "USAGE: %s\n", jsmon_interface->syntax);
1185 		return SWITCH_STATUS_SUCCESS;
1186 	}
1187 
1188 	if (!strcasecmp(cmd, "on")) {
1189 		switch_mutex_lock(globals.mutex);
1190 		globals.performance_monitor = true;
1191 		switch_mutex_unlock(globals.mutex);
1192 		stream->write_function(stream, "Performance monitor has been enabled.\n", jsmon_interface->syntax);
1193 	} else if (!strcasecmp(cmd, "off")) {
1194 		switch_mutex_lock(globals.mutex);
1195 		globals.performance_monitor = false;
1196 		switch_mutex_unlock(globals.mutex);
1197 		stream->write_function(stream, "Performance monitor has been disabled.\n", jsmon_interface->syntax);
1198 	} else {
1199 		stream->write_function(stream, "USAGE: %s\n", jsmon_interface->syntax);
1200 		return SWITCH_STATUS_SUCCESS;
1201 	}
1202 
1203 	stream->write_function(stream, "+OK\n");
1204 	return SWITCH_STATUS_SUCCESS;
1205 }
1206 
SWITCH_STANDARD_API(kill_function)1207 SWITCH_STANDARD_API(kill_function)
1208 {
1209 	if (!zstr(cmd)) {
1210 		switch_mutex_lock(globals.task_manager_mutex);
1211 
1212 		auto isolate_it = globals.task_manager->find(cmd);
1213 		if (isolate_it != globals.task_manager->end()) {
1214 			Isolate * isolate = isolate_it->second;
1215 			JSMain *js = JSMain::GetScriptInstanceFromIsolate(isolate);
1216 			if (js)
1217 				js->ExitScript(isolate, "Script termination requested by jskill API.", true);
1218 		}
1219 
1220 		switch_mutex_unlock(globals.task_manager_mutex);
1221 
1222 		stream->write_function(stream, "+OK\n");
1223 	}
1224 	else {
1225 		stream->write_function(stream, "false");
1226 	}
1227 
1228 	return SWITCH_STATUS_SUCCESS;
1229 }
1230 
stream_write_safe_d(switch_stream_handle_t * stream,const char * str)1231 inline static void stream_write_safe_d(switch_stream_handle_t *stream, const char *str) {
1232 	if (!str) {
1233 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
1234 		stream->write_function(stream, "-ERR Memory Error!\n");
1235 	}
1236 	else {
1237 		stream->write_function(stream, "%s", str);
1238 	}
1239 }
1240 #define stream_write_safe(output_text) stream_write_safe_d(stream, output_text)
1241 
SWITCH_STANDARD_API(process_status_function)1242 SWITCH_STANDARD_API(process_status_function)
1243 {
1244 	char *mydata = NULL, *argv[3] = { 0 };
1245 	char *as = NULL, *output_text = NULL, *delim = ",";
1246 	cJSON *json = NULL, *row;
1247 	switch_xml_t xml = NULL, xml_row, xml_field;
1248 	int rows = 0, f_off = 0, count = 0;
1249 	char tmp_str[50];
1250 	std::vector<js_isolate_private_data_t> tasks;
1251 
1252 	if (cmd && *cmd && (mydata = strdup(cmd))) {
1253 		switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
1254 		if (argv[1] && !strcasecmp(argv[0], "as")) {
1255 			as = argv[1];
1256 			if (!strcasecmp(as, "csv")) {
1257 				if (argv[2]) delim = argv[2];
1258 			}
1259 		}
1260 	}
1261 
1262 	if (!as) {
1263 		as = "plain";
1264 	}
1265 
1266 	if (!strcasecmp(as, "json")) {
1267 		if (!(json = cJSON_CreateArray())) {
1268 			goto end;
1269 		}
1270 	} else if (!strcasecmp(as, "xml")) {
1271 		if (!(xml = switch_xml_new("result"))) {
1272 			goto end;
1273 		}
1274 	} else if (!strcasecmp(as, "delim") || !strcasecmp(as, "csv")) {
1275 		stream->write_function(stream, "%s%s", "task_id", delim);
1276 		stream->write_function(stream, "%s%s", "input_code", delim);
1277 		stream->write_function(stream, "%s%s", "execution_time", delim);
1278 
1279 		stream->write_function(stream, "%s%s", "total_physical_size", delim);
1280 		stream->write_function(stream, "%s%s", "total_heap_size_executable", delim);
1281 		stream->write_function(stream, "%s%s", "total_heap_size", delim);
1282 		stream->write_function(stream, "%s%s", "used_heap_size", delim);
1283 		stream->write_function(stream, "%s%s", "heap_size_limit", delim);
1284 		stream->write_function(stream, "%s%s", "malloced_memory", delim);
1285 		stream->write_function(stream, "%s%s", "peak_malloced_memory", "\n");
1286 	} else {
1287 		stream->write_function(stream, "JavaScript process status.\n");
1288 	}
1289 
1290 	switch_mutex_lock(globals.task_manager_mutex);
1291 
1292 	for (auto isolate_pair : *globals.task_manager) {
1293 		Isolate *isolate = isolate_pair.second;
1294 
1295 		js_isolate_private_data_t *isolate_private_data = (js_isolate_private_data_t*)isolate->GetData(ISOLATE_DATA_PRIVATE);
1296 		js_isolate_private_data_t private_data = *isolate_private_data;
1297 
1298 		isolate->GetHeapStatistics(&private_data.stats);
1299 
1300 		tasks.push_back(private_data);
1301 	}
1302 
1303 	switch_mutex_unlock(globals.task_manager_mutex);
1304 
1305 	for (auto isolate_private_data : tasks) {
1306 		count++;
1307 
1308 		js_isolate_private_data_t *private_data = (js_isolate_private_data_t *)&(isolate_private_data);
1309 
1310 		switch_time_t end = switch_time_now();
1311 		unsigned int delay = (end - private_data->start_time) / 1000;
1312 
1313 		if (!strcasecmp(as, "plain")) {
1314 
1315 			stream->write_function(stream, "\nTask id: %s\n", private_data->str_task_id.c_str());
1316 			stream->write_function(stream, "input_code: %s\n", (private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str()));
1317 			stream->write_function(stream, "execution_time: %u ms\n", delay);
1318 
1319 			stream->write_function(stream, "total_physical_size: %u\n", private_data->stats.total_physical_size());
1320 			stream->write_function(stream, "total_heap_size_executable: %u\n", private_data->stats.total_heap_size_executable());
1321 			stream->write_function(stream, "total_heap_size: %u\n", private_data->stats.total_heap_size());
1322 			stream->write_function(stream, "used_heap_size: %u\n", private_data->stats.used_heap_size());
1323 			stream->write_function(stream, "heap_size_limit: %u\n", private_data->stats.heap_size_limit());
1324 			stream->write_function(stream, "malloced_memory: %u\n", private_data->stats.malloced_memory());
1325 			stream->write_function(stream, "peak_malloced_memory: %u\n", private_data->stats.peak_malloced_memory());
1326 		} else if (!strcasecmp(as, "json")) {
1327 			if (!(row = cJSON_CreateObject())) {
1328 				goto end;
1329 			}
1330 
1331 			cJSON_AddItemToArray(json, row);
1332 
1333 			cJSON_AddItemToObject(row, "task_id", cJSON_CreateString(private_data->str_task_id.c_str()));
1334 			cJSON_AddItemToObject(row, "input_code", cJSON_CreateString((private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str())));
1335 			cJSON_AddItemToObject(row, "execution_time", cJSON_CreateNumber(delay));
1336 
1337 			cJSON_AddItemToObject(row, "total_physical_size", cJSON_CreateNumber(private_data->stats.total_physical_size()));
1338 			cJSON_AddItemToObject(row, "total_heap_size_executable", cJSON_CreateNumber(private_data->stats.total_heap_size_executable()));
1339 			cJSON_AddItemToObject(row, "total_heap_size", cJSON_CreateNumber(private_data->stats.total_heap_size()));
1340 			cJSON_AddItemToObject(row, "used_heap_size", cJSON_CreateNumber(private_data->stats.used_heap_size()));
1341 			cJSON_AddItemToObject(row, "heap_size_limit", cJSON_CreateNumber(private_data->stats.heap_size_limit()));
1342 			cJSON_AddItemToObject(row, "malloced_memory", cJSON_CreateNumber(private_data->stats.malloced_memory()));
1343 			cJSON_AddItemToObject(row, "peak_malloced_memory", cJSON_CreateNumber(private_data->stats.peak_malloced_memory()));
1344 		} else if (!strcasecmp(as, "delim") || !strcasecmp(as, "csv")) {
1345 			stream->write_function(stream, "%s%s", private_data->str_task_id.c_str(), delim);
1346 			stream->write_function(stream, "%s%s", (private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str()), delim);
1347 
1348 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", delay);
1349 			stream->write_function(stream, "%s%s", tmp_str, delim);
1350 
1351 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_physical_size());
1352 			stream->write_function(stream, "%s%s", tmp_str, delim);
1353 
1354 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size_executable());
1355 			stream->write_function(stream, "%s%s", tmp_str, delim);
1356 
1357 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size());
1358 			stream->write_function(stream, "%s%s", tmp_str, delim);
1359 
1360 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.used_heap_size());
1361 			stream->write_function(stream, "%s%s", tmp_str, delim);
1362 
1363 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.heap_size_limit());
1364 			stream->write_function(stream, "%s%s", tmp_str, delim);
1365 
1366 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.malloced_memory());
1367 			stream->write_function(stream, "%s%s", tmp_str, delim);
1368 
1369 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.peak_malloced_memory());
1370 			stream->write_function(stream, "%s%s", tmp_str, "\n");
1371 
1372 		} else if (!strcasecmp(as, "xml")) {
1373 			if (!(xml_row = switch_xml_add_child_d(xml, "row", rows++))) {
1374 				goto end;
1375 			}
1376 
1377 			switch_snprintf(tmp_str, sizeof(tmp_str), "%d", rows);
1378 			switch_xml_set_attr(switch_xml_set_flag(xml_row, SWITCH_XML_DUP), strdup("row_id"), strdup(tmp_str));
1379 
1380 			if (!(xml_field = switch_xml_add_child_d(xml_row, "task_id", f_off++))) {
1381 				goto end;
1382 			}
1383 			switch_xml_set_txt_d(xml_field, private_data->str_task_id.c_str());
1384 
1385 			if (!(xml_field = switch_xml_add_child_d(xml_row, "input_code", f_off++))) {
1386 				goto end;
1387 			}
1388 			switch_xml_set_txt_d(xml_field, (private_data->input_code[0] == '~' ? "inline" : private_data->input_code.c_str()));
1389 
1390 			if (!(xml_field = switch_xml_add_child_d(xml_row, "execution_time", f_off++))) {
1391 				goto end;
1392 			}
1393 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", delay);
1394 			switch_xml_set_txt_d(xml_field, tmp_str);
1395 
1396 			if (!(xml_field = switch_xml_add_child_d(xml_row, "total_physical_size", f_off++))) {
1397 				goto end;
1398 			}
1399 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_physical_size());
1400 			switch_xml_set_txt_d(xml_field, tmp_str);
1401 
1402 			if (!(xml_field = switch_xml_add_child_d(xml_row, "total_heap_size_executable", f_off++))) {
1403 				goto end;
1404 			}
1405 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size_executable());
1406 			switch_xml_set_txt_d(xml_field, tmp_str);
1407 
1408 			if (!(xml_field = switch_xml_add_child_d(xml_row, "total_heap_size", f_off++))) {
1409 				goto end;
1410 			}
1411 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.total_heap_size());
1412 			switch_xml_set_txt_d(xml_field, tmp_str);
1413 
1414 			if (!(xml_field = switch_xml_add_child_d(xml_row, "used_heap_size", f_off++))) {
1415 				goto end;
1416 			}
1417 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.used_heap_size());
1418 			switch_xml_set_txt_d(xml_field, tmp_str);
1419 
1420 			if (!(xml_field = switch_xml_add_child_d(xml_row, "heap_size_limit", f_off++))) {
1421 				goto end;
1422 			}
1423 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.heap_size_limit());
1424 			switch_xml_set_txt_d(xml_field, tmp_str);
1425 
1426 			if (!(xml_field = switch_xml_add_child_d(xml_row, "malloced_memory", f_off++))) {
1427 				goto end;
1428 			}
1429 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.malloced_memory());
1430 			switch_xml_set_txt_d(xml_field, tmp_str);
1431 
1432 			if (!(xml_field = switch_xml_add_child_d(xml_row, "peak_malloced_memory", f_off++))) {
1433 				goto end;
1434 			}
1435 			switch_snprintf(tmp_str, sizeof(tmp_str), "%u", private_data->stats.peak_malloced_memory());
1436 			switch_xml_set_txt_d(xml_field, tmp_str);
1437 
1438 		}
1439 	}
1440 
1441 	if (!strcasecmp(as, "json")) {
1442 		cJSON *result;
1443 
1444 		if (!(result = cJSON_CreateObject())) {
1445 			stream->write_function(stream, "-ERR Error creating json object!\n");
1446 			goto end;
1447 		}
1448 		else {
1449 			cJSON_AddItemToObject(result, "row_count", cJSON_CreateNumber(count));
1450 			cJSON_AddItemToObject(result, "rows", json);
1451 
1452 			output_text = cJSON_PrintUnformatted(result);
1453 			json = result;
1454 		}
1455 
1456 		stream_write_safe(output_text);
1457 
1458 	} else if (!strcasecmp(as, "xml")) {
1459 		switch_snprintf(tmp_str, sizeof(tmp_str), "%u", count);
1460 		switch_xml_set_attr_d(xml, "row_count", tmp_str);
1461 
1462 		output_text = switch_xml_toxml(xml, SWITCH_FALSE);
1463 
1464 		stream_write_safe(output_text);
1465 
1466 	} else if (!strcasecmp(as, "delim") || !strcasecmp(as, "csv")) {
1467 		stream->write_function(stream, "%s%u total.%s", "\n", count, "\n");
1468 	}
1469 
1470 end:
1471 
1472 	switch_xml_free(xml);
1473 	cJSON_Delete(json);
1474 	switch_safe_free(output_text);
1475 	switch_safe_free(mydata);
1476 
1477 	return SWITCH_STATUS_SUCCESS;
1478 }
1479 
SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load)1480 SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load)
1481 {
1482 	switch_application_interface_t *app_interface;
1483 	switch_chat_application_interface_t *chat_app_interface;
1484 	switch_json_api_interface_t *json_api_interface;
1485 
1486 	if (switch_event_bind_removable(modname, SWITCH_EVENT_ALL, SWITCH_EVENT_SUBCLASS_ANY, event_handler, NULL, &globals.event_node) != SWITCH_STATUS_SUCCESS) {
1487 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to events\n");
1488 		return SWITCH_STATUS_GENERR;
1489 	}
1490 
1491 	globals.pool = pool;
1492 
1493 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
1494 	switch_mutex_init(&globals.compiled_script_hash_mutex, SWITCH_MUTEX_NESTED, globals.pool);
1495 	switch_mutex_init(&globals.task_manager_mutex, SWITCH_MUTEX_NESTED, globals.pool);
1496 	switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, globals.pool);
1497 #endif
1498 	switch_mutex_init(&globals.event_mutex, SWITCH_MUTEX_NESTED, globals.pool);
1499 	globals.event_handlers = new set<FSEventHandler *>();
1500 
1501 	if (load_modules() != SWITCH_STATUS_SUCCESS) {
1502 		delete globals.event_handlers;
1503 		switch_event_unbind(&globals.event_node);
1504 		return SWITCH_STATUS_FALSE;
1505 	}
1506 
1507 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
1508 	globals.v8platform = NULL;
1509 	globals.cache_expires_seconds = 0;
1510 	globals.performance_monitor = false;
1511 	globals.script_caching = switch_core_strdup(globals.pool, "disabled");
1512 
1513 	JSMain::Initialize(&globals.v8platform);
1514 
1515 	switch_core_hash_init(&globals.compiled_script_hash);
1516 	globals.task_manager = new map<string, Isolate *>();
1517 #else
1518 	JSMain::Initialize();
1519 #endif
1520 
1521 	/* Make all "built in" modules available to load on demand */
1522 	v8_mod_init_built_in(FSCoreDB::GetModuleInterface());
1523 	v8_mod_init_built_in(FSDBH::GetModuleInterface());
1524 	v8_mod_init_built_in(FSCURL::GetModuleInterface());
1525 #ifdef HAVE_ODBC
1526 	/* Only add ODBC class if ODBC is available in the system */
1527 	v8_mod_init_built_in(FSODBC::GetModuleInterface());
1528 #endif
1529 	v8_mod_init_built_in(FSSocket::GetModuleInterface());
1530 	v8_mod_init_built_in(FSTeleTone::GetModuleInterface());
1531 	v8_mod_init_built_in(FSXML::GetModuleInterface());
1532 	v8_mod_init_built_in(FSFile::GetModuleInterface());
1533 	v8_mod_init_built_in(FSEventHandler::GetModuleInterface());
1534 
1535 	/* connect my internal structure to the blank pointer passed to me */
1536 	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
1537 	SWITCH_ADD_API(jsrun_interface, "jsrun", "run a script", launch_async, "jsrun <script> [additional_vars [...]]");
1538 	SWITCH_ADD_API(jsapi_interface, "jsapi", "execute an api call", jsapi_function, "jsapi <script> [additional_vars [...]]");
1539 	SWITCH_ADD_API(jsmon_interface, "jsmon", "toggle performance monitor", jsmon_function, "jsmon on|off");
1540 	SWITCH_ADD_API(jsrun_interface, "jsps", "process status", process_status_function, "jsps [as plain|json|xml|delim|csv [<delimeter>]]");
1541 	SWITCH_ADD_API(jsrun_interface, "jskill", "kill a task", kill_function, "jskill <task_id>");
1542 	SWITCH_ADD_APP(app_interface, "javascript", "Launch JS ivr", "Run a javascript ivr on a channel", v8_dp_function, "<script> [additional_vars [...]]", SAF_SUPPORT_NOMEDIA);
1543 	SWITCH_ADD_CHAT_APP(chat_app_interface, "javascript", "execute a js script", "execute a js script", v8_chat_function, "<script>", SCAF_NONE);
1544 
1545 	SWITCH_ADD_JSON_API(json_api_interface, "jsjson", "JSON JS Gateway", json_function, "");
1546 
1547 	load_configuration();
1548 
1549 	/* indicate that the module should continue to be loaded */
1550 	return SWITCH_STATUS_NOUNLOAD;
1551 }
1552 
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_v8_shutdown)1553 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_v8_shutdown)
1554 {
1555 	switch_event_unbind(&globals.event_node);
1556 
1557 	delete globals.event_handlers;
1558 	switch_mutex_destroy(globals.event_mutex);
1559 
1560 #if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >=5
1561 	delete globals.v8platform;
1562 
1563 	switch_core_hash_destroy(&globals.compiled_script_hash);
1564 	switch_mutex_destroy(globals.compiled_script_hash_mutex);
1565 	switch_mutex_destroy(globals.task_manager_mutex);
1566 	delete globals.task_manager;
1567 	switch_mutex_destroy(globals.mutex);
1568 #endif
1569 
1570 	switch_core_hash_destroy(&module_manager.load_hash);
1571 	switch_core_destroy_memory_pool(&module_manager.pool);
1572 
1573 	return SWITCH_STATUS_SUCCESS;
1574 }
1575 
1576 SWITCH_END_EXTERN_C
1577 
1578 /* For Emacs:
1579  * Local Variables:
1580  * mode:c
1581  * indent-tabs-mode:t
1582  * tab-width:4
1583  * c-basic-offset:4
1584  * End:
1585  * For VIM:
1586  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
1587  */
1588