freeswitch/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c

1333 lines
35 KiB
C
Raw Normal View History

/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005/2006, Anthony Minessale II <anthmct@yahoo.com>
*
* 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 <anthmct@yahoo.com>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Anthony Minessale II <anthmct@yahoo.com>
*
*
* mod_spidermonkey.c -- Javascript Module
*
*/
#define HAVE_CURL
#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 <libteletone.h>
#include <switch.h>
#ifdef HAVE_CURL
#include <curl/curl.h>
#endif
#ifdef _MSC_VER
#pragma warning(disable: 4311)
#endif
static const char modname[] = "mod_spidermonkey";
static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval);
static struct {
size_t gStackChunkSize;
jsuword gStackBase;
int gExitCode;
JSBool gQuitting;
FILE *gErrFile;
FILE *gOutFile;
int stackDummy;
JSRuntime *rt;
} globals;
static JSClass global_class = {
"Global", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
typedef enum {
TTF_DTMF = (1 << 0)
} teletone_flag_t;
struct dtmf_callback_state {
struct js_session *session_state;
char code_buffer[1024];
size_t code_buffer_len;
char ret_buffer[1024];
int ret_buffer_len;
int digit_count;
void *extra;
};
struct js_session {
switch_core_session *session;
JSContext *cx;
JSObject *obj;
unsigned int flags;
};
struct teletone_obj {
teletone_generation_session_t ts;
JSContext *cx;
JSObject *obj;
switch_core_session *session;
switch_codec codec;
switch_buffer *audio_buffer;
switch_memory_pool *pool;
switch_timer *timer;
switch_timer timer_base;
char code_buffer[1024];
char ret_val[1024];
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;
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 switch_status js_stream_dtmf_callback(switch_core_session *session, char *dtmf, void *buf, unsigned int buflen)
{
char code[2048];
struct dtmf_callback_state *cb_state = buf;
struct js_session *jss = cb_state->session_state;
switch_file_handle *fh = cb_state->extra;
jsval rval;
char *ret;
if(!jss) {
return SWITCH_STATUS_FALSE;
}
if (cb_state->digit_count || (cb_state->code_buffer[0] > 47 && cb_state->code_buffer[0] < 58)) {
char *d;
if (!cb_state->digit_count) {
cb_state->digit_count = atoi(cb_state->code_buffer);
}
for(d = dtmf; *d; d++) {
cb_state->ret_buffer[cb_state->ret_buffer_len++] = *d;
if ((cb_state->ret_buffer_len > cb_state->digit_count)||
(cb_state->ret_buffer_len > sizeof(cb_state->ret_buffer))||
(cb_state->ret_buffer_len >= cb_state->digit_count)
) {
return SWITCH_STATUS_FALSE;
}
}
return SWITCH_STATUS_SUCCESS;
} else {
snprintf(code, sizeof(code), "~%s(\"%s\")", cb_state->code_buffer, dtmf);
eval_some_js(code, jss->cx, jss->obj, &rval);
ret = JS_GetStringBytes(JS_ValueToString(jss->cx, rval));
if (!strncasecmp(ret, "speed", 4)) {
char *p;
if ((p = strchr(ret, ':'))) {
p++;
if (*p == '+' || *p == '-') {
int step;
if (!(step = atoi(p))) {
step = 1;
}
fh->speed += step;
} else {
int speed = atoi(p);
fh->speed = speed;
}
return SWITCH_STATUS_SUCCESS;
}
return SWITCH_STATUS_FALSE;
} else if (!strcasecmp(ret, "pause")) {
if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) {
switch_clear_flag(fh, SWITCH_FILE_PAUSE);
} else {
switch_set_flag(fh, SWITCH_FILE_PAUSE);
}
return SWITCH_STATUS_SUCCESS;
} else if (!strcasecmp(ret, "restart")) {
unsigned int pos = 0;
fh->speed = 0;
switch_core_file_seek(fh, &pos, 0, SEEK_SET);
return SWITCH_STATUS_SUCCESS;
} else if (!strncasecmp(ret, "seek", 4)) {
switch_codec *codec;
unsigned int samps = 0;
unsigned int pos = 0;
char *p;
codec = switch_core_session_get_read_codec(jss->session);
if ((p = strchr(ret, ':'))) {
p++;
if (*p == '+' || *p == '-') {
int step;
if (!(step = atoi(p))) {
step = 1000;
}
if (step > 0) {
samps = step * (codec->implementation->samples_per_second / 1000);
switch_core_file_seek(fh, &pos, samps, SEEK_CUR);
} else {
samps = step * (codec->implementation->samples_per_second / 1000);
switch_core_file_seek(fh, &pos, fh->pos - samps, SEEK_SET);
}
} else {
samps = atoi(p) * (codec->implementation->samples_per_second / 1000);
switch_core_file_seek(fh, &pos, samps, SEEK_SET);
}
}
return SWITCH_STATUS_SUCCESS;
}
if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) {
return SWITCH_STATUS_SUCCESS;
}
if (ret) {
switch_copy_string(cb_state->ret_buffer, ret, sizeof(cb_state->ret_buffer));
}
}
return SWITCH_STATUS_FALSE;
}
static switch_status js_record_dtmf_callback(switch_core_session *session, char *dtmf, void *buf, unsigned int buflen)
{
char code[2048];
struct dtmf_callback_state *cb_state = buf;
struct js_session *jss = cb_state->session_state;
switch_file_handle *fh = cb_state->extra;
jsval rval;
char *ret;
if(!jss) {
return SWITCH_STATUS_FALSE;
}
if (cb_state->digit_count || (cb_state->code_buffer[0] > 47 && cb_state->code_buffer[0] < 58)) {
char *d;
if (!cb_state->digit_count) {
cb_state->digit_count = atoi(cb_state->code_buffer);
}
for(d = dtmf; *d; d++) {
cb_state->ret_buffer[cb_state->ret_buffer_len++] = *d;
if ((cb_state->ret_buffer_len > cb_state->digit_count)||
(cb_state->ret_buffer_len > sizeof(cb_state->ret_buffer))||
(cb_state->ret_buffer_len >= cb_state->digit_count)
) {
return SWITCH_STATUS_FALSE;
}
}
return SWITCH_STATUS_SUCCESS;
} else {
snprintf(code, sizeof(code), "~%s(\"%s\")", cb_state->code_buffer, dtmf);
eval_some_js(code, jss->cx, jss->obj, &rval);
ret = JS_GetStringBytes(JS_ValueToString(jss->cx, rval));
if (!strcasecmp(ret, "pause")) {
if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) {
switch_clear_flag(fh, SWITCH_FILE_PAUSE);
} else {
switch_set_flag(fh, SWITCH_FILE_PAUSE);
}
return SWITCH_STATUS_SUCCESS;
} else if (!strcasecmp(ret, "restart")) {
unsigned int pos = 0;
fh->speed = 0;
switch_core_file_seek(fh, &pos, 0, SEEK_SET);
return SWITCH_STATUS_SUCCESS;
}
if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) {
return SWITCH_STATUS_SUCCESS;
}
if (ret) {
switch_copy_string(cb_state->ret_buffer, ret, sizeof(cb_state->ret_buffer));
}
}
return SWITCH_STATUS_FALSE;
}
static switch_status js_speak_dtmf_callback(switch_core_session *session, char *dtmf, void *buf, unsigned int buflen)
{
char code[2048];
struct dtmf_callback_state *cb_state = buf;
struct js_session *jss = cb_state->session_state;
jsval rval;
char *ret;
if(!jss) {
return SWITCH_STATUS_FALSE;
}
if (cb_state->digit_count || (cb_state->code_buffer[0] > 47 && cb_state->code_buffer[0] < 58)) {
char *d;
if (!cb_state->digit_count) {
cb_state->digit_count = atoi(cb_state->code_buffer);
}
for(d = dtmf; *d; d++) {
cb_state->ret_buffer[cb_state->ret_buffer_len++] = *d;
if ((cb_state->ret_buffer_len > cb_state->digit_count)||
(cb_state->ret_buffer_len > sizeof(cb_state->ret_buffer))||
(cb_state->ret_buffer_len >= cb_state->digit_count)
) {
return SWITCH_STATUS_FALSE;
}
}
return SWITCH_STATUS_SUCCESS;
} else {
snprintf(code, sizeof(code), "~%s(\"%s\")", cb_state->code_buffer, dtmf);
eval_some_js(code, jss->cx, jss->obj, &rval);
ret = JS_GetStringBytes(JS_ValueToString(jss->cx, rval));
if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) {
return SWITCH_STATUS_SUCCESS;
}
if (ret) {
switch_copy_string(cb_state->ret_buffer, ret, sizeof(cb_state->ret_buffer));
}
}
return SWITCH_STATUS_FALSE;
}
static JSBool session_recordfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel *channel;
char *file_name = NULL;
char *dtmf_callback = NULL;
void *bp = NULL;
int len = 0;
switch_dtmf_callback_function dtmf_func = NULL;
struct dtmf_callback_state cb_state = {0};
switch_file_handle fh;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
if (argc > 0) {
file_name = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
if (switch_strlen_zero(file_name)) {
return JS_FALSE;
}
}
if (argc > 1) {
dtmf_callback = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
if (switch_strlen_zero(dtmf_callback)) {
dtmf_callback = NULL;
} else {
memset(&cb_state, 0, sizeof(cb_state));
switch_copy_string(cb_state.code_buffer, dtmf_callback, sizeof(cb_state.code_buffer));
cb_state.code_buffer_len = strlen(cb_state.code_buffer);
cb_state.session_state = jss;
dtmf_func = js_record_dtmf_callback;
bp = &cb_state;
len = sizeof(cb_state);
}
}
memset(&fh, 0, sizeof(fh));
cb_state.extra = &fh;
switch_ivr_record_file(jss->session, &fh, file_name, dtmf_func, bp, len);
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, cb_state.ret_buffer));
return (switch_channel_get_state(channel) == CS_EXECUTE) ? JS_TRUE : JS_FALSE;
}
static JSBool session_streamfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel *channel;
char *file_name = NULL;
char *timer_name = NULL;
char *dtmf_callback = NULL;
void *bp = NULL;
int len = 0;
switch_dtmf_callback_function dtmf_func = NULL;
struct dtmf_callback_state cb_state = {0};
switch_file_handle fh;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
if (argc > 0) {
file_name = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
if (switch_strlen_zero(file_name)) {
return JS_FALSE;
}
}
if (argc > 1) {
timer_name = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
if (switch_strlen_zero(timer_name)) {
timer_name = NULL;
}
}
if (argc > 2) {
dtmf_callback = JS_GetStringBytes(JS_ValueToString(cx, argv[2]));
if (switch_strlen_zero(dtmf_callback)) {
dtmf_callback = NULL;
} else {
memset(&cb_state, 0, sizeof(cb_state));
switch_copy_string(cb_state.code_buffer, dtmf_callback, sizeof(cb_state.code_buffer));
cb_state.code_buffer_len = strlen(cb_state.code_buffer);
cb_state.session_state = jss;
dtmf_func = js_stream_dtmf_callback;
bp = &cb_state;
len = sizeof(cb_state);
}
}
memset(&fh, 0, sizeof(fh));
cb_state.extra = &fh;
switch_ivr_play_file(jss->session, &fh, file_name, timer_name, dtmf_func, bp, len);
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, cb_state.ret_buffer));
return (switch_channel_get_state(channel) == CS_EXECUTE) ? JS_TRUE : JS_FALSE;
}
static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel *channel;
char *tts_name = NULL;
char *voice_name = NULL;
char *text = NULL;
char *dtmf_callback = NULL;
switch_codec *codec;
void *bp = NULL;
int len = 0;
struct dtmf_callback_state cb_state = {0};
switch_dtmf_callback_function dtmf_func = NULL;
channel = switch_core_session_get_channel(jss->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 (argc > 3) {
dtmf_callback = JS_GetStringBytes(JS_ValueToString(cx, argv[3]));
if (switch_strlen_zero(dtmf_callback)) {
dtmf_callback = NULL;
} else {
memset(&cb_state, 0, sizeof(cb_state));
switch_copy_string(cb_state.code_buffer, dtmf_callback, sizeof(cb_state.code_buffer));
cb_state.code_buffer_len = strlen(cb_state.code_buffer);
cb_state.session_state = jss;
dtmf_func = js_speak_dtmf_callback;
bp = &cb_state;
len = sizeof(cb_state);
}
}
if (!tts_name && text) {
return JS_FALSE;
}
codec = switch_core_session_get_read_codec(jss->session);
switch_ivr_speak_text(jss->session,
tts_name,
voice_name && strlen(voice_name) ? voice_name : NULL,
NULL,
codec->implementation->samples_per_second,
dtmf_func,
text,
bp,
len);
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, cb_state.ret_buffer));
return (switch_channel_get_state(channel) == CS_EXECUTE) ? JS_TRUE : JS_FALSE;
}
static JSBool session_get_digits(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
char *terminators = NULL;
char *buf;
int digits;
int32 timeout = 5000;
int32 poll_chan = 1;
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]));
}
if (argc > 2) {
JS_ValueToInt32(cx, argv[2], &timeout);
}
if (argc > 3) {
JS_ValueToInt32(cx, argv[3], &poll_chan);
}
buf = switch_core_session_alloc(jss->session, digits);
switch_ivr_collect_digits_count(jss->session, buf, digits, digits, terminators, &term, timeout, poll_chan ? SWITCH_TRUE : SWITCH_FALSE);
*rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) );
return JS_TRUE;
}
return JS_FALSE;
}
static JSBool session_answer(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel *channel;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
switch_channel_answer(channel);
return JS_TRUE;
}
#ifdef HAVE_CURL
struct config_data {
JSContext *cx;
JSObject *obj;
char *name;
};
static size_t realtime_callback(void *ptr, size_t size, size_t nmemb, void *data)
{
register int realsize = size * nmemb;
char *line, lineb[2048], *nextline = NULL, *val = NULL, *p = NULL;
jsval rval;
struct config_data *config_data = data;
char code[256];
if (config_data->name) {
switch_copy_string(lineb, (char *) ptr, sizeof(lineb));
line = lineb;
while (line) {
if ((nextline = strchr(line, '\n'))) {
*nextline = '\0';
nextline++;
}
if ((val = strchr(line, '='))) {
*val = '\0';
val++;
if (val[0] == '>') {
*val = '\0';
val++;
}
for (p = line; p && *p == ' '; p++);
line = p;
for (p=line+strlen(line)-1;*p == ' '; p--)
*p = '\0';
for (p = val; p && *p == ' '; p++);
val = p;
for (p=val+strlen(val)-1;*p == ' '; p--)
*p = '\0';
snprintf(code, sizeof(code), "~%s[\"%s\"] = \"%s\"", config_data->name, line, val);
eval_some_js(code, config_data->cx, config_data->obj, &rval);
}
line = nextline;
}
}
return realsize;
}
static JSBool js_fetchurl(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char *url = NULL, *name = NULL;
CURL *curl_handle = NULL;
struct config_data config_data;
if ( argc > 0 && (url = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) {
if (argc > 1)
name = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
curl_global_init(CURL_GLOBAL_ALL);
curl_handle = curl_easy_init();
if (!strncasecmp(url, "https", 5)) {
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
}
config_data.cx = cx;
config_data.obj = obj;
if (name)
config_data.name = name;
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, realtime_callback);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&config_data);
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "asterisk-js/1.0");
curl_easy_perform(curl_handle);
curl_easy_cleanup(curl_handle);
} else {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Error!\n");
return JS_FALSE;
}
return JS_TRUE;
}
#endif
/* Session Object */
/*********************************************************************************/
enum session_tinyid {
SESSION_NAME, SESSION_STATE
};
static JSFunctionSpec session_methods[] = {
{"streamFile", session_streamfile, 1},
{"recordFile", session_recordfile, 1},
{"speak", session_speak, 1},
{"getDigits", session_get_digits, 1},
{"answer", session_answer, 0},
{0}
};
static JSPropertySpec session_props[] = {
{"name", SESSION_NAME, JSPROP_READONLY|JSPROP_PERMANENT},
{"state", SESSION_STATE, JSPROP_READONLY|JSPROP_PERMANENT},
{0}
};
static JSBool session_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
int param = 0;
JSBool res = JS_TRUE;
switch_channel *channel;
char *name;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
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 SESSION_NAME:
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, switch_channel_get_name(channel)));
break;
case SESSION_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 session_class = {
"Session", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, session_getProperty, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
};
static JSObject *new_js_session(JSContext *cx, JSObject *obj, switch_core_session *session, struct js_session *jss, char *name, int flags)
{
JSObject *session_obj;
if ((session_obj = JS_DefineObject(cx, obj, name, &session_class, NULL, 0))) {
memset(jss, 0, sizeof(struct js_session));
jss->session = session;
jss->flags = flags;
jss->cx = cx;
jss->obj = obj;
if ((JS_SetPrivate(cx, session_obj, jss) &&
JS_DefineProperties(cx, session_obj, session_props) &&
JS_DefineFunctions(cx, session_obj, session_methods))) {
return session_obj;
}
}
return NULL;
}
static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map)
{
struct teletone_obj *tto = ts->user_data;
int wrote;
if (!tto) {
return -1;
}
wrote = teletone_mux_tones(ts, map);
switch_buffer_write(tto->audio_buffer, ts->buffer, wrote * 2);
return 0;
}
/* TeleTone Object */
/*********************************************************************************/
static JSBool teletone_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
int32 memory = 65535;
JSObject *session_obj;
struct teletone_obj *tto = NULL;
struct js_session *jss = NULL;
switch_codec *read_codec;
switch_memory_pool *pool;
char *timer_name = NULL;
if (argc > 0) {
if (JS_ValueToObject(cx, argv[0], &session_obj)) {
if (!(jss = JS_GetPrivate(cx, session_obj))) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Find Session [1]\n");
return JS_FALSE;
}
} else {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Find Session [2]\n");
return JS_FALSE;
}
} else {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Missing Session Arg\n");
return JS_FALSE;
}
if (argc > 1) {
timer_name = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
}
if (argc > 2) {
if (!JS_ValueToInt32(cx, argv[2], &memory)) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Convert to INT\n");
return JS_FALSE;
}
}
switch_core_new_memory_pool(&pool);
if (!(tto = switch_core_alloc(pool, sizeof(*tto)))) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Memory Error\n");
return JS_FALSE;
}
read_codec = switch_core_session_get_read_codec(jss->session);
if (switch_core_codec_init(&tto->codec,
"L16",
read_codec->implementation->samples_per_second,
read_codec->implementation->microseconds_per_frame / 1000,
read_codec->implementation->number_of_channels,
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
NULL, pool) == SWITCH_STATUS_SUCCESS) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activated\n");
} else {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activation Failed\n");
return JS_FALSE;
}
if (timer_name) {
unsigned int ms = read_codec->implementation->microseconds_per_frame / 1000;
if (switch_core_timer_init(&tto->timer_base,
timer_name,
ms,
(read_codec->implementation->samples_per_second / 50) * read_codec->implementation->number_of_channels,
pool) == SWITCH_STATUS_SUCCESS) {
tto->timer = &tto->timer_base;
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Timer INIT Success %d\n", ms);
} else {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Timer INIT Failed\n");
}
}
switch_buffer_create(pool, &tto->audio_buffer, SWITCH_RECCOMMENDED_BUFFER_SIZE);
tto->pool = pool;
tto->obj = obj;
tto->cx = cx;
tto->session = jss->session;
teletone_init_session(&tto->ts, memory, teletone_handler, tto);
JS_SetPrivate(cx, obj, tto);
return JS_TRUE;
}
static void teletone_destroy(JSContext *cx, JSObject *obj)
{
struct teletone_obj *tto = JS_GetPrivate(cx, obj);
switch_memory_pool *pool;
if (tto) {
pool = tto->pool;
if (tto->timer) {
switch_core_timer_destroy(tto->timer);
}
teletone_destroy_session(&tto->ts);
switch_core_destroy_memory_pool(&pool);
switch_core_codec_destroy(&tto->codec);
}
}
static JSBool teletone_add_tone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct teletone_obj *tto = JS_GetPrivate(cx, obj);
if (argc > 2) {
int x;
char *fval;
char *map_str;
map_str = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
for(x = 1; x < TELETONE_MAX_TONES; x++) {
fval = JS_GetStringBytes(JS_ValueToString(cx, argv[x]));
tto->ts.TONES[(int)*map_str].freqs[x-1] = strtod(fval, NULL);
}
return JS_TRUE;
}
return JS_FALSE;
}
static JSBool teletone_on_dtmf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct teletone_obj *tto = JS_GetPrivate(cx, obj);
if (argc > 0) {
char *func = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
switch_copy_string(tto->code_buffer, func, sizeof(tto->code_buffer));
switch_set_flag(tto, TTF_DTMF);
}
return JS_TRUE;
}
static JSBool teletone_generate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct teletone_obj *tto = JS_GetPrivate(cx, obj);
if (argc > 0) {
char *script;
switch_core_session *session;
switch_frame write_frame = {0};
unsigned char *fdata[1024];
switch_frame *read_frame;
int stream_id;
switch_core_thread_session thread_session;
switch_channel *channel;
tto->ts.debug = 1;
tto->ts.debug_stream = switch_core_get_console();
script = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
teletone_run(&tto->ts, script);
session = tto->session;
write_frame.codec = &tto->codec;
write_frame.data = fdata;
channel = switch_core_session_get_channel(session);
if (tto->timer) {
for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) {
switch_core_service_session(session, &thread_session, stream_id);
}
}
for(;;) {
if (switch_test_flag(tto, TTF_DTMF)) {
char dtmf[128];
char code[512];
char *ret;
jsval tt_rval;
if (switch_channel_has_dtmf(channel)) {
switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf));
snprintf(code, sizeof(code), "~%s(\"%s\")", tto->code_buffer, dtmf);
eval_some_js(code, cx, obj, &tt_rval);
ret = JS_GetStringBytes(JS_ValueToString(cx, tt_rval));
if (strcmp(ret, "true") && strcmp(ret, "undefined")) {
switch_copy_string(tto->ret_val, ret, sizeof(tto->ret_val));
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, tto->ret_val));
return JS_TRUE;
}
}
}
if (tto->timer) {
int x;
if ((x = switch_core_timer_next(tto->timer)) < 0) {
break;
}
} else {
if (switch_core_session_read_frame(session, &read_frame, -1, 0) != SWITCH_STATUS_SUCCESS) {
break;
}
}
if ((write_frame.datalen = switch_buffer_read(tto->audio_buffer, fdata, write_frame.codec->implementation->bytes_per_frame)) <= 0) {
break;
}
write_frame.samples = write_frame.datalen / 2;
for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) {
if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Bad Write\n");
break;
}
}
}
if (tto->timer) {
switch_core_thread_session_end(&thread_session);
}
return JS_TRUE;
}
return JS_FALSE;
}
enum teletone_tinyid {
TELETONE_NAME
};
static JSFunctionSpec teletone_methods[] = {
{"generate", teletone_generate, 1},
{"onDTMF", teletone_on_dtmf, 1},
{"addTone", teletone_add_tone, 10},
{0}
};
static JSPropertySpec teletone_props[] = {
{"name", TELETONE_NAME, JSPROP_READONLY|JSPROP_PERMANENT},
{0}
};
static JSBool teletone_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSBool res = JS_TRUE;
return res;
}
JSClass teletone_class = {
"TeleTone", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, teletone_getProperty, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, teletone_destroy, NULL, NULL, NULL,
teletone_construct
};
/* Built-In*/
/*********************************************************************************/
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 JSBool js_include(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char *code;
if ( argc > 0 && (code = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) {
eval_some_js(code, cx, obj, rval);
return JS_TRUE;
}
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Invalid Arguements\n");
return JS_FALSE;
}
#define B64BUFFLEN 1024
static const char c64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static int write_buf(int fd, char *buf) {
size_t len = strlen(buf);
if (fd && write(fd, buf, len) != len) {
close(fd);
return 0;
}
return 1;
}
static JSBool js_email(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char *to = NULL, *from = NULL, *headers, *body = NULL, *file = NULL;
char *bound = "XXXX_boundary_XXXX";
char filename[80], buf[B64BUFFLEN];
int fd = 0, ifd = 0;
int x=0, y=0, bytes=0, ilen=0;
unsigned int b=0, l=0;
unsigned char in[B64BUFFLEN];
unsigned char out[B64BUFFLEN+512];
char *path = NULL;
if (
argc > 3 &&
(from = JS_GetStringBytes(JS_ValueToString(cx, argv[0]))) &&
(to = JS_GetStringBytes(JS_ValueToString(cx, argv[1]))) &&
(headers = JS_GetStringBytes(JS_ValueToString(cx, argv[2]))) &&
(body = JS_GetStringBytes(JS_ValueToString(cx, argv[3])))
) {
if ( argc > 4)
file = JS_GetStringBytes(JS_ValueToString(cx, argv[4]));
snprintf(filename, 80, "/tmp/mail.%ld", switch_time_now());
if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
if (file) {
path = file;
if ((ifd = open(path, O_RDONLY)) < 1) {
return JS_FALSE;
}
snprintf(buf, B64BUFFLEN, "MIME-Version: 1.0\nContent-Type: multipart/mixed; boundary=\"%s\"\n", bound);
if (!write_buf(fd, buf)) {
return JS_FALSE;
}
}
if (!write_buf(fd, headers))
return JS_FALSE;
if (!write_buf(fd, "\n\n"))
return JS_FALSE;
if (file) {
snprintf(buf, B64BUFFLEN, "--%s\nContent-Type: text/plain\n\n", bound);
if (!write_buf(fd, buf))
return JS_FALSE;
}
if (!write_buf(fd, body))
return JS_FALSE;
if (file) {
snprintf(buf, B64BUFFLEN, "\n\n--%s\nContent-Type: application/octet-stream\nContent-Transfer-Encoding: base64\nContent-Description: Sound attachment.\nContent-Disposition: attachment; filename=\"%s\"\n\n", bound, file);
if (!write_buf(fd, buf))
return JS_FALSE;
while((ilen=read(ifd, in, B64BUFFLEN))) {
for (x=0;x<ilen;x++) {
b = (b<<8) + in[x];
l += 8;
while (l >= 6) {
out[bytes++] = c64[(b>>(l-=6))%64];
if (++y!=72)
continue;
out[bytes++] = '\n';
y=0;
}
}
if (write(fd, &out, bytes) != bytes) {
return -1;
} else
bytes=0;
}
if (l > 0)
out[bytes++] = c64[((b%16)<<(6-l))%64];
if (l != 0) while (l < 6)
out[bytes++] = '=', l += 2;
if (write(fd, &out, bytes) != bytes) {
return -1;
}
}
if (file) {
snprintf(buf, B64BUFFLEN, "\n\n--%s--\n.\n", bound);
if (!write_buf(fd, buf))
return JS_FALSE;
}
}
if (fd)
close(fd);
if (ifd)
close(ifd);
snprintf(buf, B64BUFFLEN, "/bin/cat %s | /usr/sbin/sendmail -tf \"%s\" %s", filename, from, to);
system(buf);
unlink(filename);
if (file) {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Emailed file [%s] to [%s]\n", filename, to);
} else {
switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Emailed data to [%s]\n", to);
}
return JS_TRUE;
}
return JS_FALSE;
}
static JSFunctionSpec fs_functions[] = {
{"console_log", js_log, 2},
{"include", js_include, 1},
{"email", js_email, 2},
#ifdef HAVE_CURL
{"fetchURL", js_fetchurl, 1},
#endif
{0}
};
static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval)
{
JSScript *script;
char *cptr;
char path[512];
int res = 0;
JS_ClearPendingException(cx);
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", SWITCH_GLOBAL_dirs.script_dir, code);
script = JS_CompileFile(cx, obj, path);
}
}
if (script) {
res = JS_ExecuteScript(cx, obj, script, rval) == JS_TRUE ? 1 : 0;
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 *javascript_global_object, *session_obj;
struct js_session jss;
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 ((javascript_global_object = JS_NewObject(cx, &global_class, NULL, NULL)) &&
JS_DefineFunctions(cx, javascript_global_object, fs_functions) &&
JS_InitStandardClasses(cx, javascript_global_object) &&
(session_obj = new_js_session(cx, javascript_global_object, session, &jss, "session", flags))) {
JS_SetGlobalObject(cx, javascript_global_object);
JS_SetPrivate(cx, javascript_global_object, session);
res = 0;
JS_InitClass(cx,
javascript_global_object,
NULL,
&teletone_class,
teletone_construct,
3,
teletone_props,
teletone_methods,
teletone_props,
teletone_methods
);
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, javascript_global_object, &rval);
snprintf(buf, sizeof(buf), "~var argc = %d", y);
eval_some_js(buf, cx, javascript_global_object, &rval);
do {
if ((nextarg = strchr(arg, ':'))) {
*nextarg = '\0';
nextarg++;
}
snprintf(buf, sizeof(buf), "~Argv[%d] = \"%s\";", x++, arg);
eval_some_js(buf, cx, javascript_global_object, &rval);
arg = nextarg;
} while (arg);
}
if (!(res=eval_some_js(code, cx, javascript_global_object, &rval))) {
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;
}