diff --git a/libs/mozilla.build.sh b/libs/mozilla.build.sh new file mode 100644 index 0000000000..c1afc761da --- /dev/null +++ b/libs/mozilla.build.sh @@ -0,0 +1,3 @@ +cd nsprpub && ./configure && make +cd ../js/src && JS_THREADSAFE=1 OTHER_LIBS="-L../../../mozilla/nsprpub/dist/lib" INCLUDES="-I../../../mozilla/nsprpub/dist/include/nspr" make -f Makefile.ref Linux_All_DBG.OBJ/libjs.a + diff --git a/src/mod/languages/mod_spidermonkey/Makefile b/src/mod/languages/mod_spidermonkey/Makefile new file mode 100644 index 0000000000..da9054d2e8 --- /dev/null +++ b/src/mod/languages/mod_spidermonkey/Makefile @@ -0,0 +1,20 @@ +CFLAGS += -I$(BASE)/libs/mozilla/js/src -Wall -Wno-format -g -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DHAVE_LOCALTIME_R -DX86_LINUX -DDEBUG -DDEBUG_root -DJS_THREADSAFE -I$(BASE)/libs/mozilla/js/src -I$(BASE)/libs/mozilla/js/src/Linux_All_DBG.OBJ -Wall -Wno-format -g -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DHAVE_LOCALTIME_R -DX86_LINUX -DDEBUG -DDEBUG_root -DJS_THREADSAFE -I$(BASE)/libs/mozilla/nsprpub/dist/include/nspr -ILinux_All_DBG.OBJ +LDFLAGS +=-DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DHAVE_LOCALTIME_R -DX86_LINUX -DDEBUG -DDEBUG_root -DJS_THREADSAFE -I$(BASE)/libs/mozilla/nsprpub/dist/include/nspr -Wall -Wno-format -g -DXP_UNIX -DSVR4 -DSYSV -D_BSD_SOURCE -DPOSIX_SOURCE -DHAVE_LOCALTIME_R -DX86_LINUX -DDEBUG -DDEBUG_root -DJS_THREADSAFE -I$(BASE)/libs/mozilla/nsprpub/dist/include/nspr +OBJS=$(BASE)/libs/mozilla/js/src/Linux_All_DBG.OBJ/libjs.a $(BASE)/libs/mozilla/nsprpub/dist/lib/libnspr4.a +LINKER=$(CC) + + +all: depends $(MODNAME).so + +depends: + MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) mozilla --prefix=$(PREFIX) + +$(MODNAME).so: $(MODNAME).c + $(CC) $(CFLAGS) -fPIC -c $(MODNAME).c -o $(MODNAME).o + $(LINKER) $(SOLINK) -o $(MODNAME).so $(MODNAME).o $(OBJS) $(LDFLAGS) + +clean: + rm -fr *.so *.o *~ + +install: + cp -f $(MODNAME).so $(PREFIX)/mod diff --git a/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c new file mode 100644 index 0000000000..9821bd2dba --- /dev/null +++ b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c @@ -0,0 +1,451 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * 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 FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * + * mod_spidermonkey.c -- Javascript Module + * + */ +#include "jstypes.h" +#include "jsarena.h" +#include "jsutil.h" +#include "jsprf.h" +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jsdbgapi.h" +#include "jsemit.h" +#include "jsfun.h" +#include "jsgc.h" +#include "jslock.h" +#include "jsobj.h" +#include "jsparse.h" +#include "jsscope.h" +#include "jsscript.h" +#include + +static const char modname[] = "mod_spidermonkey"; + + +static struct { + size_t gStackChunkSize; + jsuword gStackBase; + int gExitCode; + JSBool gQuitting; + FILE *gErrFile; + FILE *gOutFile; + int stackDummy; + JSRuntime *rt; +} globals; + + +extern JSClass global_class; +static JSClass global_class = { + "Global", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub +}; + + +struct jchan { + switch_core_session *session; + unsigned int flags; +}; + + +static void js_error(JSContext *cx, const char *message, JSErrorReport *report) +{ + if (message) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "%s\n", message); + } + +} + +static switch_status init_js(void) +{ + memset(&globals, 0, sizeof(globals)); + globals.gQuitting = JS_FALSE; + globals.gErrFile = NULL; + globals.gOutFile = NULL; + globals.gStackChunkSize = 8192; + if(0) { + js_error(NULL, NULL, NULL); + if(&global_class); + + } + + globals.gStackBase = (jsuword)&globals.stackDummy; + globals.gErrFile = stderr; + globals.gOutFile = stdout; + + + if (!(globals.rt = JS_NewRuntime(64L * 1024L * 1024L))) { + return SWITCH_STATUS_FALSE; + } + + + + return SWITCH_STATUS_SUCCESS; + +} + +static JSBool chan_streamfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + struct jchan *jc = JS_GetPrivate(cx, obj); + JSString *str = NULL; + char *filename = NULL; + char buf[25] = ""; + + if (argc > 0) { + if ((str = JS_ValueToString(cx, argv[0])) && (filename = JS_GetStringBytes(str))) { + switch_ivr_play_file(jc->session, filename, NULL, NULL, buf, sizeof(buf)); + *rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) ); + } + } + return JS_FALSE; +} + +static JSBool chan_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + struct jchan *jc = JS_GetPrivate(cx, obj); + switch_channel *channel; + char *tts_name = NULL; + char *voice_name = NULL; + char *text = NULL; + switch_codec *codec; + char buf[10] = ""; + + channel = switch_core_session_get_channel(jc->session); + assert(channel != NULL); + + if (argc > 0) { + tts_name = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); + } + if (argc > 1) { + voice_name= JS_GetStringBytes(JS_ValueToString(cx, argv[1])); + } + if (argc > 2) { + text = JS_GetStringBytes(JS_ValueToString(cx, argv[2])); + } + + if (!tts_name && text) { + return JS_FALSE; + } + + codec = switch_core_session_get_read_codec(jc->session); + switch_ivr_speak_text(jc->session, + tts_name, + voice_name && strlen(voice_name) ? voice_name : NULL, + NULL, + codec->implementation->samples_per_second, + NULL, + text, + buf, + sizeof(buf)); + *rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) ); + + return JS_TRUE; + +} + +static JSBool chan_get_digits(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + struct jchan *jc = JS_GetPrivate(cx, obj); + char *terminators = NULL; + char *buf; + int digits; + + if (argc > 0) { + char term; + digits = atoi(JS_GetStringBytes(JS_ValueToString(cx, argv[0]))); + if (argc > 1) { + terminators = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); + } + buf = switch_core_session_alloc(jc->session, digits); + switch_ivr_collect_digits_count(jc->session, buf, digits, digits, terminators, &term); + *rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) ); + return JS_TRUE; + } + + return JS_FALSE; +} + +static JSBool chan_answer(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + struct jchan *jc = JS_GetPrivate(cx, obj); + switch_channel *channel; + + channel = switch_core_session_get_channel(jc->session); + assert(channel != NULL); + + switch_channel_answer(channel); + return JS_TRUE; +} + +enum its_tinyid { + CHAN_NAME, CHAN_STATE +}; + +static JSFunctionSpec chan_methods[] = { + {"StreamFile", chan_streamfile, 1}, + {"Speak", chan_speak, 1}, + {"GetDigits", chan_get_digits, 1}, + {"Answer", chan_answer, 0}, + {0} +}; + + +static JSPropertySpec chan_props[] = { + {"name", CHAN_NAME, JSPROP_READONLY|JSPROP_PERMANENT}, + {"state", CHAN_STATE, JSPROP_READONLY|JSPROP_PERMANENT}, + {0} +}; + +static JSBool chan_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) +{ + struct jchan *jc = JS_GetPrivate(cx, obj); + int param = 0; + JSBool res = JS_TRUE; + switch_channel *channel; + + channel = switch_core_session_get_channel(jc->session); + assert(channel != NULL); + + char *name = JS_GetStringBytes(JS_ValueToString(cx, id)); + /* numbers are our props anything else is a method */ + if (name[0] >= 48 && name[0] <= 57) { + param = atoi(name); + } else { + return JS_TRUE; + } + + switch(param) { + case CHAN_NAME: + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, switch_channel_get_name(channel))); + break; + case CHAN_STATE: + *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, switch_channel_state_name(switch_channel_get_state(channel)) )); + break; + default: + res = JS_FALSE; + break; + + } + + return res; +} + + +JSClass chan_class = { + "Chan", JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, chan_getProperty, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub +}; + + +static JSBool js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *msg; + + if((msg = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "JS_LOG: %s", msg); + return JS_TRUE; + } + + return JS_FALSE; +} + + +static JSFunctionSpec fs_functions[] = { + {"Log", js_log, 2}, + {0} +}; + + +static JSObject *new_jchan(JSContext *cx, JSObject *obj, switch_core_session *session, struct jchan *jc, int flags) { + JSObject *Chan; + if ((Chan = JS_DefineObject(cx, obj, "Chan", &chan_class, NULL, 0))) { + memset(jc, 0, sizeof(struct jchan)); + jc->session = session; + jc->flags = flags; + if ((JS_SetPrivate(cx, Chan, jc) && + JS_DefineProperties(cx, Chan, chan_props) && + JS_DefineFunctions(cx, Chan, chan_methods))) { + return Chan; + } + } + + return NULL; +} + +static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval) { + JSScript *script; + JS_ClearPendingException(cx); + char *cptr; + char path[512]; + int res = 0; + + if (code[0] == '~') { + cptr = code + 1; + script = JS_CompileScript(cx, obj, cptr, strlen(cptr), "inline", 1); + } else { + if (code[0] == '/') { + script = JS_CompileFile(cx, obj, code); + } else { + snprintf(path, sizeof(path), "%s/%s", "/tmp", code); + script = JS_CompileFile(cx, obj, path); + } + } + + if (script) { + res = JS_ExecuteScript(cx, obj, script, rval) == JS_TRUE ? 0 : -1; + JS_DestroyScript(cx, script); + } + + return res; +} + +static void js_exec(switch_core_session *session, char *data) +{ + char *code, *next, *arg, *nextarg; + int res=-1; + jsval rval; + JSContext *cx; + JSObject *glob, *Chan; + struct jchan jc; + int x = 0, y = 0; + char buf[512]; + int flags = 0; + switch_channel *channel; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if (!data) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "js requires an argument (filename|code)\n"); + return; + } + + code = switch_core_session_strdup(session,(char *)data); + if (code[0] == '-') { + if ((next = strchr(code, '|'))) { + *next = '\0'; + next++; + } + code++; + code = next; + } + + if (!code) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "js requires an argument (filename|code)\n"); + return; + } + + if ((cx = JS_NewContext(globals.rt, globals.gStackChunkSize))) { + JS_SetErrorReporter(cx, js_error); + if ((glob = JS_NewObject(cx, &global_class, NULL, NULL)) && + JS_DefineFunctions(cx, glob, fs_functions) && + JS_InitStandardClasses(cx, glob) && + (Chan = new_jchan(cx, glob, session, &jc, flags))) { + JS_SetGlobalObject(cx, glob); + JS_SetPrivate(cx, glob, session); + res = 0; + + do { + if ((next = strchr(code, '|'))) { + *next = '\0'; + next++; + } + if ((arg = strchr(code, ':'))) { + for (y=0;(arg=strchr(arg, ':'));y++) + arg++; + arg = strchr(code, ':'); + *arg = '\0'; + arg++; + snprintf(buf, sizeof(buf), "~var Argv = new Array(%d);", y); + eval_some_js(buf, cx, glob, &rval); + snprintf(buf, sizeof(buf), "~var argc = %d", y); + eval_some_js(buf, cx, glob, &rval); + do { + if ((nextarg = strchr(arg, ':'))) { + *nextarg = '\0'; + nextarg++; + } + snprintf(buf, sizeof(buf), "~Argv[%d] = \"%s\";", x++, arg); + eval_some_js(buf, cx, glob, &rval); + arg = nextarg; + } while (arg); + } + if ((res=eval_some_js(code, cx, glob, &rval)) < 0) { + break; + } + code = next; + } while (code); + } + } + + if (cx) { + JS_DestroyContext(cx); + } +} + +static const switch_application_interface ivrtest_application_interface = { + /*.interface_name */ "javascript", + /*.application_function */ js_exec, + NULL, NULL, NULL, + /*.next*/ NULL +}; + + +static switch_loadable_module_interface spidermonkey_module_interface = { + /*.module_name */ modname, + /*.endpoint_interface */ NULL, + /*.timer_interface */ NULL, + /*.dialplan_interface */ NULL, + /*.codec_interface */ NULL, + /*.application_interface */ &ivrtest_application_interface, + /*.api_interface */ NULL, + /*.file_interface */ NULL, + /*.speech_interface */ NULL, + /*.directory_interface */ NULL +}; + +switch_status switch_module_load(const switch_loadable_module_interface **interface, char *filename) +{ + switch_status status; + + if((status = init_js()) != SWITCH_STATUS_SUCCESS) { + return status; + } + + /* connect my internal structure to the blank pointer passed to me */ + *interface = &spidermonkey_module_interface; + + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Hello World!\n"); + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +}