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

2489 lines
63 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
*
*/
#ifndef HAVE_CURL
#define HAVE_CURL
#endif
#include "mod_spidermonkey.h"
#ifdef HAVE_CURL
#include <curl/curl.h>
#endif
static const char modname[] = "mod_spidermonkey";
static void session_destroy(JSContext *cx, JSObject *obj);
static JSBool session_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
static switch_api_interface_t js_run_interface;
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
};
static struct {
switch_hash_t *mod_hash;
switch_hash_t *load_hash;
switch_memory_pool_t *pool;
} module_manager;
struct sm_loadable_module {
char *filename;
void *lib;
const sm_module_interface_t *module_interface;
spidermonkey_init_t spidermonkey_init;
};
typedef struct sm_loadable_module sm_loadable_module_t;
typedef enum {
S_HUP = (1 << 0)
} session_flag_t;
struct input_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;
JSFunction *function;
jsval arg;
jsval ret;
JSContext *cx;
JSObject *obj;
void *extra;
};
struct fileio_obj {
char *path;
unsigned int flags;
switch_file_t *fd;
switch_memory_pool_t *pool;
char *buf;
switch_size_t buflen;
int32 bufsize;
};
struct event_obj {
switch_event_t *event;
int freed;
};
/* Event Object */
/*********************************************************************************/
static JSBool event_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
if (argc > 0) {
switch_event_t *event;
struct event_obj *eo;
switch_event_types_t etype;
char *ename = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
if ((eo = malloc(sizeof(*eo)))) {
if (switch_name_event(ename, &etype) != SWITCH_STATUS_SUCCESS) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
if (etype == SWITCH_EVENT_CUSTOM) {
char *subclass_name;
if (argc < 1) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
subclass_name = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
if (switch_event_create_subclass(&event, etype, subclass_name) != SWITCH_STATUS_SUCCESS) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
} else {
if (!switch_event_create(&event, etype) != SWITCH_STATUS_SUCCESS) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
}
eo->event = event;
eo->freed = 0;
JS_SetPrivate(cx, obj, eo);
return JS_TRUE;
}
}
return JS_FALSE;
}
static void event_destroy(JSContext *cx, JSObject *obj)
{
struct event_obj *eo = JS_GetPrivate(cx, obj);
if (eo) {
if (!eo->freed && eo->event) {
switch_event_destroy(&eo->event);
}
switch_safe_free(eo);
}
}
static JSBool event_add_header(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct event_obj *eo = JS_GetPrivate(cx, obj);
if (!eo || eo->freed) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
if (argc > 1) {
char *hname = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
char *hval = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
switch_event_add_header(eo->event, SWITCH_STACK_BOTTOM, hname, hval);
*rval = BOOLEAN_TO_JSVAL( JS_TRUE );
return JS_TRUE;
}
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
static JSBool event_get_header(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct event_obj *eo = JS_GetPrivate(cx, obj);
if (!eo) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
if (argc > 0) {
char *hname = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
char *val = switch_event_get_header(eo->event, hname);
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, val));
return JS_TRUE;
}
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
static JSBool event_add_body(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct event_obj *eo = JS_GetPrivate(cx, obj);
if (!eo || eo->freed) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
if (argc > 0) {
char *body = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
switch_event_add_body(eo->event, body);
*rval = BOOLEAN_TO_JSVAL( JS_TRUE );
return JS_TRUE;
}
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
static JSBool event_get_body(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct event_obj *eo = JS_GetPrivate(cx, obj);
if (!eo) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, switch_event_get_body(eo->event)));
return JS_TRUE;
}
static JSBool event_get_type(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct event_obj *eo = JS_GetPrivate(cx, obj);
if (!eo) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, switch_event_name(eo->event->event_id)));
return JS_TRUE;
}
static JSBool event_serialize(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct event_obj *eo = JS_GetPrivate(cx, obj);
char *buf;
uint8_t isxml = 0;
if (!eo) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
if (argc > 0) {
char *arg = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
if (!strcasecmp(arg, "xml")) {
isxml++;
}
}
if (isxml) {
switch_xml_t xml;
char *xmlstr;
if ((xml = switch_event_xmlize(eo->event, NULL))) {
xmlstr = switch_xml_toxml(xml);
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, xmlstr));
switch_xml_free(xml);
free(xmlstr);
} else {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
}
} else {
if (switch_event_serialize(eo->event, &buf) == SWITCH_STATUS_SUCCESS) {
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, buf));
switch_safe_free(buf);
}
}
return JS_TRUE;
}
static JSBool event_fire(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct event_obj *eo = JS_GetPrivate(cx, obj);
if (eo) {
switch_event_fire(&eo->event);
JS_SetPrivate(cx, obj, NULL);
switch_safe_free(eo);
*rval = BOOLEAN_TO_JSVAL( JS_TRUE );
return JS_TRUE;
}
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
static JSBool event_destroy_(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct event_obj *eo = JS_GetPrivate(cx, obj);
if (eo) {
if (!eo->freed) {
switch_event_destroy(&eo->event);
}
JS_SetPrivate(cx, obj, NULL);
switch_safe_free(eo);
*rval = BOOLEAN_TO_JSVAL( JS_TRUE );
return JS_TRUE;
}
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
enum event_tinyid {
EVENT_READY
};
static JSFunctionSpec event_methods[] = {
{"addHeader", event_add_header, 1},
{"getHeader", event_get_header, 1},
{"addBody", event_add_body, 1},
{"getBody", event_get_body, 1},
{"getType", event_get_type, 1},
{"serialize", event_serialize, 0},
{"fire", event_fire, 0},
{"destroy", event_destroy_, 0},
{0}
};
static JSPropertySpec event_props[] = {
{"ready", EVENT_READY, JSPROP_READONLY|JSPROP_PERMANENT},
{0}
};
static JSBool event_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSBool res = JS_TRUE;
switch_event_t *event = JS_GetPrivate(cx, obj);
char *name;
int param = 0;
if (!event) {
*vp = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
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 EVENT_READY:
*vp = BOOLEAN_TO_JSVAL( JS_TRUE );
break;
}
return res;
}
JSClass event_class = {
"Event", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, event_getProperty, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, event_destroy, NULL, NULL, NULL,
event_construct
};
static void js_error(JSContext *cx, const char *message, JSErrorReport *report)
{
if (message) {
switch_log_printf(SWITCH_CHANNEL_ID_LOG, report->filename ? (char *)report->filename : "mod_spidermonkey.c", modname, report->lineno, SWITCH_LOG_ERROR,
"%s %s%s\n", message, report->linebuf ? "near " : "", report->linebuf ? report->linebuf : "");
}
}
static switch_status_t sm_load_file(char *filename, sm_loadable_module_t **new_module)
{
sm_loadable_module_t *module = NULL;
apr_dso_handle_t *dso = NULL;
apr_status_t status = SWITCH_STATUS_SUCCESS;
apr_dso_handle_sym_t function_handle = NULL;
spidermonkey_init_t spidermonkey_init = NULL;
const sm_module_interface_t *module_interface = NULL, *mp;
int loading = 1;
const char *err = NULL;
char derr[512] = "";
assert(filename != NULL);
*new_module = NULL;
status = apr_dso_load(&dso, filename, module_manager.pool);
while (loading) {
if (status != APR_SUCCESS) {
apr_dso_error(dso, derr, sizeof(derr));
err = derr;
break;
}
status = apr_dso_sym(&function_handle, dso, "spidermonkey_init");
spidermonkey_init = (spidermonkey_init_t) function_handle;
if (spidermonkey_init == NULL) {
err = "Cannot Load";
break;
}
if (spidermonkey_init(&module_interface) != SWITCH_STATUS_SUCCESS) {
err = "Module load routine returned an error";
break;
}
if ((module = switch_core_permanent_alloc(sizeof(sm_loadable_module_t))) == 0) {
err = "Could not allocate memory\n";
break;
}
loading = 0;
}
if (err) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Error Loading module %s\n**%s**\n", filename, err);
return SWITCH_STATUS_GENERR;
}
module->filename = switch_core_permanent_strdup(filename);
module->spidermonkey_init = spidermonkey_init;
module->module_interface = module_interface;
module->lib = dso;
*new_module = module;
switch_core_hash_insert(module_manager.mod_hash, (char *) module->filename, (void *) module);
for (mp = module_interface; mp; mp = mp->next) {
switch_core_hash_insert(module_manager.load_hash, (char *)mp->name, (void *) mp);
}
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Successfully Loaded [%s]\n", module->filename);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t sm_load_module(char *dir, char *fname)
{
switch_size_t len = 0;
char *path;
char *file;
sm_loadable_module_t *new_module = NULL;
#ifdef WIN32
const char *ext = ".dll";
#elif defined (MACOSX) || defined (DARWIN)
const char *ext = ".dylib";
#else
const char *ext = ".so";
#endif
if ((file = switch_core_strdup(module_manager.pool, fname)) == 0) {
return SWITCH_STATUS_FALSE;
}
if (*file == '/') {
path = switch_core_strdup(module_manager.pool, file);
} else {
if (strchr(file, '.')) {
len = strlen(dir);
len += strlen(file);
len += 4;
path = (char *) switch_core_alloc(module_manager.pool, len);
snprintf(path, len, "%s%s%s", dir, SWITCH_PATH_SEPARATOR, file);
} else {
len = strlen(dir);
len += strlen(file);
len += 8;
path = (char *) switch_core_alloc(module_manager.pool, len);
snprintf(path, len, "%s%s%s%s", dir, SWITCH_PATH_SEPARATOR, file, ext);
}
}
return sm_load_file(path, &new_module);
}
static switch_status_t load_modules(void)
{
char *cf = "spidermonkey.conf";
switch_xml_t cfg, xml;
unsigned int count = 0;
#ifdef WIN32
const char *ext = ".dll";
const char *EXT = ".DLL";
#elif defined (MACOSX) || defined (DARWIN)
const char *ext = ".dylib";
const char *EXT = ".DYLIB";
#else
const char *ext = ".so";
const char *EXT = ".SO";
#endif
memset(&module_manager, 0, sizeof(module_manager));
switch_core_new_memory_pool(&module_manager.pool);
switch_core_hash_init(&module_manager.mod_hash, module_manager.pool);
switch_core_hash_init(&module_manager.load_hash, module_manager.pool);
if ((xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
switch_xml_t mods, ld;
if ((mods = switch_xml_child(cfg, "modules"))) {
for (ld = switch_xml_child(mods, "load"); ld; ld = ld->next) {
const char *val = switch_xml_attr_soft(ld, "module");
if (strchr(val, '.') && !strstr(val, ext) && !strstr(val, EXT)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Invalid extension for %s\n", val);
continue;
}
sm_load_module((char *) SWITCH_GLOBAL_dirs.mod_dir, (char *) val);
count++;
}
}
switch_xml_free(xml);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "open of %s failed\n", cf);
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t 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;
}
if (load_modules() != SWITCH_STATUS_SUCCESS) {
return SWITCH_STATUS_FALSE;
}
return SWITCH_STATUS_SUCCESS;
}
JSObject *new_js_event(switch_event_t *event, char *name, JSContext *cx, JSObject *obj)
{
struct event_obj *eo;
JSObject *Event = NULL;
if ((eo = malloc(sizeof(*eo)))) {
eo->event = event;
eo->freed = 1;
if ((Event = JS_DefineObject(cx, obj, name, &event_class, NULL, 0))) {
if ((JS_SetPrivate(cx, Event, eo) &&
JS_DefineProperties(cx, Event, event_props) &&
JS_DefineFunctions(cx, Event, event_methods))) {
}
}
}
return Event;
}
static switch_status_t js_common_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
{
char *dtmf = NULL;
switch_event_t *event = NULL;
struct input_callback_state *cb_state = buf;
struct js_session *jss = cb_state->session_state;
uintN argc = 0;
jsval argv[4];
JSObject *Event = NULL;
if (!jss) {
return SWITCH_STATUS_FALSE;
}
switch (itype) {
case SWITCH_INPUT_TYPE_EVENT:
if ((event = (switch_event_t *) input)) {
if ((Event = new_js_event(event, "_XX_EVENT_XX_", cb_state->cx, cb_state->obj))) {
argv[argc++] = STRING_TO_JSVAL (JS_NewStringCopyZ(cb_state->cx, "event"));
argv[argc++] = OBJECT_TO_JSVAL(Event);
}
}
if (!Event) {
return SWITCH_STATUS_FALSE;
}
break;
case SWITCH_INPUT_TYPE_DTMF:
dtmf = (char *) input;
argv[argc++] = STRING_TO_JSVAL (JS_NewStringCopyZ(cb_state->cx, "dtmf"));
argv[argc++] = STRING_TO_JSVAL (JS_NewStringCopyZ(cb_state->cx, dtmf));
break;
}
if (cb_state->arg) {
argv[argc++] = cb_state->arg;
}
JS_CallFunction(cb_state->cx, cb_state->obj, cb_state->function, argc, argv, &cb_state->ret);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t js_stream_input_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
{
char *ret;
switch_status_t status;
struct input_callback_state *cb_state = buf;
switch_file_handle_t *fh = cb_state->extra;
struct js_session *jss = cb_state->session_state;
if ((status = js_common_callback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) {
return status;
}
if ((ret = JS_GetStringBytes(JS_ValueToString(cb_state->cx, cb_state->ret)))) {
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_t *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;
}
return SWITCH_STATUS_BREAK;
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t js_record_input_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
{
char *ret;
switch_status_t status;
struct input_callback_state *cb_state = buf;
switch_file_handle_t *fh = cb_state->extra;
if ((status = js_common_callback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) {
return status;
}
if ((ret = JS_GetStringBytes(JS_ValueToString(cb_state->cx, cb_state->ret)))) {
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;
}
return SWITCH_STATUS_BREAK;
}
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t js_collect_input_callback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
{
char *ret;
switch_status_t status;
struct input_callback_state *cb_state = buf;
if ((status = js_common_callback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) {
return status;
}
if ((ret = JS_GetStringBytes(JS_ValueToString(cb_state->cx, cb_state->ret)))) {
if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) {
return SWITCH_STATUS_SUCCESS;
}
}
return SWITCH_STATUS_BREAK;
}
static JSBool session_flush_digits(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel_t *channel;
char buf[256];
switch_size_t has;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
if ((has = switch_channel_has_dtmf(channel))) {
switch_channel_dequeue_dtmf(channel, buf, has);
}
*rval = BOOLEAN_TO_JSVAL( JS_TRUE );
return JS_TRUE;
}
static JSBool session_flush_events(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_event_t *event;
while (switch_core_session_dequeue_event(jss->session, &event) == SWITCH_STATUS_SUCCESS) {
switch_event_destroy(&event);
}
*rval = BOOLEAN_TO_JSVAL( JS_TRUE );
return JS_TRUE;
}
static JSBool session_recordfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel_t *channel;
char *file_name = NULL;
char *input_callback = NULL;
void *bp = NULL;
int len = 0;
switch_input_callback_function_t dtmf_func = NULL;
struct input_callback_state cb_state = {0};
switch_file_handle_t fh;
JSFunction *function;
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) {
if ((function = JS_ValueToFunction(cx, argv[1]))) {
memset(&cb_state, 0, sizeof(cb_state));
switch_copy_string(cb_state.code_buffer, input_callback, sizeof(cb_state.code_buffer));
cb_state.session_state = jss;
cb_state.function = function;
cb_state.cx = cx;
cb_state.obj = obj;
if (argc > 2) {
cb_state.arg = argv[2];
}
dtmf_func = js_record_input_callback;
bp = &cb_state;
len = sizeof(cb_state);
}
}
memset(&fh, 0, sizeof(fh));
cb_state.extra = &fh;
cb_state.ret = BOOLEAN_TO_JSVAL( JS_FALSE );
switch_ivr_record_file(jss->session, &fh, file_name, dtmf_func, bp, len);
*rval = cb_state.ret;
return (switch_channel_ready(channel)) ? JS_TRUE : JS_FALSE;
}
static JSBool session_collect_input(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel_t *channel;
void *bp = NULL;
int len = 0;
int32 to = 0;
switch_input_callback_function_t dtmf_func = NULL;
struct input_callback_state cb_state = {0};
JSFunction *function;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
if (argc > 0) {
if ((function = JS_ValueToFunction(cx, argv[0]))) {
memset(&cb_state, 0, sizeof(cb_state));
cb_state.function = function;
if (argc > 1) {
cb_state.arg = argv[1];
}
cb_state.session_state = jss;
cb_state.cx = cx;
cb_state.obj = obj;
dtmf_func = js_collect_input_callback;
bp = &cb_state;
len = sizeof(cb_state);
}
}
if (argc > 2) {
JS_ValueToInt32(jss->cx, argv[2], &to);
}
switch_ivr_collect_digits_callback(jss->session, dtmf_func, bp, len, to);
*rval = cb_state.ret;
return (switch_channel_ready(channel)) ? 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_t *channel;
char *file_name = NULL;
char *timer_name = NULL;
//char *input_callback = NULL;
void *bp = NULL;
int len = 0;
switch_input_callback_function_t dtmf_func = NULL;
struct input_callback_state cb_state = {0};
switch_file_handle_t fh;
JSFunction *function;
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) {
if ((function = JS_ValueToFunction(cx, argv[2]))) {
memset(&cb_state, 0, sizeof(cb_state));
cb_state.function = function;
if (argc > 3) {
cb_state.arg = argv[3];
}
cb_state.session_state = jss;
cb_state.cx = cx;
cb_state.obj = obj;
dtmf_func = js_stream_input_callback;
bp = &cb_state;
len = sizeof(cb_state);
}
}
memset(&fh, 0, sizeof(fh));
cb_state.extra = &fh;
cb_state.ret = BOOLEAN_TO_JSVAL( JS_FALSE );
switch_ivr_play_file(jss->session, &fh, file_name, timer_name, dtmf_func, bp, len);
*rval = cb_state.ret;
return (switch_channel_ready(channel)) ? JS_TRUE : JS_FALSE;
}
static JSBool session_set_variable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel_t *channel;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
if (argc > 1) {
char *var, *val;
var = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
val = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
switch_channel_set_variable(channel, var, val);
*rval = BOOLEAN_TO_JSVAL( JS_TRUE );
} else {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
}
return JS_TRUE;
}
static JSBool session_get_variable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel_t *channel;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
if (argc > 0) {
char *var, *val;
var = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
val = switch_channel_get_variable(channel, var);
if (val) {
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, val));
} else {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
}
} else {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
}
return JS_TRUE;
}
static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel_t *channel;
char *tts_name = NULL;
char *voice_name = NULL;
char *text = NULL;
char *timer_name = NULL;
switch_codec_t *codec;
void *bp = NULL;
int len = 0;
struct input_callback_state cb_state = {0};
switch_input_callback_function_t dtmf_func = NULL;
JSFunction *function;
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) {
if ((function = JS_ValueToFunction(cx, argv[3]))) {
memset(&cb_state, 0, sizeof(cb_state));
cb_state.function = function;
if (argc > 4) {
cb_state.arg = argv[4];
}
cb_state.cx = cx;
cb_state.obj = obj;
cb_state.session_state = jss;
dtmf_func = js_collect_input_callback;
bp = &cb_state;
len = sizeof(cb_state);
}
}
if (argc > 5) {
timer_name = JS_GetStringBytes(JS_ValueToString(cx, argv[5]));
}
if (!tts_name && text) {
return JS_FALSE;
}
codec = switch_core_session_get_read_codec(jss->session);
cb_state.ret = BOOLEAN_TO_JSVAL( JS_FALSE );
switch_ivr_speak_text(jss->session,
tts_name,
voice_name && strlen(voice_name) ? voice_name : NULL,
timer_name,
codec->implementation->samples_per_second,
dtmf_func,
text,
bp,
len);
*rval = cb_state.ret;
return (switch_channel_ready(channel)) ? 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;
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);
}
buf = switch_core_session_alloc(jss->session, digits);
switch_ivr_collect_digits_count(jss->session, buf, digits, digits, terminators, &term, timeout);
*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_t *channel;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
switch_channel_answer(channel);
return JS_TRUE;
}
static JSBool session_ready(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel_t *channel;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
*rval = BOOLEAN_TO_JSVAL( switch_channel_ready(channel) ? JS_TRUE : JS_FALSE );
return JS_TRUE;
}
static JSBool session_wait_for_media(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel_t *channel;
switch_time_t started;
unsigned int elapsed;
int32 timeout = 60;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
started = switch_time_now();
if (argc > 0) {
JS_ValueToInt32(cx, argv[0], &timeout);
}
for(;;) {
if (((elapsed = (unsigned int)((switch_time_now() - started) / 1000)) > (switch_time_t)timeout) || switch_channel_get_state(channel) >= CS_HANGUP) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
break;
}
if (switch_channel_ready(channel) && (switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag(channel, CF_EARLY_MEDIA))) {
*rval = BOOLEAN_TO_JSVAL( JS_TRUE );
break;
}
switch_yield(1000);
}
return JS_TRUE;
}
static JSBool session_wait_for_answer(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel_t *channel;
switch_time_t started;
unsigned int elapsed;
int32 timeout = 60;
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
started = switch_time_now();
if (argc > 0) {
JS_ValueToInt32(cx, argv[0], &timeout);
}
for(;;) {
if (((elapsed = (unsigned int)((switch_time_now() - started) / 1000)) > (switch_time_t)timeout) || switch_channel_get_state(channel) >= CS_HANGUP) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
break;
}
if (switch_channel_ready(channel) && switch_channel_test_flag(channel, CF_ANSWERED)) {
*rval = BOOLEAN_TO_JSVAL( JS_TRUE );
break;
}
switch_yield(1000);
}
return JS_TRUE;
}
static JSBool session_execute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSBool retval = JS_FALSE;
if (argc > 1) {
const switch_application_interface_t *application_interface;
char *app_name = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
char *app_arg = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
struct js_session *jss = JS_GetPrivate(cx, obj);
jsrefcount saveDepth;
if ((application_interface = switch_loadable_module_get_application_interface(app_name))) {
if (application_interface->application_function) {
saveDepth = JS_SuspendRequest(cx);
application_interface->application_function(jss->session, app_arg);
JS_ResumeRequest(cx, saveDepth);
retval = JS_TRUE;
}
}
}
*rval = BOOLEAN_TO_JSVAL( retval );
return JS_TRUE;
}
static JSBool session_get_event(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_event_t *event;
if (switch_core_session_dequeue_event(jss->session, &event) == SWITCH_STATUS_SUCCESS) {
JSObject *Event;
struct event_obj *eo;
if ((eo = malloc(sizeof(*eo)))) {
eo->event = event;
eo->freed = 0;
if ((Event = JS_DefineObject(cx, obj, "__event__", &event_class, NULL, 0))) {
if ((JS_SetPrivate(cx, Event, eo) &&
JS_DefineProperties(cx, Event, event_props) &&
JS_DefineFunctions(cx, Event, event_methods))) {
*rval = OBJECT_TO_JSVAL ( Event );
return JS_TRUE;
}
}
}
}
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
static JSBool session_send_event(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
JSObject *Event;
struct event_obj *eo;
if (argc > 0) {
if (JS_ValueToObject(cx, argv[0], &Event)) {
if ((eo = JS_GetPrivate(cx, Event))) {
if (switch_core_session_receive_event(jss->session, &eo->event) != SWITCH_STATUS_SUCCESS) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
JS_SetPrivate(cx, Event, NULL);
}
}
}
*rval = BOOLEAN_TO_JSVAL( JS_TRUE );
return JS_TRUE;
}
static JSBool session_hangup(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss = JS_GetPrivate(cx, obj);
switch_channel_t *channel;
char *cause_name = NULL;
switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING;
if (argc > 1) {
cause_name = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
cause = switch_channel_str2cause(cause_name);
}
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
switch_channel_hangup(channel, cause);
switch_core_session_kill_channel(jss->session, SWITCH_SIG_KILL);
return JS_TRUE;
}
#ifdef HAVE_CURL
struct config_data {
JSContext *cx;
JSObject *obj;
char *name;
int fd;
};
static size_t hash_callback(void *ptr, size_t size, size_t nmemb, void *data)
{
register size_t 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 size_t file_callback(void *ptr, size_t size, size_t nmemb, void *data)
{
register unsigned int realsize = (unsigned int)(size * nmemb);
struct config_data *config_data = data;
write(config_data->fd, ptr, realsize);
return realsize;
}
static JSBool js_fetchurl_hash(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 > 1 ) {
url = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
name = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
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, hash_callback);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&config_data);
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-js/1.0");
curl_easy_perform(curl_handle);
curl_easy_cleanup(curl_handle);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error!\n");
return JS_FALSE;
}
return JS_TRUE;
}
static JSBool js_fetchurl_file(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char *url = NULL, *filename = NULL;
CURL *curl_handle = NULL;
struct config_data config_data;
if ( argc > 1 ) {
url = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
filename = 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;
config_data.name = filename;
if ((config_data.fd = open(filename, O_CREAT | O_RDWR | O_TRUNC)) > -1) {
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, file_callback);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&config_data);
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "freeswitch-js/1.0");
curl_easy_perform(curl_handle);
curl_easy_cleanup(curl_handle);
close(config_data.fd);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error!\n");
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error!\n");
}
return JS_TRUE;
}
#endif
/* Session Object */
/*********************************************************************************/
enum session_tinyid {
SESSION_NAME, SESSION_STATE,
PROFILE_DIALPLAN, PROFILE_CID_NAME, PROFILE_CID_NUM, PROFILE_IP, PROFILE_ANI, PROFILE_ANI_II, PROFILE_DEST
};
static JSFunctionSpec session_methods[] = {
{"streamFile", session_streamfile, 1},
{"collectInput", session_collect_input, 1},
{"recordFile", session_recordfile, 1},
{"flushEvents", session_flush_events, 1},
{"flushDigits", session_flush_digits, 1},
{"speak", session_speak, 1},
{"setVariable", session_set_variable, 1},
{"getVariable", session_get_variable, 1},
{"getDigits", session_get_digits, 1},
{"answer", session_answer, 0},
{"ready", session_ready, 0},
{"waitForAnswer", session_wait_for_answer, 0},
{"waitForMedia", session_wait_for_media, 0},
{"getEvent", session_get_event, 0},
{"sendEvent", session_send_event, 0},
{"hangup", session_hangup, 0},
{"execute", session_execute, 0},
{0}
};
static JSPropertySpec session_props[] = {
{"name", SESSION_NAME, JSPROP_READONLY|JSPROP_PERMANENT},
{"state", SESSION_STATE, JSPROP_READONLY|JSPROP_PERMANENT},
{"dialplan", PROFILE_DIALPLAN, JSPROP_READONLY|JSPROP_PERMANENT},
{"caller_id_name", PROFILE_CID_NAME, JSPROP_READONLY|JSPROP_PERMANENT},
{"caller_id_num", PROFILE_CID_NUM, JSPROP_READONLY|JSPROP_PERMANENT},
{"network_addr", PROFILE_IP, JSPROP_READONLY|JSPROP_PERMANENT},
{"ani", PROFILE_ANI, JSPROP_READONLY|JSPROP_PERMANENT},
{"aniii", PROFILE_ANI_II, JSPROP_READONLY|JSPROP_PERMANENT},
{"destination", PROFILE_DEST, 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_t *channel;
switch_caller_profile_t *caller_profile;
char *name;
if (!jss || !jss->session) {
return JS_FALSE;
}
channel = switch_core_session_get_channel(jss->session);
assert(channel != NULL);
caller_profile = switch_channel_get_caller_profile(channel);
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;
case PROFILE_DIALPLAN:
if (caller_profile) {
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, caller_profile->dialplan));
}
break;
case PROFILE_CID_NAME:
if (caller_profile) {
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, caller_profile->caller_id_name));
}
break;
case PROFILE_CID_NUM:
if (caller_profile) {
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, caller_profile->caller_id_number));
}
break;
case PROFILE_IP:
if (caller_profile) {
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, caller_profile->network_addr));
}
break;
case PROFILE_ANI:
if (caller_profile) {
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, caller_profile->ani));
}
break;
case PROFILE_ANI_II:
if (caller_profile) {
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, caller_profile->aniii));
}
break;
case PROFILE_DEST:
if (caller_profile) {
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, caller_profile->destination_number));
}
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, session_destroy, NULL, NULL, NULL,
session_construct
};
static JSObject *new_js_session(JSContext *cx, JSObject *obj, switch_core_session_t *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;
}
/* Session Object */
/*********************************************************************************/
static JSBool session_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
switch_memory_pool_t *pool = NULL;
if (argc > 1) {
switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING;
struct js_session *jss = NULL;
JSObject *session_obj;
switch_core_session_t *session = NULL, *peer_session = NULL;
switch_caller_profile_t *caller_profile = NULL;
char *dest = NULL;
char *dialplan = NULL;
char *cid_name = "";
char *cid_num = "";
char *network_addr = "";
char *ani = "";
char *aniii = "";
char *rdnis = "";
char *context = "";
char *username = NULL;
char *to = NULL;
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
if (JS_ValueToObject(cx, argv[0], &session_obj)) {
struct js_session *old_jss = NULL;
if ((old_jss = JS_GetPrivate(cx, session_obj))) {
session = old_jss->session;
}
}
dest = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
if (!strchr(dest, '/')) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Channel String\n");
return JS_TRUE;
}
if (argc > 2) {
dialplan = JS_GetStringBytes(JS_ValueToString(cx, argv[2]));
}
if (argc > 3) {
context = JS_GetStringBytes(JS_ValueToString(cx, argv[3]));
}
if (argc > 4) {
cid_name = JS_GetStringBytes(JS_ValueToString(cx, argv[4]));
}
if (argc > 5) {
cid_num = JS_GetStringBytes(JS_ValueToString(cx, argv[5]));
}
if (argc > 6) {
network_addr = JS_GetStringBytes(JS_ValueToString(cx, argv[6]));
}
if (argc > 7) {
ani = JS_GetStringBytes(JS_ValueToString(cx, argv[7]));
}
if (argc > 8) {
aniii = JS_GetStringBytes(JS_ValueToString(cx, argv[8]));
}
if (argc > 9) {
rdnis = JS_GetStringBytes(JS_ValueToString(cx, argv[9]));
}
if (argc > 10) {
username = JS_GetStringBytes(JS_ValueToString(cx, argv[10]));
}
if (argc > 11) {
to = JS_GetStringBytes(JS_ValueToString(cx, argv[11]));
}
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n");
return JS_FALSE;
}
caller_profile = switch_caller_profile_new(pool,
username,
dialplan,
cid_name,
cid_num,
network_addr,
ani,
aniii,
rdnis,
(char *)modname,
context,
dest);
if (switch_ivr_originate(session, &peer_session, &cause, dest, to ? atoi(to) : 60, NULL, NULL, NULL, caller_profile) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot Create Outgoing Channel! [%s]\n", dest);
return JS_TRUE;
}
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, switch_channel_cause2str(cause)));
jss = switch_core_session_alloc(peer_session, sizeof(*jss));
jss->session = peer_session;
jss->flags = 0;
jss->cx = cx;
jss->obj = obj;
JS_SetPrivate(cx, obj, jss);
switch_set_flag(jss, S_HUP);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing Args\n");
}
return JS_TRUE;
}
static void session_destroy(JSContext *cx, JSObject *obj)
{
struct js_session *jss;
if (cx && obj) {
if ((jss = JS_GetPrivate(cx, obj))) {
if (switch_test_flag(jss, S_HUP)) {
switch_channel_t *channel;
if (jss->session) {
channel = switch_core_session_get_channel(jss->session);
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
}
}
}
}
return;
}
/* FileIO Object */
/*********************************************************************************/
static JSBool fileio_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
switch_memory_pool_t *pool;
switch_file_t *fd;
char *path, *flags_str;
unsigned int flags = 0;
struct fileio_obj *fio;
if (argc > 1) {
path = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
flags_str = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
if (strchr(flags_str, 'r')) {
flags |= SWITCH_FOPEN_READ;
}
if (strchr(flags_str, 'w')) {
flags |= SWITCH_FOPEN_WRITE;
}
if (strchr(flags_str, 'c')) {
flags |= SWITCH_FOPEN_CREATE;
}
if (strchr(flags_str, 'a')) {
flags |= SWITCH_FOPEN_APPEND;
}
if (strchr(flags_str, 't')) {
flags |= SWITCH_FOPEN_TRUNCATE;
}
if (strchr(flags_str, 'b')) {
flags |= SWITCH_FOPEN_BINARY;
}
switch_core_new_memory_pool(&pool);
if (switch_file_open(&fd, path, flags, SWITCH_FPROT_UREAD|SWITCH_FPROT_UWRITE, pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Open File: %s\n", path);
switch_core_destroy_memory_pool(&pool);
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
fio = switch_core_alloc(pool, sizeof(*fio));
fio->fd = fd;
fio->pool = pool;
fio->path = switch_core_strdup(pool, path);
fio->flags = flags;
JS_SetPrivate(cx, obj, fio);
return JS_TRUE;
}
return JS_TRUE;
}
static void fileio_destroy(JSContext *cx, JSObject *obj)
{
struct fileio_obj *fio = JS_GetPrivate(cx, obj);
if (fio) {
switch_memory_pool_t *pool;
if (fio->fd) {
switch_file_close(fio->fd);
}
pool = fio->pool;
switch_core_destroy_memory_pool(&pool);
pool = NULL;
}
}
static JSBool fileio_read(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct fileio_obj *fio = JS_GetPrivate(cx, obj);
int32 bytes = 0;
switch_size_t read = 0;
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
if (fio) {
return JS_TRUE;
}
if (!(fio->flags & SWITCH_FOPEN_READ)) {
return JS_TRUE;
}
if (argc > 0) {
JS_ValueToInt32(cx, argv[0], &bytes);
}
if (bytes) {
if (!fio->buf || fio->bufsize < bytes) {
fio->buf = switch_core_alloc(fio->pool, bytes);
fio->bufsize = bytes;
}
read = bytes;
switch_file_read(fio->fd, fio->buf, &read);
fio->buflen = read;
*rval = BOOLEAN_TO_JSVAL( (read > 0) ? JS_TRUE : JS_FALSE );
}
return JS_TRUE;
}
static JSBool fileio_data(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct fileio_obj *fio = JS_GetPrivate(cx, obj);
if (!fio) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, fio->buf));
return JS_TRUE;
}
static JSBool fileio_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct fileio_obj *fio = JS_GetPrivate(cx, obj);
switch_size_t wrote = 0;
char *data = NULL;
if (!fio) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
if (!(fio->flags & SWITCH_FOPEN_WRITE)) {
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
if (argc > 0) {
data = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
}
if (data) {
wrote = strlen(data);
*rval = BOOLEAN_TO_JSVAL( (switch_file_write(fio->fd, data, &wrote) == SWITCH_STATUS_SUCCESS) ? JS_TRUE : JS_FALSE);
}
*rval = BOOLEAN_TO_JSVAL( JS_FALSE );
return JS_TRUE;
}
enum fileio_tinyid {
FILEIO_PATH, FILEIO_OPEN
};
static JSFunctionSpec fileio_methods[] = {
{"read", fileio_read, 1},
{"write", fileio_write, 1},
{"data", fileio_data, 0},
{0}
};
static JSPropertySpec fileio_props[] = {
{"path", FILEIO_PATH, JSPROP_READONLY|JSPROP_PERMANENT},
{"open", FILEIO_OPEN, JSPROP_READONLY|JSPROP_PERMANENT},
{0}
};
static JSBool fileio_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSBool res = JS_TRUE;
struct fileio_obj *fio = JS_GetPrivate(cx, obj);
char *name;
int param = 0;
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 FILEIO_PATH:
if (fio) {
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, fio->path));
} else {
*vp = BOOLEAN_TO_JSVAL( JS_FALSE );
}
break;
case FILEIO_OPEN:
*vp = BOOLEAN_TO_JSVAL( fio ? JS_TRUE : JS_FALSE );
break;
}
return res;
}
JSClass fileio_class = {
"FileIO", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, fileio_getProperty, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, fileio_destroy, NULL, NULL, NULL,
fileio_construct
};
/* Built-In*/
/*********************************************************************************/
static JSBool js_exit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
return JS_FALSE;
}
static JSBool js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char *level_str, *msg;
switch_log_level_t level = SWITCH_LOG_DEBUG;
JSScript *script = NULL;
const char *file = __FILE__;
int line = __LINE__;
JSStackFrame *caller;
caller = JS_GetScriptedCaller(cx, NULL);
script = JS_GetFrameScript(cx, caller);
if (script) {
file = JS_GetScriptFilename(cx, script);
line = JS_GetScriptBaseLineNumber(cx, script);
}
if (argc > 1) {
if ((level_str = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) {
level = switch_log_str2level(level_str);
}
if ((msg = JS_GetStringBytes(JS_ValueToString(cx, argv[1])))) {
switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char*) file, "console_log", line, level, "%s", msg);
return JS_TRUE;
}
} else if (argc > 0) {
if ((msg = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) {
switch_log_printf(SWITCH_CHANNEL_ID_LOG, (char*) file, "console_log", line, level, "%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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Arguements\n");
return JS_FALSE;
}
#define B64BUFFLEN 1024
static const char c64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static int write_buf(int fd, char *buf) {
int len = (int)strlen(buf);
if (fd && write(fd, buf, len) != len) {
close(fd);
return 0;
}
return 1;
}
static JSBool js_api_use(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
char *mod_name = NULL;
if (argc > 0 && (mod_name = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) {
const sm_module_interface_t *mp;
if ((mp = switch_core_hash_find(module_manager.load_hash, mod_name))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Loading %s\n", mod_name);
mp->spidermonkey_load(cx, obj);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error loading %s\n", mod_name);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Filename\n");
}
return JS_TRUE;
}
static JSBool js_api_execute(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
if (argc > 1) {
char *cmd = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
char *arg = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
Modify XML Dialplan BTW, forget what I said yesterday RE: the strftime app I was at McDonalds, how can I concentrate there eh? see below.... The Definitive Guide To XML Dialplan: The "dialplan" section of the freeswitch.xml meta document may contain several contexts <?xml version="1.0"?> <document type="freeswitch/xml"> <section name="dialplan" description="Regex/XML Dialplan"> <!-- the default context is a safe start --> <context name="default"> <!-- one or more extension tags --> </context> <!-- more optional contexts --> </section> </document> The important thing to remember is that the dialplan is parsed once when the call hits the dialplan parser in the RING state. With one pass across the XML the result will be a complete list of instructions installed into the channel based on parsed <action> or <anti-action> tags. Those accustomed to Asterisk may expect the call to follow the dialplan by executing the applications as it parses them allowing data obtained from one action to influence the next action. This not the case with the exception being the %{api func} {api arg} field type where an pluggable api call from a module may be executed as the parsing occurs but this is meant to be used to draw realtime info such as date and time or other quickly accessible information and shold *not* be abused. The anatomy of an <extension> tag. Legend: Text wrapped in [] indicates optional and is not part of the actual code. a '|' inside [] indicates mutiple possible values and also is not part of the code. Text wrapped in {} indicates it's a description of the parameter in place of the param itself. <extension name="{exten_name}" [continue="[true|false]"]> continue=true means even if an extension executes to continue parsing the next extension too The {exten_name} above may anything but if it's an exact match with the destination number the parser will leap to this extension to begin the searching that does not mean it will execute the extension. Searching will either begin at the first extension in the context or at the point the the parser has jumped to in the case described above. Each condition is parsed in turn first taking the 'field' param. The parser will apply the perl regular expression to each 'field' param encountered. If the expression matches, it will parse each existing <action> tag in turn and add the data from the <application> tags to the channels todo list. If a matched expression contains any data wrapped in () the variables $1,$2..$N will be valid and expanded in any of 'data' params from the subsequent action tags. If the expression does NOT match, it will parse each <anti-action> tag in turn and add the data from the <application> tags to the channels todo list. *NOTE* since there was no match the () feature is not availabe in anti-actions The 'break' param indicates how to behave in relation to matching: *) 'on-true' - stop searching conditions after the first successful match. *) 'on-false' - stop searching after the first unsuccessful match. *) 'always' - stop at this conditon regardless of a match or non-match. *) 'never' - continue searching regardless of a match or non-match. <condition field="[{field name}|${variable name}|%{api func} {api arg}]" expression="{expression}" break="[on-true|on-false|always|never]"> <action application="{app name}" data="{app arg}"/> <anti-action application="{app name}" data="{app arg}"/> </condition> <!-- any number of condition tags may follow where the same rules apply --> </extension> git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@2167 d0543943-73ff-0310-b7d9-9358b9ac24b2
2006-07-26 20:12:49 +00:00
switch_core_session_t *session = NULL;
switch_stream_handle_t stream = {0};
char retbuf[2048] = "";
Modify XML Dialplan BTW, forget what I said yesterday RE: the strftime app I was at McDonalds, how can I concentrate there eh? see below.... The Definitive Guide To XML Dialplan: The "dialplan" section of the freeswitch.xml meta document may contain several contexts <?xml version="1.0"?> <document type="freeswitch/xml"> <section name="dialplan" description="Regex/XML Dialplan"> <!-- the default context is a safe start --> <context name="default"> <!-- one or more extension tags --> </context> <!-- more optional contexts --> </section> </document> The important thing to remember is that the dialplan is parsed once when the call hits the dialplan parser in the RING state. With one pass across the XML the result will be a complete list of instructions installed into the channel based on parsed <action> or <anti-action> tags. Those accustomed to Asterisk may expect the call to follow the dialplan by executing the applications as it parses them allowing data obtained from one action to influence the next action. This not the case with the exception being the %{api func} {api arg} field type where an pluggable api call from a module may be executed as the parsing occurs but this is meant to be used to draw realtime info such as date and time or other quickly accessible information and shold *not* be abused. The anatomy of an <extension> tag. Legend: Text wrapped in [] indicates optional and is not part of the actual code. a '|' inside [] indicates mutiple possible values and also is not part of the code. Text wrapped in {} indicates it's a description of the parameter in place of the param itself. <extension name="{exten_name}" [continue="[true|false]"]> continue=true means even if an extension executes to continue parsing the next extension too The {exten_name} above may anything but if it's an exact match with the destination number the parser will leap to this extension to begin the searching that does not mean it will execute the extension. Searching will either begin at the first extension in the context or at the point the the parser has jumped to in the case described above. Each condition is parsed in turn first taking the 'field' param. The parser will apply the perl regular expression to each 'field' param encountered. If the expression matches, it will parse each existing <action> tag in turn and add the data from the <application> tags to the channels todo list. If a matched expression contains any data wrapped in () the variables $1,$2..$N will be valid and expanded in any of 'data' params from the subsequent action tags. If the expression does NOT match, it will parse each <anti-action> tag in turn and add the data from the <application> tags to the channels todo list. *NOTE* since there was no match the () feature is not availabe in anti-actions The 'break' param indicates how to behave in relation to matching: *) 'on-true' - stop searching conditions after the first successful match. *) 'on-false' - stop searching after the first unsuccessful match. *) 'always' - stop at this conditon regardless of a match or non-match. *) 'never' - continue searching regardless of a match or non-match. <condition field="[{field name}|${variable name}|%{api func} {api arg}]" expression="{expression}" break="[on-true|on-false|always|never]"> <action application="{app name}" data="{app arg}"/> <anti-action application="{app name}" data="{app arg}"/> </condition> <!-- any number of condition tags may follow where the same rules apply --> </extension> git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@2167 d0543943-73ff-0310-b7d9-9358b9ac24b2
2006-07-26 20:12:49 +00:00
if (argc > 2) {
JSObject *session_obj;
struct js_session *jss;
if (JS_ValueToObject(cx, argv[2], &session_obj)) {
if ((jss = JS_GetPrivate(cx, session_obj))) {
session = jss->session;
}
}
}
stream.data = retbuf;
stream.end = stream.data;
stream.data_size = sizeof(retbuf);
Modify XML Dialplan BTW, forget what I said yesterday RE: the strftime app I was at McDonalds, how can I concentrate there eh? see below.... The Definitive Guide To XML Dialplan: The "dialplan" section of the freeswitch.xml meta document may contain several contexts <?xml version="1.0"?> <document type="freeswitch/xml"> <section name="dialplan" description="Regex/XML Dialplan"> <!-- the default context is a safe start --> <context name="default"> <!-- one or more extension tags --> </context> <!-- more optional contexts --> </section> </document> The important thing to remember is that the dialplan is parsed once when the call hits the dialplan parser in the RING state. With one pass across the XML the result will be a complete list of instructions installed into the channel based on parsed <action> or <anti-action> tags. Those accustomed to Asterisk may expect the call to follow the dialplan by executing the applications as it parses them allowing data obtained from one action to influence the next action. This not the case with the exception being the %{api func} {api arg} field type where an pluggable api call from a module may be executed as the parsing occurs but this is meant to be used to draw realtime info such as date and time or other quickly accessible information and shold *not* be abused. The anatomy of an <extension> tag. Legend: Text wrapped in [] indicates optional and is not part of the actual code. a '|' inside [] indicates mutiple possible values and also is not part of the code. Text wrapped in {} indicates it's a description of the parameter in place of the param itself. <extension name="{exten_name}" [continue="[true|false]"]> continue=true means even if an extension executes to continue parsing the next extension too The {exten_name} above may anything but if it's an exact match with the destination number the parser will leap to this extension to begin the searching that does not mean it will execute the extension. Searching will either begin at the first extension in the context or at the point the the parser has jumped to in the case described above. Each condition is parsed in turn first taking the 'field' param. The parser will apply the perl regular expression to each 'field' param encountered. If the expression matches, it will parse each existing <action> tag in turn and add the data from the <application> tags to the channels todo list. If a matched expression contains any data wrapped in () the variables $1,$2..$N will be valid and expanded in any of 'data' params from the subsequent action tags. If the expression does NOT match, it will parse each <anti-action> tag in turn and add the data from the <application> tags to the channels todo list. *NOTE* since there was no match the () feature is not availabe in anti-actions The 'break' param indicates how to behave in relation to matching: *) 'on-true' - stop searching conditions after the first successful match. *) 'on-false' - stop searching after the first unsuccessful match. *) 'always' - stop at this conditon regardless of a match or non-match. *) 'never' - continue searching regardless of a match or non-match. <condition field="[{field name}|${variable name}|%{api func} {api arg}]" expression="{expression}" break="[on-true|on-false|always|never]"> <action application="{app name}" data="{app arg}"/> <anti-action application="{app name}" data="{app arg}"/> </condition> <!-- any number of condition tags may follow where the same rules apply --> </extension> git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@2167 d0543943-73ff-0310-b7d9-9358b9ac24b2
2006-07-26 20:12:49 +00:00
switch_api_execute(cmd, arg, session, &stream);
stream.write_function = switch_console_stream_write;
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, retbuf));
} else {
*rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, ""));
}
return JS_TRUE;
}
static JSBool js_bridge(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
struct js_session *jss_a = NULL, *jss_b = NULL;
JSObject *session_obj_a = NULL, *session_obj_b = NULL;
void *bp = NULL;
int len = 0;
switch_input_callback_function_t dtmf_func = NULL;
struct input_callback_state cb_state = {0};
JSFunction *function;
if (argc > 1) {
if (JS_ValueToObject(cx, argv[0], &session_obj_a)) {
if (!(jss_a = JS_GetPrivate(cx, session_obj_a))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Find Session [1]\n");
return JS_FALSE;
}
}
if (JS_ValueToObject(cx, argv[1], &session_obj_b)) {
if (!(jss_b = JS_GetPrivate(cx, session_obj_b))) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Find Session [1]\n");
return JS_FALSE;
}
}
}
if (!(jss_a && jss_b)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failure! %s %s\n", jss_a ? "y" : "n", jss_b ? "y" : "n");
return JS_FALSE;
}
if (argc > 2) {
if ((function = JS_ValueToFunction(cx, argv[2]))) {
memset(&cb_state, 0, sizeof(cb_state));
cb_state.function = function;
if (argc > 3) {
cb_state.arg = argv[3];
}
cb_state.cx = cx;
cb_state.obj = obj;
cb_state.session_state = jss_a;
dtmf_func = js_collect_input_callback;
bp = &cb_state;
len = sizeof(cb_state);
}
}
switch_ivr_multi_threaded_bridge(jss_a->session, jss_b->session, dtmf_func, bp, bp);
return JS_TRUE;
}
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, "%smail.%ld%04x", SWITCH_GLOBAL_dirs.temp_dir, time(NULL), rand() & 0xffff);
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\n"
"Content-Transfer-Encoding: base64\n"
"Content-Description: Sound attachment.\n"
"Content-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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Emailed file [%s] to [%s]\n", filename, to);
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Emailed data to [%s]\n", to);
}
return JS_TRUE;
}
return JS_FALSE;
}
static JSFunctionSpec fs_functions[] = {
{"console_log", js_log, 2},
{"exit", js_exit, 0},
{"include", js_include, 1},
{"email", js_email, 2},
{"bridge", js_bridge, 2},
{"apiExecute", js_api_execute, 2},
{"use", js_api_use, 1},
#ifdef HAVE_CURL
{"fetchURLHash", js_fetchurl_hash, 1},
{"fetchURLFile", js_fetchurl_file, 1},
#endif
{0}
};
SWITCH_SM_DECLARE(int) eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval)
{
JSScript *script = NULL;
char *cptr;
char *path = NULL;
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 == '/') {
script = JS_CompileFile(cx, obj, code);
} else if ((path = switch_mprintf("%s%s%s", SWITCH_GLOBAL_dirs.script_dir, SWITCH_PATH_SEPARATOR, code))) {
script = JS_CompileFile(cx, obj, path);
switch_safe_free(path);
}
}
if (script) {
res = JS_ExecuteScript(cx, obj, script, rval) == JS_TRUE ? 1 : 0;
JS_DestroyScript(cx, script);
}
return res;
}
static int env_init(JSContext *cx, JSObject *javascript_object)
{
JS_DefineFunctions(cx, javascript_object, fs_functions);
JS_InitStandardClasses(cx, javascript_object);
JS_InitClass(cx,
javascript_object,
NULL,
&session_class,
session_construct,
3,
session_props,
session_methods,
session_props,
session_methods
);
JS_InitClass(cx,
javascript_object,
NULL,
&fileio_class,
fileio_construct,
3,
fileio_props,
fileio_methods,
fileio_props,
fileio_methods
);
JS_InitClass(cx,
javascript_object,
NULL,
&event_class,
event_construct,
3,
event_props,
event_methods,
event_props,
event_methods
);
return 1;
}
static void js_parse_and_execute(switch_core_session_t *session, char *input_code)
{
JSObject *javascript_global_object = NULL;
char buf[1024], *script, *arg, *argv[512];
int argc = 0, x = 0, y = 0;
unsigned int flags = 0;
struct js_session jss;
JSContext *cx = NULL;
jsval rval;
if ((cx = JS_NewContext(globals.rt, globals.gStackChunkSize))) {
JS_BeginRequest(cx);
JS_SetErrorReporter(cx, js_error);
javascript_global_object = JS_NewObject(cx, &global_class, NULL, NULL);
env_init(cx, javascript_global_object);
JS_SetGlobalObject(cx, javascript_global_object);
/* Emaculent conception of session object into the script if one is available */
if (session && new_js_session(cx, javascript_global_object, session, &jss, "session", flags)) {
JS_SetPrivate(cx, javascript_global_object, session);
}
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Allocation Error!\n");
return;
}
script = input_code;
if ((arg = strchr(script, ' '))) {
*arg++ = '\0';
argc = switch_separate_string(arg, ':', argv, (sizeof(argv) / sizeof(argv[0])));
}
if (argc) { /* create a js doppleganger of this argc/argv*/
snprintf(buf, sizeof(buf), "~var argv = new Array(%d);", argc);
eval_some_js(buf, cx, javascript_global_object, &rval);
snprintf(buf, sizeof(buf), "~var argc = %d", argc);
eval_some_js(buf, cx, javascript_global_object, &rval);
for (y = 0; y < argc; y++) {
snprintf(buf, sizeof(buf), "~argv[%d] = \"%s\";", x++, argv[y]);
eval_some_js(buf, cx, javascript_global_object, &rval);
}
}
if (cx) {
eval_some_js(script, cx, javascript_global_object, &rval);
JS_DestroyContext(cx);
}
}
static void *SWITCH_THREAD_FUNC js_thread_run(switch_thread_t *thread, void *obj)
{
char *input_code = obj;
js_parse_and_execute(NULL, input_code);
if (input_code) {
free(input_code);
}
return NULL;
}
static switch_memory_pool_t *module_pool = NULL;
static void js_thread_launch(char *text)
{
switch_thread_t *thread;
switch_threadattr_t *thd_attr = NULL;
if (!module_pool) {
if (switch_core_new_memory_pool(&module_pool) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n");
return;
}
}
switch_threadattr_create(&thd_attr, module_pool);
switch_threadattr_detach_set(thd_attr, 1);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
switch_thread_create(&thread, thd_attr, js_thread_run, strdup(text), module_pool);
}
Modify XML Dialplan BTW, forget what I said yesterday RE: the strftime app I was at McDonalds, how can I concentrate there eh? see below.... The Definitive Guide To XML Dialplan: The "dialplan" section of the freeswitch.xml meta document may contain several contexts <?xml version="1.0"?> <document type="freeswitch/xml"> <section name="dialplan" description="Regex/XML Dialplan"> <!-- the default context is a safe start --> <context name="default"> <!-- one or more extension tags --> </context> <!-- more optional contexts --> </section> </document> The important thing to remember is that the dialplan is parsed once when the call hits the dialplan parser in the RING state. With one pass across the XML the result will be a complete list of instructions installed into the channel based on parsed <action> or <anti-action> tags. Those accustomed to Asterisk may expect the call to follow the dialplan by executing the applications as it parses them allowing data obtained from one action to influence the next action. This not the case with the exception being the %{api func} {api arg} field type where an pluggable api call from a module may be executed as the parsing occurs but this is meant to be used to draw realtime info such as date and time or other quickly accessible information and shold *not* be abused. The anatomy of an <extension> tag. Legend: Text wrapped in [] indicates optional and is not part of the actual code. a '|' inside [] indicates mutiple possible values and also is not part of the code. Text wrapped in {} indicates it's a description of the parameter in place of the param itself. <extension name="{exten_name}" [continue="[true|false]"]> continue=true means even if an extension executes to continue parsing the next extension too The {exten_name} above may anything but if it's an exact match with the destination number the parser will leap to this extension to begin the searching that does not mean it will execute the extension. Searching will either begin at the first extension in the context or at the point the the parser has jumped to in the case described above. Each condition is parsed in turn first taking the 'field' param. The parser will apply the perl regular expression to each 'field' param encountered. If the expression matches, it will parse each existing <action> tag in turn and add the data from the <application> tags to the channels todo list. If a matched expression contains any data wrapped in () the variables $1,$2..$N will be valid and expanded in any of 'data' params from the subsequent action tags. If the expression does NOT match, it will parse each <anti-action> tag in turn and add the data from the <application> tags to the channels todo list. *NOTE* since there was no match the () feature is not availabe in anti-actions The 'break' param indicates how to behave in relation to matching: *) 'on-true' - stop searching conditions after the first successful match. *) 'on-false' - stop searching after the first unsuccessful match. *) 'always' - stop at this conditon regardless of a match or non-match. *) 'never' - continue searching regardless of a match or non-match. <condition field="[{field name}|${variable name}|%{api func} {api arg}]" expression="{expression}" break="[on-true|on-false|always|never]"> <action application="{app name}" data="{app arg}"/> <anti-action application="{app name}" data="{app arg}"/> </condition> <!-- any number of condition tags may follow where the same rules apply --> </extension> git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@2167 d0543943-73ff-0310-b7d9-9358b9ac24b2
2006-07-26 20:12:49 +00:00
static switch_status_t launch_async(char *text, switch_core_session_t *session, switch_stream_handle_t *stream)
{
if (switch_strlen_zero(text)) {
stream->write_function(stream, "USAGE: %s\n", js_run_interface.syntax);
return SWITCH_STATUS_SUCCESS;
}
js_thread_launch(text);
stream->write_function(stream, "OK\n");
return SWITCH_STATUS_SUCCESS;
}
static const switch_application_interface_t ivrtest_application_interface = {
/*.interface_name */ "javascript",
/*.application_function */ js_parse_and_execute,
NULL, NULL, NULL,
/*.next*/ NULL
};
static switch_api_interface_t js_run_interface = {
/*.interface_name */ "jsrun",
/*.desc */ "run a script",
/*.function */ launch_async,
/*.syntax */ "jsrun <script>",
/*.next */ NULL
};
static switch_loadable_module_interface_t 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 */ &js_run_interface,
/*.file_interface */ NULL,
/*.speech_interface */ NULL,
/*.directory_interface */ NULL
};
SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename)
{
switch_status_t status;
if ((status = init_js()) != SWITCH_STATUS_SUCCESS) {
return status;
}
/* connect my internal structure to the blank pointer passed to me */
*module_interface = &spidermonkey_module_interface;
curl_global_init(CURL_GLOBAL_ALL);
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MOD_DECLARE(switch_status_t) switch_module_shutdown(void)
{
curl_global_cleanup();
return SWITCH_STATUS_SUCCESS;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:nil
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
*/