2006-02-26 23:10:22 +00:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#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"
|
2006-02-28 21:21:48 +00:00
|
|
|
#include <libteletone.h>
|
2006-02-26 23:10:22 +00:00
|
|
|
#include <switch.h>
|
|
|
|
|
2006-02-27 04:01:38 +00:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#pragma warning(disable: 4311)
|
|
|
|
#endif
|
|
|
|
|
2006-02-26 23:10:22 +00:00
|
|
|
static const char modname[] = "mod_spidermonkey";
|
|
|
|
|
2006-02-28 02:08:42 +00:00
|
|
|
static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval);
|
2006-02-26 23:10:22 +00:00
|
|
|
|
|
|
|
static struct {
|
|
|
|
size_t gStackChunkSize;
|
|
|
|
jsuword gStackBase;
|
|
|
|
int gExitCode;
|
|
|
|
JSBool gQuitting;
|
|
|
|
FILE *gErrFile;
|
|
|
|
FILE *gOutFile;
|
|
|
|
int stackDummy;
|
|
|
|
JSRuntime *rt;
|
|
|
|
} globals;
|
|
|
|
|
|
|
|
|
2006-02-27 03:11:13 +00:00
|
|
|
//extern JSClass global_class;
|
2006-02-26 23:10:22 +00:00
|
|
|
static JSClass global_class = {
|
|
|
|
"Global", JSCLASS_HAS_PRIVATE,
|
|
|
|
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-02-28 02:08:42 +00:00
|
|
|
struct dtmf_callback_state {
|
|
|
|
struct js_session *session_state;
|
|
|
|
char code_buffer[1024];
|
2006-02-28 05:55:22 +00:00
|
|
|
size_t code_buffer_len;
|
2006-02-28 02:08:42 +00:00
|
|
|
char ret_buffer[1024];
|
|
|
|
int ret_buffer_len;
|
|
|
|
int digit_count;
|
|
|
|
void *extra;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct js_session {
|
2006-02-26 23:10:22 +00:00
|
|
|
switch_core_session *session;
|
2006-02-28 02:08:42 +00:00
|
|
|
JSContext *cx;
|
|
|
|
JSObject *obj;
|
2006-02-26 23:10:22 +00:00
|
|
|
unsigned int flags;
|
|
|
|
};
|
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
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;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-02-26 23:10:22 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2006-02-28 02:08:42 +00:00
|
|
|
static switch_status js_dtmf_callback(switch_core_session *session, char *dtmf, void *buf, unsigned int buflen)
|
2006-02-26 23:10:22 +00:00
|
|
|
{
|
2006-02-28 02:08:42 +00:00
|
|
|
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));
|
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
if (!strncasecmp(ret, "speed", 4)) {
|
|
|
|
char *p;
|
2006-02-28 02:08:42 +00:00
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
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")) {
|
2006-02-28 02:08:42 +00:00
|
|
|
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;
|
2006-02-28 21:21:48 +00:00
|
|
|
} else if (!strcasecmp(ret, "restart")) {
|
2006-02-28 02:08:42 +00:00
|
|
|
unsigned int pos = 0;
|
|
|
|
fh->speed = 0;
|
|
|
|
switch_core_file_seek(fh, &pos, 0, SEEK_SET);
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
2006-02-28 21:21:48 +00:00
|
|
|
} else if (!strncasecmp(ret, "seek", 4)) {
|
2006-02-28 02:08:42 +00:00
|
|
|
switch_codec *codec;
|
|
|
|
unsigned int samps = 0;
|
|
|
|
unsigned int pos = 0;
|
2006-02-28 21:21:48 +00:00
|
|
|
char *p;
|
2006-02-28 05:55:22 +00:00
|
|
|
codec = switch_core_session_get_read_codec(jss->session);
|
2006-02-28 21:21:48 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2006-02-28 02:08:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2006-02-26 23:10:22 +00:00
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
static JSBool session_streamfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
2006-02-28 02:08:42 +00:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
2006-02-26 23:10:22 +00:00
|
|
|
if (argc > 0) {
|
2006-02-28 02:08:42 +00:00
|
|
|
file_name = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
|
|
|
|
if (switch_strlen_zero(file_name)) {
|
|
|
|
return JS_FALSE;
|
2006-02-26 23:10:22 +00:00
|
|
|
}
|
|
|
|
}
|
2006-02-28 02:08:42 +00:00
|
|
|
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_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;
|
2006-02-26 23:10:22 +00:00
|
|
|
}
|
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
2006-02-26 23:10:22 +00:00
|
|
|
{
|
2006-02-28 02:08:42 +00:00
|
|
|
struct js_session *jss = JS_GetPrivate(cx, obj);
|
2006-02-26 23:10:22 +00:00
|
|
|
switch_channel *channel;
|
|
|
|
char *tts_name = NULL;
|
|
|
|
char *voice_name = NULL;
|
|
|
|
char *text = NULL;
|
|
|
|
switch_codec *codec;
|
|
|
|
char buf[10] = "";
|
2006-02-28 02:08:42 +00:00
|
|
|
void *bp = NULL;
|
|
|
|
int len = 0;
|
|
|
|
//switch_dtmf_callback_function dtmf_func = NULL;
|
2006-02-26 23:10:22 +00:00
|
|
|
|
2006-02-28 02:08:42 +00:00
|
|
|
channel = switch_core_session_get_channel(jss->session);
|
2006-02-26 23:10:22 +00:00
|
|
|
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]));
|
|
|
|
}
|
2006-02-28 02:08:42 +00:00
|
|
|
if (argc > 3) {
|
|
|
|
bp = buf;
|
|
|
|
len = sizeof(buf);
|
|
|
|
}
|
2006-02-26 23:10:22 +00:00
|
|
|
|
|
|
|
if (!tts_name && text) {
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
2006-02-28 02:08:42 +00:00
|
|
|
codec = switch_core_session_get_read_codec(jss->session);
|
|
|
|
switch_ivr_speak_text(jss->session,
|
2006-02-26 23:10:22 +00:00
|
|
|
tts_name,
|
|
|
|
voice_name && strlen(voice_name) ? voice_name : NULL,
|
|
|
|
NULL,
|
|
|
|
codec->implementation->samples_per_second,
|
|
|
|
NULL,
|
|
|
|
text,
|
2006-02-28 02:08:42 +00:00
|
|
|
bp,
|
|
|
|
len);
|
|
|
|
if(len) {
|
|
|
|
*rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) );
|
|
|
|
}
|
2006-02-26 23:10:22 +00:00
|
|
|
return JS_TRUE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
static JSBool session_get_digits(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
2006-02-26 23:10:22 +00:00
|
|
|
{
|
2006-02-28 02:08:42 +00:00
|
|
|
struct js_session *jss = JS_GetPrivate(cx, obj);
|
2006-02-26 23:10:22 +00:00
|
|
|
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]));
|
|
|
|
}
|
2006-02-28 02:08:42 +00:00
|
|
|
buf = switch_core_session_alloc(jss->session, digits);
|
|
|
|
switch_ivr_collect_digits_count(jss->session, buf, digits, digits, terminators, &term);
|
2006-02-26 23:10:22 +00:00
|
|
|
*rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) );
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return JS_FALSE;
|
|
|
|
}
|
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
static JSBool session_answer(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
2006-02-26 23:10:22 +00:00
|
|
|
{
|
2006-02-28 02:08:42 +00:00
|
|
|
struct js_session *jss = JS_GetPrivate(cx, obj);
|
2006-02-26 23:10:22 +00:00
|
|
|
switch_channel *channel;
|
|
|
|
|
2006-02-28 02:08:42 +00:00
|
|
|
channel = switch_core_session_get_channel(jss->session);
|
2006-02-26 23:10:22 +00:00
|
|
|
assert(channel != NULL);
|
|
|
|
|
|
|
|
switch_channel_answer(channel);
|
|
|
|
return JS_TRUE;
|
|
|
|
}
|
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
/* Session Object */
|
|
|
|
/*********************************************************************************/
|
|
|
|
enum session_tinyid {
|
|
|
|
SESSION_NAME, SESSION_STATE
|
2006-02-26 23:10:22 +00:00
|
|
|
};
|
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
static JSFunctionSpec session_methods[] = {
|
|
|
|
{"streamFile", session_streamfile, 1},
|
|
|
|
{"speak", session_speak, 1},
|
|
|
|
{"getDigits", session_get_digits, 1},
|
|
|
|
{"answer", session_answer, 0},
|
2006-02-26 23:10:22 +00:00
|
|
|
{0}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
static JSPropertySpec session_props[] = {
|
|
|
|
{"name", SESSION_NAME, JSPROP_READONLY|JSPROP_PERMANENT},
|
|
|
|
{"state", SESSION_STATE, JSPROP_READONLY|JSPROP_PERMANENT},
|
2006-02-26 23:10:22 +00:00
|
|
|
{0}
|
|
|
|
};
|
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
static JSBool session_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
2006-02-26 23:10:22 +00:00
|
|
|
{
|
2006-02-28 02:08:42 +00:00
|
|
|
struct js_session *jss = JS_GetPrivate(cx, obj);
|
2006-02-26 23:10:22 +00:00
|
|
|
int param = 0;
|
|
|
|
JSBool res = JS_TRUE;
|
|
|
|
switch_channel *channel;
|
2006-02-27 04:01:38 +00:00
|
|
|
char *name;
|
2006-02-26 23:10:22 +00:00
|
|
|
|
2006-02-28 02:08:42 +00:00
|
|
|
channel = switch_core_session_get_channel(jss->session);
|
2006-02-26 23:10:22 +00:00
|
|
|
assert(channel != NULL);
|
|
|
|
|
2006-02-27 04:01:38 +00:00
|
|
|
name = JS_GetStringBytes(JS_ValueToString(cx, id));
|
2006-02-26 23:10:22 +00:00
|
|
|
/* 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) {
|
2006-02-28 21:21:48 +00:00
|
|
|
case SESSION_NAME:
|
2006-02-26 23:10:22 +00:00
|
|
|
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, switch_channel_get_name(channel)));
|
|
|
|
break;
|
2006-02-28 21:21:48 +00:00
|
|
|
case SESSION_STATE:
|
2006-02-26 23:10:22 +00:00
|
|
|
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, switch_channel_state_name(switch_channel_get_state(channel)) ));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
res = JS_FALSE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2006-02-28 02:08:42 +00:00
|
|
|
JSClass session_class = {
|
|
|
|
"Session", JSCLASS_HAS_PRIVATE,
|
2006-02-28 21:21:48 +00:00
|
|
|
JS_PropertyStub, JS_PropertyStub, session_getProperty, JS_PropertyStub,
|
2006-02-26 23:10:22 +00:00
|
|
|
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
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_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;
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
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 (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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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},
|
|
|
|
{"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*/
|
|
|
|
/*********************************************************************************/
|
2006-02-26 23:10:22 +00:00
|
|
|
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[] = {
|
2006-02-28 02:08:42 +00:00
|
|
|
{"console_log", js_log, 2},
|
2006-02-26 23:10:22 +00:00
|
|
|
{0}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-02-28 02:08:42 +00:00
|
|
|
static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval)
|
|
|
|
{
|
2006-02-26 23:10:22 +00:00
|
|
|
JSScript *script;
|
|
|
|
char *cptr;
|
|
|
|
char path[512];
|
|
|
|
int res = 0;
|
|
|
|
|
2006-02-27 04:01:38 +00:00
|
|
|
JS_ClearPendingException(cx);
|
|
|
|
|
2006-02-26 23:10:22 +00:00
|
|
|
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 {
|
2006-02-28 21:21:48 +00:00
|
|
|
snprintf(path, sizeof(path), "%s/%s", SWITCH_GLOBAL_dirs.script_dir, code);
|
2006-02-26 23:10:22 +00:00
|
|
|
script = JS_CompileFile(cx, obj, path);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (script) {
|
2006-02-28 02:08:42 +00:00
|
|
|
res = JS_ExecuteScript(cx, obj, script, rval) == JS_TRUE ? 1 : 0;
|
2006-02-26 23:10:22 +00:00
|
|
|
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;
|
2006-02-28 02:08:42 +00:00
|
|
|
JSObject *javascript_global_object, *session_obj;
|
|
|
|
struct js_session jss;
|
2006-02-26 23:10:22 +00:00
|
|
|
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);
|
2006-02-28 02:08:42 +00:00
|
|
|
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);
|
2006-02-26 23:10:22 +00:00
|
|
|
res = 0;
|
|
|
|
|
2006-02-28 21:21:48 +00:00
|
|
|
|
|
|
|
JS_InitClass(cx,
|
|
|
|
javascript_global_object,
|
|
|
|
NULL,
|
|
|
|
&teletone_class,
|
|
|
|
teletone_construct,
|
|
|
|
3,
|
|
|
|
teletone_props,
|
|
|
|
teletone_methods,
|
|
|
|
teletone_props,
|
|
|
|
teletone_methods
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-02-26 23:10:22 +00:00
|
|
|
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);
|
2006-02-28 02:08:42 +00:00
|
|
|
eval_some_js(buf, cx, javascript_global_object, &rval);
|
2006-02-26 23:10:22 +00:00
|
|
|
snprintf(buf, sizeof(buf), "~var argc = %d", y);
|
2006-02-28 02:08:42 +00:00
|
|
|
eval_some_js(buf, cx, javascript_global_object, &rval);
|
2006-02-26 23:10:22 +00:00
|
|
|
do {
|
|
|
|
if ((nextarg = strchr(arg, ':'))) {
|
|
|
|
*nextarg = '\0';
|
|
|
|
nextarg++;
|
|
|
|
}
|
|
|
|
snprintf(buf, sizeof(buf), "~Argv[%d] = \"%s\";", x++, arg);
|
2006-02-28 02:08:42 +00:00
|
|
|
eval_some_js(buf, cx, javascript_global_object, &rval);
|
2006-02-26 23:10:22 +00:00
|
|
|
arg = nextarg;
|
|
|
|
} while (arg);
|
|
|
|
}
|
2006-02-28 02:08:42 +00:00
|
|
|
if (!(res=eval_some_js(code, cx, javascript_global_object, &rval))) {
|
2006-02-26 23:10:22 +00:00
|
|
|
break;
|
2006-02-28 02:08:42 +00:00
|
|
|
}
|
2006-02-26 23:10:22 +00:00
|
|
|
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;
|
|
|
|
}
|