]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
mod_v8: Added new extension class that makes it possible to subscribe to FS events...
authorPeter Olsson <peter@olssononline.se>
Sat, 1 Feb 2014 17:20:04 +0000 (18:20 +0100)
committerPeter Olsson <peter@olssononline.se>
Sat, 1 Feb 2014 17:20:04 +0000 (18:20 +0100)
src/mod/languages/mod_v8/Makefile.am
src/mod/languages/mod_v8/include/fseventhandler.hpp [new file with mode: 0644]
src/mod/languages/mod_v8/mod_v8.2010.vcxproj
src/mod/languages/mod_v8/mod_v8.2010.vcxproj.filters
src/mod/languages/mod_v8/mod_v8.2012.vcxproj
src/mod/languages/mod_v8/mod_v8.2012.vcxproj.filters
src/mod/languages/mod_v8/mod_v8.cpp
src/mod/languages/mod_v8/mod_v8.h
src/mod/languages/mod_v8/src/fseventhandler.cpp [new file with mode: 0644]
src/mod/languages/mod_v8/src/jsbase.cpp

index c1e7a035b0c49d7ab05286614e1cd37806b1d55e..fc6b67a658f190779cba02a5382ec6cd7443205d 100644 (file)
@@ -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 (file)
index 0000000..cfc9c37
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013-2014, Peter Olsson <peter@olssononline.se>
+ *
+ * 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 <peter@olssononline.se>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Peter Olsson <peter@olssononline.se>
+ *
+ * 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<v8::Value>& info);
+public:
+       FSEventHandler(JSMain *owner) : JSBase(owner) { Init(); }
+       FSEventHandler(const v8::FunctionCallbackInfo<v8::Value>& 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<v8::Value>& 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:
+ */
index bc5175a4a7d042aa2e0120a1cde0bd042e1ff154..101f0ffaadb6239b815e4d5eae05412ae5eb8277 100644 (file)
     <ClCompile Include=".\src\fsglobal.cpp" />\r
     <ClCompile Include=".\src\fsteletone.cpp" />\r
     <ClCompile Include="mod_v8.cpp" />\r
+    <ClCompile Include="src\fseventhandler.cpp" />\r
     <ClCompile Include="src\fsfile.cpp" />\r
     <ClCompile Include="src\fsxml.cpp" />\r
   </ItemGroup>\r
     <ClInclude Include=".\include\fssocket.hpp" />\r
     <ClInclude Include=".\include\fsglobal.hpp" />\r
     <ClInclude Include=".\include\fsteletone.hpp" />\r
+    <ClInclude Include="include\fseventhandler.hpp" />\r
     <ClInclude Include="include\fsfile.hpp" />\r
     <ClInclude Include="include\fsxml.hpp" />\r
     <ClInclude Include="mod_v8.h" />\r
index 1f35875ef77da4d26802d06d64b9c4eed6f3e28b..77911bad54a1d7b8a6acc9a2c23f2ce276c63859 100644 (file)
@@ -50,6 +50,9 @@
     <ClCompile Include="src\fsxml.cpp">\r
       <Filter>FSClasses</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="src\fseventhandler.cpp">\r
+      <Filter>FSClasses</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="mod_v8.h" />\r
     <ClInclude Include="include\fsxml.hpp">\r
       <Filter>FSClasses\include</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="include\fseventhandler.hpp">\r
+      <Filter>FSClasses\include</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <Filter Include="BaseClasses">\r
index 9595223bbc0bb3f02f3185f3d6d0f88647628946..2ff8aaabc6eb82e8619235a962bada09f02ac2c4 100644 (file)
     <ClCompile Include=".\src\fsglobal.cpp" />\r
     <ClCompile Include=".\src\fsteletone.cpp" />\r
     <ClCompile Include="mod_v8.cpp" />\r
+    <ClCompile Include="src\fseventhandler.cpp" />\r
     <ClCompile Include="src\fsfile.cpp" />\r
     <ClCompile Include="src\fsxml.cpp" />\r
   </ItemGroup>\r
     <ClInclude Include=".\include\fssocket.hpp" />\r
     <ClInclude Include=".\include\fsglobal.hpp" />\r
     <ClInclude Include=".\include\fsteletone.hpp" />\r
+    <ClInclude Include="include\fseventhandler.hpp" />\r
     <ClInclude Include="include\fsfile.hpp" />\r
     <ClInclude Include="include\fsxml.hpp" />\r
     <ClInclude Include="mod_v8.h" />\r
index 1f35875ef77da4d26802d06d64b9c4eed6f3e28b..77911bad54a1d7b8a6acc9a2c23f2ce276c63859 100644 (file)
@@ -50,6 +50,9 @@
     <ClCompile Include="src\fsxml.cpp">\r
       <Filter>FSClasses</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="src\fseventhandler.cpp">\r
+      <Filter>FSClasses</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="mod_v8.h" />\r
     <ClInclude Include="include\fsxml.hpp">\r
       <Filter>FSClasses\include</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="include\fseventhandler.hpp">\r
+      <Filter>FSClasses\include</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <Filter Include="BaseClasses">\r
index 2e4bdc3b33cd38f957dd37b0f9d9581b175902af..4e72fbd8a8bddbd7d624116dc346e4ca9e4103b0 100644 (file)
  * 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 <set>
 
 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<FSEventHandler *> *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<FSEventHandler *>(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<FSEventHandler *>(event_handler);
+
+       if (eh) {
+               switch_mutex_lock(globals.event_mutex);
+
+               set<FSEventHandler *>::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<FSEventHandler *>::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<FSEventHandler *>();
+
        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;
index 7cca8195f44edb81be52c8f828d91325a36c672a..e40d6d27c87797897f907dd86a4c7561217c8ec0 100644 (file)
@@ -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<v8::Value>& 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 (file)
index 0000000..bef824d
--- /dev/null
@@ -0,0 +1,660 @@
+/*
+ * mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2013-2014, Peter Olsson <peter@olssononline.se>
+ *
+ * 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 <peter@olssononline.se>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Peter Olsson <peter@olssononline.se>
+ *
+ * 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 {\r
+       char *cmd;\r
+       char *arg;\r
+       char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];\r
+       int ack;\r
+       switch_memory_pool_t *pool;\r
+} api_command_struct_t;\r
+
+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]) {\r
+               send = 1;\r
+       } else if ((_event_list[event->event_id])) {\r
+               if (event->event_id != SWITCH_EVENT_CUSTOM || !event->subclass_name || (switch_core_hash_find(_event_hash, event->subclass_name))) {\r
+                       send = 1;\r
+               }\r
+       }\r
+\r
+       if (send) {\r
+               if (_filters && _filters->headers) {\r
+                       switch_event_header_t *hp;\r
+                       const char *hval;\r
+\r
+                       send = 0;\r
+\r
+                       for (hp = _filters->headers; hp; hp = hp->next) {\r
+                               if ((hval = switch_event_get_header(event, hp->name))) {\r
+                                       const char *comp_to = hp->value;\r
+                                       int pos = 1, cmp = 0;\r
+\r
+                                       while (comp_to && *comp_to) {\r
+                                               if (*comp_to == '+') {\r
+                                                       pos = 1;\r
+                                               } else if (*comp_to == '-') {\r
+                                                       pos = 0;\r
+                                               } else if (*comp_to != ' ') {\r
+                                                       break;\r
+                                               }\r
+                                               comp_to++;\r
+                                       }\r
+\r
+                                       if (send && pos) {\r
+                                               continue;\r
+                                       }\r
+\r
+                                       if (!comp_to) {\r
+                                               continue;\r
+                                       }\r
+\r
+                                       if (*hp->value == '/') {\r
+                                               switch_regex_t *re = NULL;\r
+                                               int ovector[30];\r
+                                               cmp = !!switch_regex_perform(hval, comp_to, &re, ovector, sizeof(ovector) / sizeof(ovector[0]));\r
+                                               switch_regex_safe_free(re);\r
+                                       } else {\r
+                                               cmp = !strcasecmp(hval, comp_to);\r
+                                       }\r
+\r
+                                       if (cmp) {\r
+                                               if (pos) {\r
+                                                       send = 1;\r
+                                               } else {\r
+                                                       send = 0;\r
+                                                       break;\r
+                                               }\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       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<v8::Value>& 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) {\r
+                       switch_mutex_lock(_mutex);\r
+                       switch_core_hash_insert(_event_hash, js_safe_str(*str), MARKER);\r
+                       switch_mutex_unlock(_mutex);\r
+               } else if (switch_name_event(js_safe_str(*str), &etype) == SWITCH_STATUS_SUCCESS) {\r
+                       ret = true;\r
+\r
+                       if (etype == SWITCH_EVENT_ALL) {\r
+                               uint32_t x = 0;\r
+                               for (x = 0; x < SWITCH_EVENT_ALL; x++) {\r
+                                       _event_list[x] = 1;\r
+                               }\r
+                       }\r
+\r
+                       if (etype <= SWITCH_EVENT_ALL) {\r
+                               _event_list[etype] = 1;\r
+                       }\r
+\r
+                       if (etype == SWITCH_EVENT_CUSTOM) {\r
+                               custom++;\r
+                       }\r
+               }\r
+       }
+       
+       info.GetReturnValue().Set(ret);
+}
+
+void *FSEventHandler::Construct(const v8::FunctionCallbackInfo<v8::Value>& 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) {\r
+                       switch_mutex_lock(_mutex);\r
+                       switch_core_hash_delete(_event_hash, js_safe_str(*str));\r
+                       switch_mutex_unlock(_mutex);\r
+               } else if (switch_name_event(js_safe_str(*str), &etype) == SWITCH_STATUS_SUCCESS) {\r
+                       uint32_t x = 0;\r
+                       ret = true;\r
+\r
+                       if (etype == SWITCH_EVENT_CUSTOM) {\r
+                               custom++;\r
+                       } else if (etype == SWITCH_EVENT_ALL) {\r
+                               for (x = 0; x <= SWITCH_EVENT_ALL; x++) {\r
+                                       _event_list[x] = 0;\r
+                               }\r
+                       } else {\r
+                               if (_event_list[SWITCH_EVENT_ALL]) {\r
+                                       _event_list[SWITCH_EVENT_ALL] = 0;\r
+                                       for (x = 0; x < SWITCH_EVENT_ALL; x++) {\r
+                                               _event_list[x] = 1;\r
+                                       }\r
+                               }\r
+                               _event_list[etype] = 0;\r
+                       }\r
+               }\r
+       }
+       
+       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)) {\r
+                       info.GetReturnValue().Set(false);\r
+                       return;\r
+               }\r
+
+               switch_mutex_lock(_mutex);
+
+               if (!_filters) {\r
+                       switch_event_create_plain(&_filters, SWITCH_EVENT_CLONE);\r
+               }\r
+\r
+               if (!strcasecmp(headerName, "all")) {\r
+                       switch_event_destroy(&_filters);\r
+                       switch_event_create_plain(&_filters, SWITCH_EVENT_CLONE);\r
+               } else {\r
+                       switch_event_del_header(_filters, headerName);\r
+               }\r
+\r
+               info.GetReturnValue().Set(true);\r
+\r
+               switch_mutex_unlock(_mutex);\r
+       }
+}
+
+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)) {\r
+                       info.GetReturnValue().Set(false);\r
+                       return;\r
+               }\r
+
+               switch_mutex_lock(_mutex);
+
+               if (!_filters) {\r
+                       switch_event_create_plain(&_filters, SWITCH_EVENT_CLONE);\r
+               }\r
+\r
+               switch_event_add_header_string(_filters, SWITCH_STACK_BOTTOM, headerName, headerVal);\r
+\r
+               info.GetReturnValue().Set(true);\r
+\r
+               switch_mutex_unlock(_mutex);\r
+       }
+}
+
+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<FSEvent>(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<FSSession>(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()))) {\r
+                                               if ((status = switch_core_session_queue_private_event(session, event, SWITCH_FALSE)) == SWITCH_STATUS_SUCCESS) {\r
+                                                       info.GetReturnValue().Set(true);\r
+                                               } else {\r
+                                                       info.GetReturnValue().Set(false);\r
+                                               }\r
+                                               switch_core_session_rwunlock(session);\r
+                                       } else {\r
+                                               info.GetReturnValue().Set(false);\r
+                                               // TODO LOGGING\r
+                                               //switch_snprintf(reply, reply_len, "-ERR invalid session id [%s]", switch_str_nil(uuid));\r
+                                       }\r
+                               } 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)\r
+{\r
+       api_command_struct_t *acs = (api_command_struct_t *) obj;\r
+       switch_stream_handle_t stream = { 0 };\r
+       char *reply, *freply = NULL;\r
+       switch_status_t status;\r
+       switch_event_t *event;\r
+\r
+       if (!acs) {\r
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Internal error.\n");\r
+               return NULL;\r
+       }\r
+\r
+       acs->ack = 1;\r
+\r
+       SWITCH_STANDARD_STREAM(stream);\r
+\r
+       status = switch_api_execute(acs->cmd, acs->arg, NULL, &stream);\r
+\r
+       if (status == SWITCH_STATUS_SUCCESS) {\r
+               reply = (char *)stream.data;\r
+       } else {\r
+               freply = switch_mprintf("-ERR %s Command not found!\n", acs->cmd);\r
+               reply = freply;\r
+       }\r
+\r
+       if (!reply) {\r
+               reply = "Command returned no output!";\r
+       }\r
+\r
+       if (switch_event_create(&event, SWITCH_EVENT_BACKGROUND_JOB) == SWITCH_STATUS_SUCCESS) {\r
+               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-UUID", acs->uuid_str);\r
+               switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-Command", acs->cmd);\r
+               if (acs->arg) {\r
+                       switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Job-Command-Arg", acs->arg);\r
+               }\r
+               switch_event_add_body(event, "%s", reply);\r
+               switch_event_fire(&event);\r
+       }\r
+\r
+       switch_safe_free(stream.data);\r
+       switch_safe_free(freply);\r
+\r
+       switch_memory_pool_t *pool = acs->pool;\r
+       if (acs->ack == -1) {\r
+               int sanity = 2000;\r
+               while (acs->ack == -1) {\r
+                       switch_cond_next();\r
+                       if (--sanity <= 0)\r
+                               break;\r
+               }\r
+       }\r
+\r
+       acs = NULL;\r
+       switch_core_destroy_memory_pool(&pool);\r
+       pool = NULL;\r
+\r
+       return NULL;\r
+}\r
+
+JS_EVENTHANDLER_FUNCTION_IMPL(ExecuteBgApi)
+{
+       string cmd;\r
+       string arg;\r
+       string jobuuid;\r
+       api_command_struct_t *acs = NULL;\r
+       switch_memory_pool_t *pool;\r
+       switch_thread_t *thread;\r
+       switch_threadattr_t *thd_attr = NULL;\r
+       switch_uuid_t uuid;\r
+       int sanity = 2000;\r
+\r
+       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;
+       }
+\r
+       switch_core_new_memory_pool(&pool);\r
+       acs = (api_command_struct_t *)switch_core_alloc(pool, sizeof(*acs));\r
+       switch_assert(acs);\r
+       acs->pool = pool;\r
+\r
+       acs->cmd = switch_core_strdup(acs->pool, cmd.c_str());\r
+\r
+       if (arg.c_str()) {\r
+               acs->arg = switch_core_strdup(acs->pool, arg.c_str());\r
+       }\r
+\r
+       switch_threadattr_create(&thd_attr, acs->pool);\r
+       switch_threadattr_detach_set(thd_attr, 1);\r
+       switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);\r
+\r
+       if (jobuuid.length() > 0) {\r
+               switch_copy_string(acs->uuid_str, jobuuid.c_str(), sizeof(acs->uuid_str));\r
+       } else {\r
+               switch_uuid_get(&uuid);\r
+               switch_uuid_format(acs->uuid_str, &uuid);\r
+       }\r
+\r
+       info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), acs->uuid_str));\r
+\r
+       switch_thread_create(&thread, thd_attr, api_exec, acs, acs->pool);\r
+\r
+       while (!acs->ack) {\r
+               switch_cond_next();\r
+               if (--sanity <= 0)\r
+                       break;\r
+       }\r
+\r
+       if (acs->ack == -1) {\r
+               acs->ack--;\r
+       }\r
+}
+
+JS_EVENTHANDLER_FUNCTION_IMPL_STATIC(Destroy)
+{
+       JS_CHECK_SCRIPT_STATE();
+
+       FSEventHandler *obj = JSBase::GetInstance<FSEventHandler>(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<FSEventHandler>(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<Value>& 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:
+ */
index 35aa0c46cd62fc474adc9069685af545209b0d4d..f8c34086f38756355892043df18c4596a5350d69 100644 (file)
@@ -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<Object> jsObj = GetJavaScriptObject();
                jsObj->SetInternalField(0, Null(GetIsolate()));
        }