From 04005dfa68579d5766739b32ee8118779b93e4b4 Mon Sep 17 00:00:00 2001 From: Peter Olsson Date: Sat, 1 Feb 2014 18:20:04 +0100 Subject: [PATCH] mod_v8: Added new extension class that makes it possible to subscribe to FS events. Wiki will be updated soon. --- src/mod/languages/mod_v8/Makefile.am | 3 +- .../mod_v8/include/fseventhandler.hpp | 94 +++ src/mod/languages/mod_v8/mod_v8.2010.vcxproj | 2 + .../mod_v8/mod_v8.2010.vcxproj.filters | 6 + src/mod/languages/mod_v8/mod_v8.2012.vcxproj | 2 + .../mod_v8/mod_v8.2012.vcxproj.filters | 6 + src/mod/languages/mod_v8/mod_v8.cpp | 112 ++- src/mod/languages/mod_v8/mod_v8.h | 7 + .../languages/mod_v8/src/fseventhandler.cpp | 660 ++++++++++++++++++ src/mod/languages/mod_v8/src/jsbase.cpp | 2 +- 10 files changed, 875 insertions(+), 19 deletions(-) create mode 100644 src/mod/languages/mod_v8/include/fseventhandler.hpp create mode 100644 src/mod/languages/mod_v8/src/fseventhandler.cpp diff --git a/src/mod/languages/mod_v8/Makefile.am b/src/mod/languages/mod_v8/Makefile.am index c1e7a035b0..fc6b67a658 100644 --- a/src/mod/languages/mod_v8/Makefile.am +++ b/src/mod/languages/mod_v8/Makefile.am @@ -90,7 +90,8 @@ mod_v8_la_SOURCES = \ src/fssocket.cpp \ src/fsteletone.cpp \ src/fsxml.cpp \ - src/fsfile.cpp + src/fsfile.cpp \ + src/fseventhandler.cpp mod_v8_la_CFLAGS = $(AM_CFLAGS) $(LIBCURL_CPPFLAGS) -I$(switch_srcdir)/libs/libteletone/src mod_v8_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBCURL_CPPFLAGS) -I$(switch_srcdir)/libs/libteletone/src diff --git a/src/mod/languages/mod_v8/include/fseventhandler.hpp b/src/mod/languages/mod_v8/include/fseventhandler.hpp new file mode 100644 index 0000000000..cfc9c37bd2 --- /dev/null +++ b/src/mod/languages/mod_v8/include/fseventhandler.hpp @@ -0,0 +1,94 @@ +/* + * mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2013-2014, Peter Olsson + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Peter Olsson + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Peter Olsson + * + * fseventhandler.hpp -- JavaScript EventHandler class header + * + */ + +#ifndef FS_EVENTHANDLER_H +#define FS_EVENTHANDLER_H + +#include "mod_v8.h" + +/* Macros for easier V8 callback definitions */ +#define JS_EVENTHANDLER_GET_PROPERTY_DEF(method_name) JS_GET_PROPERTY_DEF(method_name, FSEventHandler) +#define JS_EVENTHANDLER_SET_PROPERTY_DEF(method_name) JS_SET_PROPERTY_DEF(method_name, FSEventHandler) +#define JS_EVENTHANDLER_FUNCTION_DEF(method_name) JS_FUNCTION_DEF(method_name, FSEventHandler) +#define JS_EVENTHANDLER_GET_PROPERTY_IMPL(method_name) JS_GET_PROPERTY_IMPL(method_name, FSEventHandler) +#define JS_EVENTHANDLER_SET_PROPERTY_IMPL(method_name) JS_SET_PROPERTY_IMPL(method_name, FSEventHandler) +#define JS_EVENTHANDLER_FUNCTION_IMPL(method_name) JS_FUNCTION_IMPL(method_name, FSEventHandler) +#define JS_EVENTHANDLER_FUNCTION_IMPL_STATIC(method_name) JS_FUNCTION_IMPL_STATIC(method_name, FSEventHandler) +#define JS_EVENTHANDLER_GET_PROPERTY_IMPL_STATIC(method_name) JS_GET_PROPERTY_IMPL_STATIC(method_name, FSEventHandler) + +class FSEventHandler : public JSBase +{ +private: + switch_mutex_t *_mutex; + switch_memory_pool_t *_pool; + switch_hash_t *_event_hash; + switch_queue_t *_event_queue; + uint8_t _event_list[SWITCH_EVENT_ALL + 1]; + switch_event_t *_filters; + + void Init(); + void DoSubscribe(const v8::FunctionCallbackInfo& info); +public: + FSEventHandler(JSMain *owner) : JSBase(owner) { Init(); } + FSEventHandler(const v8::FunctionCallbackInfo& info) : JSBase(info) { Init(); } + virtual ~FSEventHandler(void); + virtual std::string GetJSClassName(); + + static const v8_mod_interface_t *GetModuleInterface(); + + /* Public method to queue an event to this instance */ + void QueueEvent(switch_event_t *event); + + /* Methods available from JavaScript */ + static void *Construct(const v8::FunctionCallbackInfo& info); + JS_EVENTHANDLER_FUNCTION_DEF(Subscribe); + JS_EVENTHANDLER_FUNCTION_DEF(UnSubscribe); + JS_EVENTHANDLER_FUNCTION_DEF(AddFilter); + JS_EVENTHANDLER_FUNCTION_DEF(DeleteFilter); + JS_EVENTHANDLER_FUNCTION_DEF(GetEvent); + JS_EVENTHANDLER_FUNCTION_DEF(SendEvent); + JS_EVENTHANDLER_FUNCTION_DEF(ExecuteApi); + JS_EVENTHANDLER_FUNCTION_DEF(ExecuteBgApi); + JS_FUNCTION_DEF_STATIC(Destroy); + JS_GET_PROPERTY_DEF_STATIC(GetReadyProperty); +}; + +#endif /* FS_EVENTHANDLER_H */ + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/src/mod/languages/mod_v8/mod_v8.2010.vcxproj b/src/mod/languages/mod_v8/mod_v8.2010.vcxproj index bc5175a4a7..101f0ffaad 100644 --- a/src/mod/languages/mod_v8/mod_v8.2010.vcxproj +++ b/src/mod/languages/mod_v8/mod_v8.2010.vcxproj @@ -178,6 +178,7 @@ + @@ -195,6 +196,7 @@ + diff --git a/src/mod/languages/mod_v8/mod_v8.2010.vcxproj.filters b/src/mod/languages/mod_v8/mod_v8.2010.vcxproj.filters index 1f35875ef7..77911bad54 100644 --- a/src/mod/languages/mod_v8/mod_v8.2010.vcxproj.filters +++ b/src/mod/languages/mod_v8/mod_v8.2010.vcxproj.filters @@ -50,6 +50,9 @@ FSClasses + + FSClasses + @@ -98,6 +101,9 @@ FSClasses\include + + FSClasses\include + diff --git a/src/mod/languages/mod_v8/mod_v8.2012.vcxproj b/src/mod/languages/mod_v8/mod_v8.2012.vcxproj index 9595223bbc..2ff8aaabc6 100644 --- a/src/mod/languages/mod_v8/mod_v8.2012.vcxproj +++ b/src/mod/languages/mod_v8/mod_v8.2012.vcxproj @@ -182,6 +182,7 @@ + @@ -199,6 +200,7 @@ + diff --git a/src/mod/languages/mod_v8/mod_v8.2012.vcxproj.filters b/src/mod/languages/mod_v8/mod_v8.2012.vcxproj.filters index 1f35875ef7..77911bad54 100644 --- a/src/mod/languages/mod_v8/mod_v8.2012.vcxproj.filters +++ b/src/mod/languages/mod_v8/mod_v8.2012.vcxproj.filters @@ -50,6 +50,9 @@ FSClasses + + FSClasses + @@ -98,6 +101,9 @@ FSClasses\include + + FSClasses\include + diff --git a/src/mod/languages/mod_v8/mod_v8.cpp b/src/mod/languages/mod_v8/mod_v8.cpp index 2e4bdc3b33..4e72fbd8a8 100644 --- a/src/mod/languages/mod_v8/mod_v8.cpp +++ b/src/mod/languages/mod_v8/mod_v8.cpp @@ -34,20 +34,21 @@ * This module executes JavaScript using Google's V8 JavaScript engine. * * It extends the available JavaScript classes with the following FS related classes; - * CoreDB Adds features to access the core DB (SQLite) in FreeSWITCH. (on request only) - * CURL Adds some extra methods for CURL access. (on request only) - * DTMF Object that holds information about a DTMF event. - * Event Object that holds information about a FreeSWITCH event. - * File Class to reflect the Spidermonkey built-in class "File". Not yet implemented! (on request only) - * FileIO Simple class for basic file IO. - * ODBC Adds features to access any ODBC available database in the system. (on request only) - * PCRE Adds features to do regexp using the PCRE implementeation. - * Request Class for extra features during API call from FS (using 'jsapi' function). This class cannot be constructed from JS code! - * The Request class is only availble when started from 'jsapi' FS command, and only inside the predefined variable 'request'. - * Session Main FS class, includes all functions to handle a session. - * Socket Class for communicating over a TCP/IP socket. (on request only) - * TeleTone Class used to play tones to a FS channel. (on request only) - * XML XML parsing class, using the features from switch_xml. (on request only) + * CoreDB Adds features to access the core DB (SQLite) in FreeSWITCH. (on request only) + * CURL Adds some extra methods for CURL access. (on request only) + * DTMF Object that holds information about a DTMF event. + * Event Object that holds information about a FreeSWITCH event. + * EventHandler Features for handling FS events. + * File Class to reflect the Spidermonkey built-in class "File". Not yet implemented! (on request only) + * FileIO Simple class for basic file IO. + * ODBC Adds features to access any ODBC available database in the system. (on request only) + * PCRE Adds features to do regexp using the PCRE implementeation. + * Request Class for extra features during API call from FS (using 'jsapi' function). This class cannot be constructed from JS code! + * The Request class is only availble when started from 'jsapi' FS command, and only inside the predefined variable 'request'. + * Session Main FS class, includes all functions to handle a session. + * Socket Class for communicating over a TCP/IP socket. (on request only) + * TeleTone Class used to play tones to a FS channel. (on request only) + * XML XML parsing class, using the features from switch_xml. (on request only) * * Some of the classes above are available on request only, using the command [use('Class');] before using the class for the first time. * @@ -73,7 +74,7 @@ #include "fsglobal.hpp" /* Common JavaScript classes */ -#include "fsrequest.hpp" /* Only loaded during 'jsapi' call */ +#include "fsrequest.hpp" /* Only loaded during 'jsapi' and 'jsjson' call */ #include "fspcre.hpp" #include "fsevent.hpp" #include "fssession.hpp" @@ -88,6 +89,9 @@ #include "fsodbc.hpp" #include "fsxml.hpp" #include "fsfile.hpp" +#include "fseventhandler.hpp" + +#include using namespace std; using namespace v8; @@ -106,7 +110,17 @@ static switch_api_interface_t *jsapi_interface = NULL; /* Module manager for loadable modules */ module_manager_t module_manager = { 0 }; -/* Loadable module struct */ +/* Global data for this module */ +typedef struct { + switch_memory_pool_t *pool; + switch_mutex_t *event_mutex; + switch_event_node_t *event_node; + set *event_handlers; +} mod_v8_global_t; + +mod_v8_global_t globals = { 0 }; + +/* Loadable module struct, used for external extension modules */ typedef struct { char *filename; void *lib; @@ -258,7 +272,6 @@ static switch_status_t load_modules(void) const char *EXT = ".SO"; #endif - memset(&module_manager, 0, sizeof(module_manager)); switch_core_new_memory_pool(&module_manager.pool); switch_core_hash_init(&module_manager.load_hash, module_manager.pool); @@ -688,8 +701,53 @@ static void v8_thread_launch(const char *text) switch_thread_create(&thread, thd_attr, v8_thread_run, task, pool); } +void v8_add_event_handler(void *event_handler) +{ + FSEventHandler *eh = static_cast(event_handler); + + if (eh) { + switch_mutex_lock(globals.event_mutex); + globals.event_handlers->insert(eh); + switch_mutex_unlock(globals.event_mutex); + } +} + +void v8_remove_event_handler(void *event_handler) +{ + FSEventHandler *eh = static_cast(event_handler); + + if (eh) { + switch_mutex_lock(globals.event_mutex); + + set::iterator it = globals.event_handlers->find(eh); + + if (it != globals.event_handlers->end()) { + globals.event_handlers->erase(it); + } + + switch_mutex_unlock(globals.event_mutex); + } +} + SWITCH_BEGIN_EXTERN_C +static void event_handler(switch_event_t *event) +{ + if (event) { + switch_mutex_lock(globals.event_mutex); + + set::iterator it; + + for (it = globals.event_handlers->begin(); it != globals.event_handlers->end(); ++it) { + if (*it) { + (*it)->QueueEvent(event); + } + } + + switch_mutex_unlock(globals.event_mutex); + } +} + SWITCH_STANDARD_API(jsapi_function) { char *path_info = NULL; @@ -769,6 +827,19 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load) switch_chat_application_interface_t *chat_app_interface; switch_json_api_interface_t *json_api_interface; + if (switch_event_bind_removable(modname, SWITCH_EVENT_ALL, SWITCH_EVENT_SUBCLASS_ANY, event_handler, NULL, &globals.event_node) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind to events\n"); + return SWITCH_STATUS_GENERR; + } + + if (switch_core_new_memory_pool(&globals.pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OH OH no pool\n"); + return SWITCH_STATUS_GENERR; + } + + switch_mutex_init(&globals.event_mutex, SWITCH_MUTEX_NESTED, globals.pool); + globals.event_handlers = new set(); + if (load_modules() != SWITCH_STATUS_SUCCESS) { return SWITCH_STATUS_FALSE; } @@ -786,6 +857,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load) v8_mod_init_built_in(FSTeleTone::GetModuleInterface()); v8_mod_init_built_in(FSXML::GetModuleInterface()); v8_mod_init_built_in(FSFile::GetModuleInterface()); + v8_mod_init_built_in(FSEventHandler::GetModuleInterface()); /* connect my internal structure to the blank pointer passed to me */ *module_interface = switch_loadable_module_create_module_interface(pool, modname); @@ -802,6 +874,12 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load) SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_v8_shutdown) { + switch_event_unbind(&globals.event_node); + + delete globals.event_handlers; + switch_mutex_destroy(globals.event_mutex); + switch_core_destroy_memory_pool(&globals.pool); + switch_core_hash_destroy(&module_manager.load_hash); return SWITCH_STATUS_SUCCESS; diff --git a/src/mod/languages/mod_v8/mod_v8.h b/src/mod/languages/mod_v8/mod_v8.h index 7cca8195f4..e40d6d27c8 100644 --- a/src/mod/languages/mod_v8/mod_v8.h +++ b/src/mod/languages/mod_v8/mod_v8.h @@ -40,15 +40,19 @@ SWITCH_BEGIN_EXTERN_C #define JS_BUFFER_SIZE 1024 * 32 #define JS_BLOCK_SIZE JS_BUFFER_SIZE +/* Function definition for initialization of an extension module */ typedef switch_status_t (*v8_mod_load_t) (const v8::FunctionCallbackInfo& info); +/* Extension module interface, stored inside the load_hash */ typedef struct { const char *name; v8_mod_load_t v8_mod_load; } v8_mod_interface_t; +/* Function definition for external extension module */ typedef switch_status_t (*v8_mod_init_t) (const v8_mod_interface_t **module_interface); +/* Struct that holds information about loadable extension modules */ typedef struct { switch_hash_t *load_hash; switch_memory_pool_t *pool; @@ -58,6 +62,9 @@ extern module_manager_t module_manager; SWITCH_END_EXTERN_C +void v8_add_event_handler(void *event_handler); +void v8_remove_event_handler(void *event_handler); + #endif /* MOD_V8_H */ /* For Emacs: diff --git a/src/mod/languages/mod_v8/src/fseventhandler.cpp b/src/mod/languages/mod_v8/src/fseventhandler.cpp new file mode 100644 index 0000000000..bef824d6ad --- /dev/null +++ b/src/mod/languages/mod_v8/src/fseventhandler.cpp @@ -0,0 +1,660 @@ +/* + * mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2013-2014, Peter Olsson + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Peter Olsson + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Peter Olsson + * + * fseventhandler.cpp -- JavaScript EventHandler class + * + */ + +#include "fseventhandler.hpp" +#include "fsevent.hpp" +#include "fssession.hpp" + +#define MAX_QUEUE_LEN 100000 + +using namespace std; +using namespace v8; + +typedef struct { + char *cmd; + char *arg; + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + int ack; + switch_memory_pool_t *pool; +} api_command_struct_t; + +static const char js_class_name[] = "EventHandler"; + +FSEventHandler::~FSEventHandler(void) +{ + v8_remove_event_handler(this); + + if (_event_hash) switch_core_hash_destroy(&_event_hash); + + if (_event_queue) { + void *pop; + + while (switch_queue_trypop(_event_queue, &pop) == SWITCH_STATUS_SUCCESS) { + switch_event_t *pevent = (switch_event_t *) pop; + if (pevent) { + switch_event_destroy(&pevent); + } + } + } + + if (_filters) switch_event_destroy(&_filters); + if (_mutex) switch_mutex_destroy(_mutex); + if (_pool) switch_core_destroy_memory_pool(&_pool); +} + +void FSEventHandler::Init() +{ + if (switch_core_new_memory_pool(&_pool) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "OH OH no pool\n"); + return; + } + + switch_mutex_init(&_mutex, SWITCH_MUTEX_NESTED, _pool); + switch_core_hash_init(&_event_hash, _pool); + switch_queue_create(&_event_queue, MAX_QUEUE_LEN, _pool); + + _filters = NULL; + memset(&_event_list, 0, sizeof(_event_list)); + + v8_add_event_handler(this); +} + +string FSEventHandler::GetJSClassName() +{ + return js_class_name; +} + +void FSEventHandler::QueueEvent(switch_event_t *event) +{ + switch_event_t *clone; + int send = 0; + + switch_mutex_lock(_mutex); + + if (_event_list[SWITCH_EVENT_ALL]) { + send = 1; + } else if ((_event_list[event->event_id])) { + if (event->event_id != SWITCH_EVENT_CUSTOM || !event->subclass_name || (switch_core_hash_find(_event_hash, event->subclass_name))) { + send = 1; + } + } + + if (send) { + if (_filters && _filters->headers) { + switch_event_header_t *hp; + const char *hval; + + send = 0; + + for (hp = _filters->headers; hp; hp = hp->next) { + if ((hval = switch_event_get_header(event, hp->name))) { + const char *comp_to = hp->value; + int pos = 1, cmp = 0; + + while (comp_to && *comp_to) { + if (*comp_to == '+') { + pos = 1; + } else if (*comp_to == '-') { + pos = 0; + } else if (*comp_to != ' ') { + break; + } + comp_to++; + } + + if (send && pos) { + continue; + } + + if (!comp_to) { + continue; + } + + if (*hp->value == '/') { + switch_regex_t *re = NULL; + int ovector[30]; + cmp = !!switch_regex_perform(hval, comp_to, &re, ovector, sizeof(ovector) / sizeof(ovector[0])); + switch_regex_safe_free(re); + } else { + cmp = !strcasecmp(hval, comp_to); + } + + if (cmp) { + if (pos) { + send = 1; + } else { + send = 0; + break; + } + } + } + } + } + } + + switch_mutex_unlock(_mutex); + + if (send) { + if (switch_event_dup(&clone, event) == SWITCH_STATUS_SUCCESS) { + + if (switch_queue_trypush(_event_queue, clone) == SWITCH_STATUS_SUCCESS) { + // TODO + /*if (l->lost_events) { + int le = l->lost_events; + l->lost_events = 0; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(l->session), SWITCH_LOG_CRIT, "Lost %d events!\n", le); + }*/ + } else { + /*if (++l->lost_events > MAX_MISSED) { + kill_listener(l, NULL); + }*/ + switch_event_destroy(&clone); + } + } else { + ////switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(l->session), SWITCH_LOG_ERROR, "Memory Error!\n"); + } + } +} + +static char *MARKER = "1"; + +void FSEventHandler::DoSubscribe(const v8::FunctionCallbackInfo& info) +{ + int i, custom = 0; + bool ret = false; + + for (i = 0; i < info.Length(); i++) { + String::Utf8Value str(info[i]); + switch_event_types_t etype; + + if (custom) { + switch_mutex_lock(_mutex); + switch_core_hash_insert(_event_hash, js_safe_str(*str), MARKER); + switch_mutex_unlock(_mutex); + } else if (switch_name_event(js_safe_str(*str), &etype) == SWITCH_STATUS_SUCCESS) { + ret = true; + + if (etype == SWITCH_EVENT_ALL) { + uint32_t x = 0; + for (x = 0; x < SWITCH_EVENT_ALL; x++) { + _event_list[x] = 1; + } + } + + if (etype <= SWITCH_EVENT_ALL) { + _event_list[etype] = 1; + } + + if (etype == SWITCH_EVENT_CUSTOM) { + custom++; + } + } + } + + info.GetReturnValue().Set(ret); +} + +void *FSEventHandler::Construct(const v8::FunctionCallbackInfo& info) +{ + FSEventHandler *obj = new FSEventHandler(info); + obj->DoSubscribe(info); + return obj; +} + +JS_EVENTHANDLER_FUNCTION_IMPL(Subscribe) +{ + DoSubscribe(info); +} + +JS_EVENTHANDLER_FUNCTION_IMPL(UnSubscribe) +{ + int i, custom = 0; + bool ret = false; + + for (i = 0; i < info.Length(); i++) { + String::Utf8Value str(info[i]); + switch_event_types_t etype; + + if (custom) { + switch_mutex_lock(_mutex); + switch_core_hash_delete(_event_hash, js_safe_str(*str)); + switch_mutex_unlock(_mutex); + } else if (switch_name_event(js_safe_str(*str), &etype) == SWITCH_STATUS_SUCCESS) { + uint32_t x = 0; + ret = true; + + if (etype == SWITCH_EVENT_CUSTOM) { + custom++; + } else if (etype == SWITCH_EVENT_ALL) { + for (x = 0; x <= SWITCH_EVENT_ALL; x++) { + _event_list[x] = 0; + } + } else { + if (_event_list[SWITCH_EVENT_ALL]) { + _event_list[SWITCH_EVENT_ALL] = 0; + for (x = 0; x < SWITCH_EVENT_ALL; x++) { + _event_list[x] = 1; + } + } + _event_list[etype] = 0; + } + } + } + + info.GetReturnValue().Set(ret); +} + +JS_EVENTHANDLER_FUNCTION_IMPL(DeleteFilter) +{ + if (info.Length() < 1) { + info.GetReturnValue().Set(false); + } else { + String::Utf8Value str(info[0]); + const char *headerName = js_safe_str(*str); + + if (zstr(headerName)) { + info.GetReturnValue().Set(false); + return; + } + + switch_mutex_lock(_mutex); + + if (!_filters) { + switch_event_create_plain(&_filters, SWITCH_EVENT_CLONE); + } + + if (!strcasecmp(headerName, "all")) { + switch_event_destroy(&_filters); + switch_event_create_plain(&_filters, SWITCH_EVENT_CLONE); + } else { + switch_event_del_header(_filters, headerName); + } + + info.GetReturnValue().Set(true); + + switch_mutex_unlock(_mutex); + } +} + +JS_EVENTHANDLER_FUNCTION_IMPL(AddFilter) +{ + if (info.Length() < 2) { + info.GetReturnValue().Set(false); + } else { + String::Utf8Value str1(info[0]); + String::Utf8Value str2(info[1]); + const char *headerName = js_safe_str(*str1); + const char *headerVal = js_safe_str(*str2); + + if (zstr(headerName) || zstr(headerVal)) { + info.GetReturnValue().Set(false); + return; + } + + switch_mutex_lock(_mutex); + + if (!_filters) { + switch_event_create_plain(&_filters, SWITCH_EVENT_CLONE); + } + + switch_event_add_header_string(_filters, SWITCH_STACK_BOTTOM, headerName, headerVal); + + info.GetReturnValue().Set(true); + + switch_mutex_unlock(_mutex); + } +} + +JS_EVENTHANDLER_FUNCTION_IMPL(GetEvent) +{ + void *pop = NULL; + int timeout = 0; + switch_event_t *pevent = NULL; + + if (info.Length() > 0 && !info[0].IsEmpty()) { + timeout = info[0]->Int32Value(); + } + + if (timeout > 0) { + if (switch_queue_pop_timeout(_event_queue, &pop, (switch_interval_time_t) timeout * 1000) == SWITCH_STATUS_SUCCESS && pop) { + pevent = (switch_event_t *) pop; + } + } else { + if (switch_queue_trypop(_event_queue, &pop) == SWITCH_STATUS_SUCCESS && pop) { + pevent = (switch_event_t *) pop; + } + } + + if (pevent) { + FSEvent *evt = new FSEvent(info); + evt->SetEvent(pevent, 0); + evt->RegisterInstance(info.GetIsolate(), "", true); + info.GetReturnValue().Set(evt->GetJavaScriptObject()); + } else { + info.GetReturnValue().Set(Null(info.GetIsolate())); + } +} + +JS_EVENTHANDLER_FUNCTION_IMPL(SendEvent) +{ + if (info.Length() == 0) { + info.GetReturnValue().Set(false); + } else { + if (!info[0].IsEmpty() && info[0]->IsObject()) { + FSEvent *evt = JSBase::GetInstance(info[0]->ToObject()); + switch_event_t **event; + + if (!evt || !(event = evt->GetEvent())) { + info.GetReturnValue().Set(false); + } else { + string session_uuid; + + if (info.Length() > 1) { + if (!info[1].IsEmpty() && info[1]->IsObject()) { + /* The second argument is a session object */ + FSSession *sess = JSBase::GetInstance(info[1]->ToObject()); + switch_core_session_t *tmp; + + if (sess && (tmp = sess->GetSession())) { + session_uuid = switch_core_session_get_uuid(tmp); + } + } else { + /* The second argument is a session uuid string */ + String::Utf8Value str(info[1]); + session_uuid = js_safe_str(*str); + } + } + + if (session_uuid.length() > 0) { + /* This is a session event */ + switch_core_session_t *session; + switch_status_t status = SWITCH_STATUS_FALSE; + + if ((session = switch_core_session_locate(session_uuid.c_str()))) { + if ((status = switch_core_session_queue_private_event(session, event, SWITCH_FALSE)) == SWITCH_STATUS_SUCCESS) { + info.GetReturnValue().Set(true); + } else { + info.GetReturnValue().Set(false); + } + switch_core_session_rwunlock(session); + } else { + info.GetReturnValue().Set(false); + // TODO LOGGING + //switch_snprintf(reply, reply_len, "-ERR invalid session id [%s]", switch_str_nil(uuid)); + } + } else { + /* "Normal" event */ + // TODO LOGGING + switch_event_fire(event); + } + } + } + } +} + +JS_EVENTHANDLER_FUNCTION_IMPL(ExecuteApi) +{ + if (info.Length() > 0) { + String::Utf8Value str(info[0]); + const char *cmd = js_safe_str(*str); + string arg; + switch_stream_handle_t stream = { 0 }; + + if (!strcasecmp(cmd, "jsapi")) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Possible recursive API Call is not allowed\n"); + info.GetReturnValue().Set(false); + return; + } + + if (info.Length() > 1) { + String::Utf8Value str2(info[1]); + arg = js_safe_str(*str2); + } + + SWITCH_STANDARD_STREAM(stream); + switch_api_execute(cmd, arg.c_str(), NULL, &stream); + + info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_str_nil((char *) stream.data))); + switch_safe_free(stream.data); + } else { + info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), "-ERR")); + } +} + +static void *SWITCH_THREAD_FUNC api_exec(switch_thread_t *thread, void *obj) +{ + api_command_struct_t *acs = (api_command_struct_t *) obj; + switch_stream_handle_t stream = { 0 }; + char *reply, *freply = NULL; + switch_status_t status; + switch_event_t *event; + + if (!acs) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Internal error.\n"); + return NULL; + } + + acs->ack = 1; + + SWITCH_STANDARD_STREAM(stream); + + status = switch_api_execute(acs->cmd, acs->arg, NULL, &stream); + + if (status == SWITCH_STATUS_SUCCESS) { + reply = (char *)stream.data; + } else { + freply = switch_mprintf("-ERR %s Command not found!\n", acs->cmd); + reply = freply; + } + + if (!reply) { + reply = "Command returned no output!"; + } + + if (switch_event_create(&event, SWITCH_EVENT_BACKGROUND_JOB) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-UUID", acs->uuid_str); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-Command", acs->cmd); + if (acs->arg) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-Command-Arg", acs->arg); + } + switch_event_add_body(event, "%s", reply); + switch_event_fire(&event); + } + + switch_safe_free(stream.data); + switch_safe_free(freply); + + switch_memory_pool_t *pool = acs->pool; + if (acs->ack == -1) { + int sanity = 2000; + while (acs->ack == -1) { + switch_cond_next(); + if (--sanity <= 0) + break; + } + } + + acs = NULL; + switch_core_destroy_memory_pool(&pool); + pool = NULL; + + return NULL; +} + +JS_EVENTHANDLER_FUNCTION_IMPL(ExecuteBgApi) +{ + string cmd; + string arg; + string jobuuid; + api_command_struct_t *acs = NULL; + switch_memory_pool_t *pool; + switch_thread_t *thread; + switch_threadattr_t *thd_attr = NULL; + switch_uuid_t uuid; + int sanity = 2000; + + if (info.Length() > 0) { + String::Utf8Value str(info[0]); + cmd = js_safe_str(*str); + + if (info.Length() > 1) { + String::Utf8Value str2(info[1]); + arg = js_safe_str(*str2); + } + + if (info.Length() > 2) { + String::Utf8Value str2(info[2]); + jobuuid = js_safe_str(*str2); + } + } else { + info.GetReturnValue().Set(false); + return; + } + + if (cmd.length() == 0) { + info.GetReturnValue().Set(false); + return; + } + + switch_core_new_memory_pool(&pool); + acs = (api_command_struct_t *)switch_core_alloc(pool, sizeof(*acs)); + switch_assert(acs); + acs->pool = pool; + + acs->cmd = switch_core_strdup(acs->pool, cmd.c_str()); + + if (arg.c_str()) { + acs->arg = switch_core_strdup(acs->pool, arg.c_str()); + } + + switch_threadattr_create(&thd_attr, acs->pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE); + + if (jobuuid.length() > 0) { + switch_copy_string(acs->uuid_str, jobuuid.c_str(), sizeof(acs->uuid_str)); + } else { + switch_uuid_get(&uuid); + switch_uuid_format(acs->uuid_str, &uuid); + } + + info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), acs->uuid_str)); + + switch_thread_create(&thread, thd_attr, api_exec, acs, acs->pool); + + while (!acs->ack) { + switch_cond_next(); + if (--sanity <= 0) + break; + } + + if (acs->ack == -1) { + acs->ack--; + } +} + +JS_EVENTHANDLER_FUNCTION_IMPL_STATIC(Destroy) +{ + JS_CHECK_SCRIPT_STATE(); + + FSEventHandler *obj = JSBase::GetInstance(info.Holder()); + + if (obj) { + delete obj; + info.GetReturnValue().Set(true); + } else { + info.GetReturnValue().Set(false); + } +} + +JS_EVENTHANDLER_GET_PROPERTY_IMPL_STATIC(GetReadyProperty) +{ + JS_CHECK_SCRIPT_STATE(); + + FSEventHandler *obj = JSBase::GetInstance(info.Holder()); + + if (obj) { + info.GetReturnValue().Set(true); + } else { + info.GetReturnValue().Set(false); + } +} + +static const js_function_t eventhandler_methods[] = { + {"subscribe", FSEventHandler::Subscribe}, + {"unSubscribe", FSEventHandler::UnSubscribe}, + {"addFilter", FSEventHandler::AddFilter}, + {"deleteFilter", FSEventHandler::DeleteFilter}, + {"getEvent", FSEventHandler::GetEvent}, + {"sendEvent", FSEventHandler::SendEvent}, + {"executeApi", FSEventHandler::ExecuteApi}, + {"executeBgApi", FSEventHandler::ExecuteBgApi}, + {"destroy", FSEventHandler::Destroy}, + {0} +}; + +static const js_property_t eventhandler_props[] = { + {"ready", FSEventHandler::GetReadyProperty, JSBase::DefaultSetProperty}, + {0} +}; + +static const js_class_definition_t eventhandler_desc = { + js_class_name, + FSEventHandler::Construct, + eventhandler_methods, + eventhandler_props +}; + +static switch_status_t eventhandler_load(const v8::FunctionCallbackInfo& info) +{ + JSBase::Register(info.GetIsolate(), &eventhandler_desc); + return SWITCH_STATUS_SUCCESS; +} + +static const v8_mod_interface_t eventhandler_module_interface = { + /*.name = */ js_class_name, + /*.js_mod_load */ eventhandler_load +}; + +const v8_mod_interface_t *FSEventHandler::GetModuleInterface() +{ + return &eventhandler_module_interface; +} + +/* For Emacs: + * Local Variables: + * mode:c + * indent-tabs-mode:t + * tab-width:4 + * c-basic-offset:4 + * End: + * For VIM: + * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet: + */ diff --git a/src/mod/languages/mod_v8/src/jsbase.cpp b/src/mod/languages/mod_v8/src/jsbase.cpp index 35aa0c46cd..f8c34086f3 100644 --- a/src/mod/languages/mod_v8/src/jsbase.cpp +++ b/src/mod/languages/mod_v8/src/jsbase.cpp @@ -69,7 +69,7 @@ JSBase::~JSBase(void) } /* If the object is still alive inside V8, set the internal field to NULL. But only if we're actually inside a JS context */ - if (!persistentHandle->IsNearDeath() && !GetIsolate()->GetCurrentContext().IsEmpty()) { + if (!persistentHandle->IsNearDeath() && !GetIsolate()->GetCurrentContext().IsEmpty() && (!js || !js->GetForcedTermination())) { Handle jsObj = GetJavaScriptObject(); jsObj->SetInternalField(0, Null(GetIsolate())); }