diff --git a/src/mod/languages/mod_v8/Makefile.am b/src/mod/languages/mod_v8/Makefile.am index 69ad7ea49e..7b3abc8a9d 100644 --- a/src/mod/languages/mod_v8/Makefile.am +++ b/src/mod/languages/mod_v8/Makefile.am @@ -80,6 +80,7 @@ mod_v8_la_SOURCES = \ src/jsmain.cpp \ src/jsbase.cpp \ src/fscoredb.cpp \ + src/fsdbh.cpp \ src/fscurl.cpp \ src/fsdtmf.cpp \ src/fsevent.cpp \ diff --git a/src/mod/languages/mod_v8/include/fsdbh.hpp b/src/mod/languages/mod_v8/include/fsdbh.hpp new file mode 100644 index 0000000000..f82dbb83da --- /dev/null +++ b/src/mod/languages/mod_v8/include/fsdbh.hpp @@ -0,0 +1,96 @@ +/* + * 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 + * Andrey Volk + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Peter Olsson + * Andrey Volk + * + * fsdbh.hpp -- JavaScript DBH class header + * + */ + +#ifndef FS_DBH_H +#define FS_DBH_H + +#include "mod_v8.h" + +/* Macros for easier V8 callback definitions */ +#define JS_DBH_GET_PROPERTY_DEF(method_name) JS_GET_PROPERTY_DEF(method_name, FSDBH) +#define JS_DBH_SET_PROPERTY_DEF(method_name) JS_SET_PROPERTY_DEF(method_name, FSDBH) +#define JS_DBH_FUNCTION_DEF(method_name) JS_FUNCTION_DEF(method_name, FSDBH) +#define JS_DBH_GET_PROPERTY_IMPL(method_name) JS_GET_PROPERTY_IMPL(method_name, FSDBH) +#define JS_DBH_SET_PROPERTY_IMPL(method_name) JS_SET_PROPERTY_IMPL(method_name, FSDBH) +#define JS_DBH_FUNCTION_IMPL(method_name) JS_FUNCTION_IMPL(method_name, FSDBH) + +#define JS_DBH_GET_PROPERTY_IMPL_STATIC(method_name) JS_GET_PROPERTY_IMPL_STATIC(method_name, FSDBH) +#define JS_DBH_SET_PROPERTY_IMPL_STATIC(method_name) JS_SET_PROPERTY_IMPL_STATIC(method_name, FSDBH) +#define JS_DBH_FUNCTION_IMPL_STATIC(method_name) JS_FUNCTION_IMPL_STATIC(method_name, FSDBH) + +class FSDBH : public JSBase +{ +private: + v8::Persistent _callback; + std::string _dsn; + switch_cache_db_handle_t *dbh; + char *err; + + void Init(); + + bool _release(); + void clear_error(); + + static int Callback(void *pArg, int argc, char **argv, char **columnNames); +public: + FSDBH(JSMain *owner) : JSBase(owner) { Init(); } + FSDBH(const v8::FunctionCallbackInfo& info) : JSBase(info) { Init(); } + + virtual ~FSDBH(void); + virtual std::string GetJSClassName(); + + static const v8_mod_interface_t *GetModuleInterface(); + + static FSDBH *New(char *dsn, char *username, char *password, const v8::FunctionCallbackInfo& info); + + /* Methods available from JavaScript */ + static void *Construct(const v8::FunctionCallbackInfo& info); + JS_DBH_FUNCTION_DEF(affected_rows); + JS_DBH_FUNCTION_DEF(connected); + JS_DBH_FUNCTION_DEF(last_error); + JS_DBH_FUNCTION_DEF(query); + JS_DBH_FUNCTION_DEF(release); + JS_DBH_FUNCTION_DEF(test_reactive); + JS_DBH_GET_PROPERTY_DEF(GetProperty); +}; + +#endif /* FS_DB_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.filters b/src/mod/languages/mod_v8/mod_v8.2010.vcxproj.filters index 77911bad54..42de878a39 100644 --- a/src/mod/languages/mod_v8/mod_v8.2010.vcxproj.filters +++ b/src/mod/languages/mod_v8/mod_v8.2010.vcxproj.filters @@ -11,6 +11,9 @@ FSClasses + + FSClasses + FSClasses @@ -62,6 +65,9 @@ FSClasses\include + + FSClasses\include + FSClasses\include diff --git a/src/mod/languages/mod_v8/mod_v8.2015.vcxproj b/src/mod/languages/mod_v8/mod_v8.2015.vcxproj index 543e5c26f5..135a8abc70 100644 --- a/src/mod/languages/mod_v8/mod_v8.2015.vcxproj +++ b/src/mod/languages/mod_v8/mod_v8.2015.vcxproj @@ -170,6 +170,7 @@ + @@ -189,6 +190,7 @@ + diff --git a/src/mod/languages/mod_v8/mod_v8.cpp b/src/mod/languages/mod_v8/mod_v8.cpp index 4590b96867..955a94bd46 100644 --- a/src/mod/languages/mod_v8/mod_v8.cpp +++ b/src/mod/languages/mod_v8/mod_v8.cpp @@ -83,6 +83,7 @@ /* Optional JavaScript classes (loaded on demand) */ #include "fscoredb.hpp" +#include "fsdbh.hpp" #include "fscurl.hpp" #include "fsteletone.hpp" #include "fssocket.hpp" @@ -840,6 +841,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_v8_load) /* Make all "built in" modules available to load on demand */ v8_mod_init_built_in(FSCoreDB::GetModuleInterface()); + v8_mod_init_built_in(FSDBH::GetModuleInterface()); v8_mod_init_built_in(FSCURL::GetModuleInterface()); #ifdef HAVE_ODBC /* Only add ODBC class if ODBC is available in the system */ diff --git a/src/mod/languages/mod_v8/src/fsdbh.cpp b/src/mod/languages/mod_v8/src/fsdbh.cpp new file mode 100644 index 0000000000..cea4dbb552 --- /dev/null +++ b/src/mod/languages/mod_v8/src/fsdbh.cpp @@ -0,0 +1,400 @@ +/* + * 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. + * + * Ported from the Original Code in FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Andrey Volk + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Peter Olsson + * Andrey Volk + * + * fsdbh.cpp -- JavaScript DBH class + * + */ + +#include "fsdbh.hpp" + +using namespace std; +using namespace v8; + +static const char js_class_name[] = "DBH"; + +FSDBH::~FSDBH() +{ + if (dbh) _release(); + clear_error(); +} + +bool FSDBH::_release() +{ + if (dbh) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "DBH handle %p released.\n", (void *)dbh); + switch_cache_db_release_db_handle(&dbh); + return true; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DBH NOT Connected.\n"); + return false; +} + +string FSDBH::GetJSClassName() +{ + return js_class_name; +} + +void FSDBH::Init() +{ + dbh = NULL; +} + +FSDBH *FSDBH::New(char *dsn, char *user, char *pass, const v8::FunctionCallbackInfo& info) +{ + FSDBH *new_obj = NULL; + + char *tmp = NULL; + + if (!(new_obj = new FSDBH(info))) { + goto err; + } + + new_obj->dbh = NULL; + new_obj->err = NULL; + + if (!zstr(user) || !zstr(pass)) { + tmp = switch_mprintf("%s%s%s%s%s", dsn, + zstr(user) ? "" : ":", + zstr(user) ? "" : user, + zstr(pass) ? "" : ":", + zstr(pass) ? "" : pass + ); + + dsn = tmp; + } + + if (!zstr(dsn) && switch_cache_db_get_db_handle_dsn(&(new_obj->dbh), dsn) == SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "DBH handle %p Connected.\n", (void *)(new_obj->dbh)); + } + else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Connection failed. DBH NOT Connected.\n"); + goto err; + } + + new_obj->_dsn = dsn; + return new_obj; + + err: + if (new_obj) { + delete new_obj; + } + + return NULL; +} + +void *FSDBH::Construct(const v8::FunctionCallbackInfo& info) +{ + FSDBH *dbh_obj = NULL; + char *dsn, *username = NULL, *password = NULL; + + if (info.Length() < 1 || info.Length() > 3) { + info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid parameters")); + return NULL; + } + + String::Utf8Value str1(info[0]); + dsn = *str1; + + if (info.Length() > 1) + { + String::Utf8Value str2(info[1]); + username = *str2; + } + + if (info.Length() > 2) + { + String::Utf8Value str3(info[2]); + password = *str3; + } + + if (zstr(username)) { + username = NULL; + } + + if (zstr(password)) { + password = NULL; + } + + if (dsn) { + dbh_obj = New(dsn, username, password, info); + } + + if (!dbh_obj) { + info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Failed to create new DBH instance")); + return NULL; + } + + return dbh_obj; +} + +int FSDBH::Callback(void *pArg, int argc, char **argv, char **columnNames) +{ + FSDBH *dbo = static_cast(pArg); + int x = 0; + + if (!dbo) { + return 0; + } + + HandleScope handle_scope(dbo->GetIsolate()); + + if (dbo->_callback.IsEmpty()) { + dbo->GetIsolate()->ThrowException(String::NewFromUtf8(dbo->GetIsolate(), "No callback specified")); + return 0; + } + + Handle arg = Array::New(dbo->GetIsolate(), argc); + + for (x = 0; x < argc; x++) { + if (columnNames[x] && argv[x]) { + arg->Set(String::NewFromUtf8(dbo->GetIsolate(), columnNames[x]), String::NewFromUtf8(dbo->GetIsolate(), argv[x])); + } + } + + HandleScope scope(dbo->GetIsolate()); + Handle func = Local::New(dbo->GetIsolate(), dbo->_callback); + Handle jsargv[1] = { arg }; + + func->Call(dbo->GetIsolate()->GetCurrentContext()->Global(), 1, jsargv); + + return 0; +} + +void FSDBH::clear_error() +{ + switch_safe_free(err); +} + +JS_DBH_FUNCTION_IMPL(last_error) +{ + if (err) + info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), err)); + else + info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), "")); + return; +} + +JS_DBH_FUNCTION_IMPL(query) +{ + HandleScope handle_scope(info.GetIsolate()); + + info.GetReturnValue().Set(true); + + clear_error(); + + if (info.Length() < 1 || info.Length() > 2) { + info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid parameters")); + return info.GetReturnValue().Set(false); + } + + if (!dbh) { + info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Database is not connected")); + return info.GetReturnValue().Set(false); + } + + String::Utf8Value str(info[0]); + const char *sql = js_safe_str(*str); + + if (zstr(sql)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing SQL query.\n"); + return info.GetReturnValue().Set(false); + } + + void *arg = NULL; + switch_core_db_callback_func_t cb_func = NULL; + + Handle func = Handle(); + + if (info.Length() > 1) + func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[1]); + + if (!func.IsEmpty()) { + _callback.Reset(info.GetIsolate(), func); + cb_func = FSDBH::Callback; + arg = this; + } + + if (dbh) { + if (!func.IsEmpty()) { + if (switch_cache_db_execute_sql_callback(dbh, sql, cb_func, arg, &err) == SWITCH_STATUS_SUCCESS) { + return; + } + } + else { /* if no callback func arg is passed from javascript, an empty initialized struct will be sent - see freeswitch.i */ + if (switch_cache_db_execute_sql(dbh, (char *)sql, &err) == SWITCH_STATUS_SUCCESS) { + return; + } + } + + if (err) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error %s\n", err); + switch_core_db_free(err); + info.GetReturnValue().Set(false); + } + } + else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DBH NOT Connected.\n"); + info.GetReturnValue().Set(false); + } + + /* Make sure to release the callback handle again */ + _callback.Reset(); + + return; +} + +JS_DBH_FUNCTION_IMPL(release) +{ + return info.GetReturnValue().Set(_release()); +} + +JS_DBH_FUNCTION_IMPL(test_reactive) +{ + HandleScope handle_scope(info.GetIsolate()); + + info.GetReturnValue().Set(true); + + clear_error(); + + if (info.Length() < 3) { + info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid parameters")); + return info.GetReturnValue().Set(false); + } + + if (!dbh) { + info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Database is not connected")); + return info.GetReturnValue().Set(false); + } + + String::Utf8Value str0(info[0]); + String::Utf8Value str1(info[1]); + String::Utf8Value str2(info[2]); + const char *test_sql = js_safe_str(*str0); + const char *drop_sql = js_safe_str(*str1); + const char *reactive_sql = js_safe_str(*str2); + + if (dbh) { + if (!zstr(test_sql) && !zstr(reactive_sql)) { + if (switch_cache_db_test_reactive(dbh, test_sql, drop_sql, reactive_sql) == SWITCH_TRUE) { + return; + } + } + else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing parameters.\n"); + return info.GetReturnValue().Set(false); + } + } + else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DBH NOT Connected.\n"); + return info.GetReturnValue().Set(false); + } + return; +} + +JS_DBH_FUNCTION_IMPL(connected) +{ + return info.GetReturnValue().Set(dbh ? true : false); +} + +JS_DBH_FUNCTION_IMPL(affected_rows) +{ + if (dbh) { + return info.GetReturnValue().Set(switch_cache_db_affected_rows(dbh)); + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "DBH is NOT Connected.\n"); + return info.GetReturnValue().Set(0); +} + +JS_DBH_GET_PROPERTY_IMPL(GetProperty) +{ + HandleScope handle_scope(info.GetIsolate()); + + if (!this) { + info.GetReturnValue().Set(false); + return; + } + + String::Utf8Value str(property); + + if (!strcmp(js_safe_str(*str), "dsn")) { + info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), _dsn.c_str())); + } + else { + info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Bad property")); + } +} + +static const js_function_t dbh_methods[] = { + { "affected_rows", FSDBH::affected_rows }, + { "connected", FSDBH::connected }, + { "last_error", FSDBH::last_error }, + { "query", FSDBH::query }, + { "release", FSDBH::release }, + { "test_reactive", FSDBH::test_reactive }, + {0} +}; + +static const js_property_t dbh_props[] = { + {"dsn", FSDBH::GetProperty, JSBase::DefaultSetProperty}, + {0} +}; + +static const js_class_definition_t dbh_desc = { + js_class_name, + FSDBH::Construct, + dbh_methods, + dbh_props +}; + +static switch_status_t dbh_load(const v8::FunctionCallbackInfo& info) +{ + JSBase::Register(info.GetIsolate(), &dbh_desc); + return SWITCH_STATUS_SUCCESS; +} + +static const v8_mod_interface_t dbh_module_interface = { + /*.name = */ js_class_name, + /*.js_mod_load */ dbh_load +}; + +const v8_mod_interface_t *FSDBH::GetModuleInterface() +{ + return &dbh_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: + */