1930 lines
54 KiB
C++
1930 lines
54 KiB
C++
/*
|
|
* mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
* Copyright (C) 2013-2014, Peter Olsson <peter@olssononline.se>
|
|
*
|
|
* Version: MPL 1.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* Ported from the Original Code in FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Anthony Minessale II <anthm@freeswitch.org>
|
|
* Portions created by the Initial Developer are Copyright (C)
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Peter Olsson <peter@olssononline.se>
|
|
* Anthony Minessale II <anthm@freeswitch.org>
|
|
* William King <william.king@quentustech.com>
|
|
*
|
|
* fssession.cpp -- JavaScript Session class
|
|
*
|
|
*/
|
|
|
|
#include "fssession.hpp"
|
|
#include "fsevent.hpp"
|
|
#include "fsdtmf.hpp"
|
|
|
|
using namespace std;
|
|
using namespace v8;
|
|
|
|
static const char js_class_name[] = "Session";
|
|
|
|
#define METHOD_SANITY_CHECK() if (!this->_session) {\
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "No session is active, you must have an active session before calling this method"));\
|
|
return;\
|
|
} else CheckHangupHook(this, NULL)
|
|
|
|
#define CHANNEL_SANITY_CHECK() do {\
|
|
if (!switch_channel_ready(channel)) {\
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Session is not active!"));\
|
|
return;\
|
|
}\
|
|
if (!((switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag(channel, CF_EARLY_MEDIA)))) {\
|
|
switch_channel_pre_answer(channel);\
|
|
if (!((switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag(channel, CF_EARLY_MEDIA)))) {\
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Session is not answered!"));\
|
|
return;\
|
|
}\
|
|
}\
|
|
} while (foo == 1)
|
|
|
|
#define CHANNEL_SANITY_CHECK_ANSWER() do {\
|
|
if (!switch_channel_ready(channel)) {\
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Session is not active!"));\
|
|
return;\
|
|
}\
|
|
} while (foo == 1)
|
|
|
|
#define CHANNEL_MEDIA_SANITY_CHECK() do {\
|
|
if (!switch_channel_media_ready(channel)) {\
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Session is not in media mode!"));\
|
|
return;\
|
|
}\
|
|
} while (foo == 1)
|
|
|
|
#define LOCK_ISOLATE_ON_CALLBACK_MACRO() \
|
|
FSSession *obj = cb_state->session_state;\
|
|
Isolate *isolate = obj->GetOwner()->GetIsolate();\
|
|
Locker lock(isolate);\
|
|
Isolate::Scope isolate_scope(isolate);\
|
|
HandleScope handle_scope(isolate);\
|
|
Local<Context> context = Local<Context>::New(isolate, cb_state->context);\
|
|
Context::Scope context_scope(context);
|
|
|
|
static int foo = 0;
|
|
|
|
string FSSession::GetJSClassName()
|
|
{
|
|
return js_class_name;
|
|
}
|
|
|
|
void FSSession::Init(void)
|
|
{
|
|
_session = NULL;
|
|
flags = 0;
|
|
_cause = SWITCH_CAUSE_NONE;
|
|
_stack_depth = 0;
|
|
_hook_state = CS_EXECUTE;
|
|
_destination_number = NULL;
|
|
_dialplan = NULL;
|
|
_caller_id_name = NULL;
|
|
_caller_id_number = NULL;
|
|
_network_addr = NULL;
|
|
_ani = NULL;
|
|
_aniii = NULL;
|
|
_rdnis = NULL;
|
|
_context = NULL;
|
|
_username = NULL;
|
|
_check_state = 1;
|
|
_speech = NULL;
|
|
}
|
|
|
|
switch_core_session_t *FSSession::GetSession()
|
|
{
|
|
return _session;
|
|
}
|
|
|
|
void FSSession::Init(switch_core_session_t *session, unsigned int flags)
|
|
{
|
|
this->_session = session;
|
|
this->flags = flags;
|
|
}
|
|
|
|
void FSSession::DestroySpeechEngine()
|
|
{
|
|
if (_speech) {
|
|
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
|
|
switch_core_codec_destroy(&_speech->codec);
|
|
switch_core_speech_close(&_speech->sh, &flags);
|
|
_speech = NULL;
|
|
}
|
|
}
|
|
|
|
FSSession::~FSSession(void)
|
|
{
|
|
_on_hangup.Reset();
|
|
|
|
if (_speech && *_speech->sh.name) {
|
|
DestroySpeechEngine();
|
|
}
|
|
|
|
if (_session) {
|
|
switch_channel_t *channel = switch_core_session_get_channel(_session);
|
|
|
|
switch_channel_set_private(channel, "jsobject", NULL);
|
|
switch_core_event_hook_remove_state_change(_session, HangupHook);
|
|
|
|
if (switch_test_flag(this, S_HUP)) {
|
|
switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
|
|
}
|
|
|
|
switch_safe_free(_dialplan);
|
|
switch_safe_free(_username);
|
|
switch_safe_free(_caller_id_name);
|
|
switch_safe_free(_ani);
|
|
switch_safe_free(_aniii);
|
|
switch_safe_free(_caller_id_number);
|
|
switch_safe_free(_network_addr);
|
|
switch_safe_free(_rdnis);
|
|
switch_safe_free(_destination_number);
|
|
switch_safe_free(_context);
|
|
switch_core_session_rwunlock(_session);
|
|
}
|
|
}
|
|
|
|
FSInputCallbackState::FSInputCallbackState(void)
|
|
{
|
|
session_state = NULL;
|
|
memset(&code_buffer, 0, sizeof(code_buffer));
|
|
code_buffer_len = 0;
|
|
memset(&ret_buffer, 0, sizeof(ret_buffer));
|
|
ret_buffer_len = 0;
|
|
digit_count = 0;
|
|
extra = NULL;
|
|
jss_a = NULL;
|
|
jss_b = NULL;
|
|
}
|
|
|
|
FSInputCallbackState::~FSInputCallbackState(void)
|
|
{
|
|
function.Reset();
|
|
arg.Reset();
|
|
ret.Reset();
|
|
session_obj_a.Reset();
|
|
session_obj_b.Reset();
|
|
context.Reset();
|
|
}
|
|
|
|
#define MAX_STACK_DEPTH 2
|
|
|
|
switch_status_t FSSession::CommonCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
|
|
{
|
|
FSInputCallbackState *cb_state = (FSInputCallbackState *)buf;
|
|
switch_event_t *event = NULL;
|
|
int argc = 0;
|
|
Handle<Value> argv[4];
|
|
Handle<Object> Event;
|
|
bool ret = true;
|
|
switch_status_t status = SWITCH_STATUS_FALSE;
|
|
|
|
/* Session sanity check first */
|
|
if (!cb_state->session_state || !cb_state->session_state->_session) {
|
|
if (cb_state->session_state && cb_state->session_state->GetIsolate()) {
|
|
cb_state->session_state->GetIsolate()->ThrowException(String::NewFromUtf8(cb_state->session_state->GetIsolate(), "No session is active, you must have an active session before calling this method"));
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No session is active, you must have an active session before calling this method\n");
|
|
}
|
|
return SWITCH_STATUS_FALSE;
|
|
} else {
|
|
/* Check hangup hook before we continue */
|
|
if (!CheckHangupHook(cb_state->session_state, NULL)) {
|
|
JSMain::ExitScript(cb_state->session_state->GetIsolate(), NULL);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
}
|
|
|
|
if (++cb_state->session_state->_stack_depth > MAX_STACK_DEPTH) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Maximum recursive callback limit %d reached.\n", MAX_STACK_DEPTH);
|
|
cb_state->session_state->_stack_depth--;
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
Isolate *isolate = cb_state->session_state->GetIsolate();
|
|
HandleScope handle_scope(isolate);
|
|
|
|
if (cb_state->jss_a && cb_state->jss_a->_session && cb_state->jss_a->_session == session) {
|
|
argv[argc++] = Local<Object>::New(isolate, cb_state->session_obj_a);
|
|
} else if (cb_state->jss_b && cb_state->jss_b->_session && cb_state->jss_b->_session == session) {
|
|
argv[argc++] = Local<Object>::New(isolate, cb_state->session_obj_b);
|
|
} else {
|
|
argv[argc++] = Local<Object>::New(isolate, cb_state->session_state->GetJavaScriptObject());
|
|
}
|
|
|
|
switch (itype) {
|
|
case SWITCH_INPUT_TYPE_EVENT:
|
|
if ((event = (switch_event_t *) input)) {
|
|
Event = FSEvent::New(event, "", cb_state->session_state->GetOwner());
|
|
if (!Event.IsEmpty()) {
|
|
argv[argc++] = String::NewFromUtf8(isolate, "event");
|
|
argv[argc++] = Local<Object>::New(isolate, Event);
|
|
}
|
|
}
|
|
if (Event.IsEmpty()) {
|
|
goto done;
|
|
}
|
|
break;
|
|
case SWITCH_INPUT_TYPE_DTMF:
|
|
{
|
|
switch_dtmf_t *dtmf = (switch_dtmf_t *) input;
|
|
|
|
if (dtmf) {
|
|
Event = FSDTMF::New(dtmf, "", cb_state->session_state->GetOwner());
|
|
if (!Event.IsEmpty()) {
|
|
argv[argc++] = String::NewFromUtf8(isolate, "dtmf");
|
|
argv[argc++] = Local<Object>::New(isolate, Event);
|
|
} else {
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!cb_state->arg.IsEmpty()) {
|
|
argv[argc++] = Local<Value>::New(isolate, cb_state->arg);
|
|
}
|
|
|
|
CheckHangupHook(cb_state->session_state, &ret);
|
|
|
|
if (ret) {
|
|
if (!cb_state->function.IsEmpty()) {
|
|
Handle<Function> func = Local<Function>::New(isolate, cb_state->function);
|
|
|
|
if (func->IsFunction()) {
|
|
Handle<Value> res = func->Call(isolate->GetCurrentContext()->Global(), argc, argv);
|
|
|
|
if (!res.IsEmpty()) {
|
|
cb_state->ret.Reset(isolate, res);
|
|
} else {
|
|
cb_state->ret.Reset();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
JSMain::ExitScript(cb_state->session_state->GetIsolate(), NULL);
|
|
status = SWITCH_STATUS_FALSE;
|
|
goto done;
|
|
}
|
|
|
|
status = SWITCH_STATUS_SUCCESS;
|
|
done:
|
|
cb_state->session_state->_stack_depth--;
|
|
return status;
|
|
}
|
|
|
|
switch_status_t FSSession::StreamInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
|
|
{
|
|
FSInputCallbackState *cb_state = (FSInputCallbackState *)buf;
|
|
LOCK_ISOLATE_ON_CALLBACK_MACRO();
|
|
|
|
switch_status_t status;
|
|
switch_file_handle_t *fh = (switch_file_handle_t *)cb_state->extra;
|
|
|
|
if (!switch_test_flag(fh, SWITCH_FILE_OPEN)) {
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
if ((status = CommonCallback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
if (!cb_state->ret.IsEmpty()) {
|
|
Handle<Value> tmp = Local<Value>::New(obj->GetOwner()->GetIsolate(), cb_state->ret);
|
|
String::Utf8Value str(tmp);
|
|
const char *ret = js_safe_str(*str);
|
|
|
|
if (!strncasecmp(ret, "speed", 5)) {
|
|
const 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 (!strncasecmp(ret, "volume", 6)) {
|
|
const char *p;
|
|
|
|
if ((p = strchr(ret, ':'))) {
|
|
p++;
|
|
if (*p == '+' || *p == '-') {
|
|
int step;
|
|
if (!(step = atoi(p))) {
|
|
step = 1;
|
|
}
|
|
fh->vol += step;
|
|
} else {
|
|
int vol = atoi(p);
|
|
fh->vol = vol;
|
|
}
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
if (fh->vol) {
|
|
switch_normalize_volume(fh->vol);
|
|
}
|
|
|
|
return SWITCH_STATUS_FALSE;
|
|
} else if (!strcasecmp(ret, "pause")) {
|
|
if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) {
|
|
switch_clear_flag_locked(fh, SWITCH_FILE_PAUSE);
|
|
} else {
|
|
switch_set_flag_locked(fh, SWITCH_FILE_PAUSE);
|
|
}
|
|
return SWITCH_STATUS_SUCCESS;
|
|
} else if (!strcasecmp(ret, "truncate")) {
|
|
switch_core_file_truncate(fh, 0);
|
|
} else if (!strcasecmp(ret, "restart")) {
|
|
uint32_t pos = 0;
|
|
fh->speed = 0;
|
|
switch_core_file_seek(fh, &pos, 0, SEEK_SET);
|
|
return SWITCH_STATUS_SUCCESS;
|
|
} else if (!strncasecmp(ret, "seek", 4)) {
|
|
uint32_t samps = 0;
|
|
uint32_t pos = 0;
|
|
const char *p;
|
|
|
|
if ((p = strchr(ret, ':'))) {
|
|
p++;
|
|
if (*p == '+' || *p == '-') {
|
|
int step;
|
|
if (!(step = atoi(p))) {
|
|
step = 1000;
|
|
}
|
|
if (step > 0) {
|
|
samps = step * (fh->native_rate / 1000);
|
|
switch_core_file_seek(fh, &pos, samps, SEEK_CUR);
|
|
} else {
|
|
samps = abs(step) * (fh->native_rate / 1000);
|
|
switch_core_file_seek(fh, &pos, fh->pos - samps, SEEK_SET);
|
|
}
|
|
} else {
|
|
samps = atoi(p) * (fh->native_rate / 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;
|
|
}
|
|
|
|
switch_status_t FSSession::RecordInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
|
|
{
|
|
FSInputCallbackState *cb_state = (FSInputCallbackState *)buf;
|
|
LOCK_ISOLATE_ON_CALLBACK_MACRO();
|
|
|
|
switch_status_t status;
|
|
switch_file_handle_t *fh = (switch_file_handle_t *)cb_state->extra;
|
|
|
|
if ((status = CommonCallback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
if (!cb_state->ret.IsEmpty()) {
|
|
Handle<Value> tmp = Local<Value>::New(obj->GetOwner()->GetIsolate(), cb_state->ret);
|
|
String::Utf8Value str(tmp);
|
|
const char *ret = js_safe_str(*str);
|
|
|
|
if (!strcasecmp(ret, "pause")) {
|
|
if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) {
|
|
switch_clear_flag_locked(fh, SWITCH_FILE_PAUSE);
|
|
} else {
|
|
switch_set_flag_locked(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;
|
|
}
|
|
|
|
switch_status_t FSSession::CollectInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
|
|
{
|
|
FSInputCallbackState *cb_state = (FSInputCallbackState *)buf;
|
|
LOCK_ISOLATE_ON_CALLBACK_MACRO();
|
|
|
|
const char *ret;
|
|
switch_status_t status;
|
|
|
|
if ((status = CommonCallback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) {
|
|
return status;
|
|
}
|
|
|
|
if (!cb_state->ret.IsEmpty()) {
|
|
Handle<Value> tmp = Local<Value>::New(obj->GetOwner()->GetIsolate(), cb_state->ret);
|
|
String::Utf8Value str(tmp);
|
|
ret = js_safe_str(*str);
|
|
|
|
if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) {
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return SWITCH_STATUS_BREAK;
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(FlushDigits)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
CHANNEL_MEDIA_SANITY_CHECK();
|
|
|
|
switch_channel_flush_dtmf(switch_core_session_get_channel(this->_session));
|
|
|
|
info.GetReturnValue().Set(true);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(FlushEvents)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_event_t *event;
|
|
|
|
if (!this->_session) {
|
|
info.GetReturnValue().Set(false);
|
|
return;
|
|
}
|
|
|
|
while (switch_core_session_dequeue_event(this->_session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
|
|
switch_event_destroy(&event);
|
|
}
|
|
|
|
info.GetReturnValue().Set(true);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(RecordFile)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
string file_name;
|
|
void *bp = NULL;
|
|
int len = 0;
|
|
switch_input_callback_function_t dtmf_func = NULL;
|
|
FSInputCallbackState cb_state;
|
|
switch_file_handle_t fh = { 0 };
|
|
int32_t limit = 0;
|
|
switch_input_args_t args = { 0 };
|
|
bool ret = true;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
CHANNEL_SANITY_CHECK();
|
|
|
|
CHANNEL_MEDIA_SANITY_CHECK();
|
|
|
|
if (info.Length() > 0) {
|
|
String::Utf8Value str(info[0]);
|
|
file_name = js_safe_str(*str);
|
|
if (zstr(file_name.c_str())) {
|
|
info.GetReturnValue().Set(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (info.Length() > 1) {
|
|
Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[1]);
|
|
|
|
if (!func.IsEmpty()) {
|
|
cb_state.session_state = this;
|
|
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
|
|
cb_state.function.Reset(info.GetIsolate(), func);
|
|
if (info.Length() > 2 && !info[2].IsEmpty()) {
|
|
cb_state.arg.Reset(info.GetIsolate(), info[2]);
|
|
}
|
|
|
|
dtmf_func = RecordInputCallback;
|
|
bp = &cb_state;
|
|
len = sizeof(cb_state);
|
|
}
|
|
|
|
if (info.Length() > 3) {
|
|
limit = info[3]->Int32Value();
|
|
}
|
|
|
|
if (info.Length() > 4) {
|
|
fh.thresh = info[4]->Int32Value();
|
|
}
|
|
|
|
if (info.Length() > 5) {
|
|
fh.silence_hits = info[5]->Int32Value();
|
|
}
|
|
}
|
|
|
|
cb_state.extra = &fh;
|
|
cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
|
|
args.input_callback = dtmf_func;
|
|
args.buf = bp;
|
|
args.buflen = len;
|
|
|
|
JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_record_file(this->_session, &fh, file_name.c_str(), &args, limit));
|
|
info.GetReturnValue().Set(cb_state.ret);
|
|
|
|
CheckHangupHook(this, &ret);
|
|
if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(CollectInput)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
void *bp = NULL;
|
|
int len = 0;
|
|
int32_t abs_timeout = 0;
|
|
int32_t digit_timeout = 0;
|
|
switch_input_callback_function_t dtmf_func = NULL;
|
|
FSInputCallbackState cb_state;
|
|
switch_input_args_t args = { 0 };
|
|
bool ret = true;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
CHANNEL_SANITY_CHECK();
|
|
CHANNEL_MEDIA_SANITY_CHECK();
|
|
|
|
if (info.Length() > 0) {
|
|
Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[0]);
|
|
|
|
if (!func.IsEmpty()) {
|
|
cb_state.function.Reset(info.GetIsolate(), func);
|
|
|
|
if (info.Length() > 1 && !info[1].IsEmpty()) {
|
|
cb_state.arg.Reset(info.GetIsolate(), info[1]);
|
|
}
|
|
|
|
cb_state.session_state = this;
|
|
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
|
|
dtmf_func = CollectInputCallback;
|
|
bp = &cb_state;
|
|
len = sizeof(cb_state);
|
|
}
|
|
}
|
|
|
|
if (info.Length() == 3) {
|
|
abs_timeout = info[2]->Int32Value();
|
|
} else if (info.Length() > 3) {
|
|
digit_timeout = info[2]->Int32Value();
|
|
abs_timeout = info[3]->Int32Value();
|
|
}
|
|
|
|
cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
|
|
args.input_callback = dtmf_func;
|
|
args.buf = bp;
|
|
args.buflen = len;
|
|
|
|
JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_collect_digits_callback(this->_session, &args, digit_timeout, abs_timeout));
|
|
info.GetReturnValue().Set(cb_state.ret);
|
|
|
|
CheckHangupHook(this, &ret);
|
|
if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(SayPhrase)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
string phrase_name;
|
|
string phrase_data;
|
|
string phrase_lang;
|
|
string tmp;
|
|
void *bp = NULL;
|
|
int len = 0;
|
|
switch_input_callback_function_t dtmf_func = NULL;
|
|
FSInputCallbackState cb_state;
|
|
switch_input_args_t args = { 0 };
|
|
bool ret = true;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
CHANNEL_SANITY_CHECK();
|
|
CHANNEL_MEDIA_SANITY_CHECK();
|
|
|
|
if (info.Length() > 0) {
|
|
String::Utf8Value str(info[0]);
|
|
phrase_name = js_safe_str(*str);
|
|
if (zstr(phrase_name.c_str())) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid phrase name"));
|
|
return;
|
|
}
|
|
} else {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid arguments"));
|
|
return;
|
|
}
|
|
|
|
if (info.Length() > 1) {
|
|
String::Utf8Value str(info[1]);
|
|
phrase_data = js_safe_str(*str);
|
|
}
|
|
|
|
if (info.Length() > 2) {
|
|
String::Utf8Value str(info[2]);
|
|
tmp = js_safe_str(*str);
|
|
if (!zstr(tmp.c_str())) {
|
|
phrase_lang = tmp;
|
|
}
|
|
}
|
|
|
|
if (info.Length() > 3) {
|
|
Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[3]);
|
|
|
|
if (!func.IsEmpty()) {
|
|
cb_state.function.Reset(info.GetIsolate(), func);
|
|
|
|
if (info.Length() > 4 && !info[4].IsEmpty()) {
|
|
cb_state.arg.Reset(info.GetIsolate(), info[4]);
|
|
}
|
|
|
|
cb_state.session_state = this;
|
|
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
|
|
dtmf_func = CollectInputCallback;
|
|
bp = &cb_state;
|
|
len = sizeof(cb_state);
|
|
}
|
|
}
|
|
|
|
cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
|
|
args.input_callback = dtmf_func;
|
|
args.buf = bp;
|
|
args.buflen = len;
|
|
|
|
JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_phrase_macro(this->_session, phrase_name.c_str(), phrase_data.c_str(), phrase_lang.c_str(), &args));
|
|
info.GetReturnValue().Set(cb_state.ret);
|
|
|
|
CheckHangupHook(this, &ret);
|
|
if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
}
|
|
|
|
bool FSSession::CheckHangupHook(FSSession *obj, bool *ret)
|
|
{
|
|
if (!obj) {
|
|
return true;
|
|
}
|
|
|
|
Isolate *isolate = obj->GetIsolate();
|
|
HandleScope handle_scope(isolate);
|
|
Handle<Value> argv[2];
|
|
int argc = 0;
|
|
bool res = true;
|
|
string resp;
|
|
|
|
if (!obj->_check_state && !obj->_on_hangup.IsEmpty() && (obj->_hook_state == CS_HANGUP || obj->_hook_state == CS_ROUTING)) {
|
|
obj->_check_state++;
|
|
argv[argc++] = Local<Object>::New(obj->GetOwner()->GetIsolate(), obj->GetJavaScriptObject());
|
|
|
|
if (obj->_hook_state == CS_HANGUP) {
|
|
argv[argc++] = String::NewFromUtf8(isolate, "hangup");
|
|
} else {
|
|
argv[argc++] = String::NewFromUtf8(isolate, "transfer");
|
|
}
|
|
|
|
// Run the hangup hook
|
|
Handle<Function> func = Local<Function>::New(isolate, obj->_on_hangup);
|
|
|
|
if (!func.IsEmpty() && func->IsFunction()) {
|
|
Handle<Value> res = func->Call(isolate->GetCurrentContext()->Global(), argc, argv);
|
|
|
|
if (!res.IsEmpty()) {
|
|
String::Utf8Value str(res);
|
|
resp = js_safe_str(*str);
|
|
}
|
|
}
|
|
|
|
if (resp.length() > 0) {
|
|
res = !strcasecmp(resp.c_str(), "exit") ? false : true;
|
|
}
|
|
}
|
|
|
|
if (ret) {
|
|
*ret = res;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
switch_status_t FSSession::HangupHook(switch_core_session_t *session)
|
|
{
|
|
switch_channel_t *channel = switch_core_session_get_channel(session);
|
|
switch_channel_state_t state = switch_channel_get_state(channel);
|
|
FSSession *obj = NULL;
|
|
|
|
if (state == CS_HANGUP || state == CS_ROUTING) {
|
|
if ((obj = (FSSession *)switch_channel_get_private(channel, "jsobject"))) {
|
|
obj->_hook_state = state;
|
|
obj->_check_state = 0;
|
|
}
|
|
}
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(SetHangupHook)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
info.GetReturnValue().Set(false);
|
|
|
|
if (this->_session) {
|
|
switch_channel_t *channel = switch_core_session_get_channel(this->_session);
|
|
|
|
/* Reset hangup hook first */
|
|
if (!this->_on_hangup.IsEmpty()) {
|
|
switch_channel_set_private(channel, "jsobject", NULL);
|
|
switch_core_event_hook_remove_state_change(this->_session, HangupHook);
|
|
this->_on_hangup.Reset();
|
|
this->_hook_state = switch_channel_get_state(channel);
|
|
}
|
|
|
|
if (info.Length() > 0) {
|
|
Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[0]);
|
|
|
|
if (!func.IsEmpty()) {
|
|
this->_on_hangup.Reset(info.GetIsolate(), func);
|
|
this->_hook_state = switch_channel_get_state(channel);
|
|
switch_channel_set_private(channel, "jsobject", this);
|
|
switch_core_event_hook_add_state_change(this->_session, HangupHook);
|
|
info.GetReturnValue().Set(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(StreamFile)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
string file_name;
|
|
void *bp = NULL;
|
|
int len = 0;
|
|
switch_input_callback_function_t dtmf_func = NULL;
|
|
FSInputCallbackState cb_state;
|
|
switch_file_handle_t fh = { 0 };
|
|
switch_input_args_t args = { 0 };
|
|
const char *prebuf;
|
|
char posbuf[35] = "";
|
|
bool ret = true;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
CHANNEL_SANITY_CHECK();
|
|
CHANNEL_MEDIA_SANITY_CHECK();
|
|
|
|
if (info.Length() > 0) {
|
|
String::Utf8Value str(info[0]);
|
|
file_name = js_safe_str(*str);
|
|
if (zstr(file_name.c_str())) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid filename"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (info.Length() > 1) {
|
|
Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[1]);
|
|
|
|
if (!func.IsEmpty()) {
|
|
cb_state.function.Reset(info.GetIsolate(), func);
|
|
|
|
if (info.Length() > 2 && !info[2].IsEmpty()) {
|
|
cb_state.arg.Reset(info.GetIsolate(), info[2]);
|
|
}
|
|
|
|
cb_state.session_state = this;
|
|
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
|
|
dtmf_func = StreamInputCallback;
|
|
bp = &cb_state;
|
|
len = sizeof(cb_state);
|
|
}
|
|
}
|
|
|
|
if (info.Length() > 3) {
|
|
fh.samples = info[3]->Int32Value();
|
|
}
|
|
|
|
if ((prebuf = switch_channel_get_variable(channel, "stream_prebuffer"))) {
|
|
int maybe = atoi(prebuf);
|
|
if (maybe > 0) {
|
|
fh.prebuf = maybe;
|
|
}
|
|
}
|
|
|
|
cb_state.extra = &fh;
|
|
cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
|
|
args.input_callback = dtmf_func;
|
|
args.buf = bp;
|
|
args.buflen = len;
|
|
JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_play_file(this->_session, &fh, file_name.c_str(), &args));
|
|
info.GetReturnValue().Set(cb_state.ret);
|
|
|
|
switch_snprintf(posbuf, sizeof(posbuf), "%u", fh.offset_pos);
|
|
switch_channel_set_variable(channel, "last_file_position", posbuf);
|
|
|
|
CheckHangupHook(this, &ret);
|
|
if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(Sleep)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
void *bp = NULL;
|
|
int len = 0;
|
|
switch_input_callback_function_t dtmf_func = NULL;
|
|
FSInputCallbackState cb_state;
|
|
switch_input_args_t args = { 0 };
|
|
int32_t ms = 0;
|
|
bool ret = true;
|
|
int32_t sync = 0;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
CHANNEL_SANITY_CHECK();
|
|
CHANNEL_MEDIA_SANITY_CHECK();
|
|
|
|
if (info.Length() > 0) {
|
|
ms = info[0]->Int32Value();
|
|
}
|
|
|
|
if (ms <= 0) {
|
|
return;
|
|
}
|
|
|
|
if (info.Length() > 1) {
|
|
Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[1]);
|
|
|
|
if (!func.IsEmpty()) {
|
|
cb_state.function.Reset(info.GetIsolate(), func);
|
|
|
|
if (info.Length() > 2 && !info[2].IsEmpty()) {
|
|
cb_state.arg.Reset(info.GetIsolate(), info[2]);
|
|
}
|
|
|
|
cb_state.session_state = this;
|
|
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
|
|
dtmf_func = CollectInputCallback;
|
|
bp = &cb_state;
|
|
len = sizeof(cb_state);
|
|
}
|
|
}
|
|
|
|
if (info.Length() > 2) {
|
|
sync = info[2]->Int32Value();
|
|
}
|
|
|
|
cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
|
|
args.input_callback = dtmf_func;
|
|
args.buf = bp;
|
|
args.buflen = len;
|
|
JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_sleep(this->_session, ms, (switch_bool_t)sync, &args));
|
|
info.GetReturnValue().Set(cb_state.ret);
|
|
|
|
CheckHangupHook(this, &ret);
|
|
if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(SetVariable)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
|
|
if (info.Length() > 1) {
|
|
const char *var, *val;
|
|
String::Utf8Value str1(info[0]);
|
|
String::Utf8Value str2(info[1]);
|
|
|
|
var = js_safe_str(*str1);
|
|
val = *str2;
|
|
switch_channel_set_variable_var_check(channel, var, val, SWITCH_FALSE);
|
|
info.GetReturnValue().Set(true);
|
|
} else {
|
|
info.GetReturnValue().Set(false);
|
|
}
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(GetVariable)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
|
|
if (info.Length() > 0) {
|
|
const char *var, *val;
|
|
String::Utf8Value str(info[0]);
|
|
|
|
var = js_safe_str(*str);
|
|
val = switch_channel_get_variable(channel, var);
|
|
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), js_safe_str(val)));
|
|
} else {
|
|
info.GetReturnValue().Set(false);
|
|
}
|
|
}
|
|
|
|
switch_status_t FSSession::InitSpeechEngine(const char *engine, const char *voice)
|
|
{
|
|
switch_codec_t *read_codec;
|
|
switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
|
|
uint32_t rate = 0;
|
|
int interval = 0;
|
|
|
|
read_codec = switch_core_session_get_read_codec(this->_session);
|
|
rate = read_codec->implementation->actual_samples_per_second;
|
|
interval = read_codec->implementation->microseconds_per_packet / 1000;
|
|
|
|
if (switch_core_codec_init(&this->_speech->codec,
|
|
"L16",
|
|
NULL,
|
|
NULL,
|
|
rate,
|
|
interval,
|
|
1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,
|
|
switch_core_session_get_pool(this->_session)) == SWITCH_STATUS_SUCCESS) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Success L16@%uhz 1 channel %dms\n", rate, interval);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Failed L16@%uhz 1 channel %dms\n", rate, interval);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
if (switch_core_speech_open(&this->_speech->sh, engine, voice, rate, interval, read_codec->implementation->number_of_channels,
|
|
&flags, switch_core_session_get_pool(this->_session)) != SWITCH_STATUS_SUCCESS) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid TTS module!\n");
|
|
switch_core_codec_destroy(&this->_speech->codec);
|
|
return SWITCH_STATUS_FALSE;
|
|
}
|
|
|
|
return SWITCH_STATUS_SUCCESS;
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(Speak)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
const char *tts_name = NULL;
|
|
const char *voice_name = NULL;
|
|
const char *text = NULL;
|
|
void *bp = NULL;
|
|
int len = 0;
|
|
FSInputCallbackState cb_state;
|
|
switch_input_callback_function_t dtmf_func = NULL;
|
|
switch_input_args_t args = { 0 };
|
|
bool ret = true;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
info.GetReturnValue().Set(false);
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
CHANNEL_SANITY_CHECK();
|
|
CHANNEL_MEDIA_SANITY_CHECK();
|
|
|
|
if (info.Length() < 3) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid arguments"));
|
|
return;
|
|
}
|
|
|
|
String::Utf8Value str1(info[0]);
|
|
String::Utf8Value str2(info[1]);
|
|
String::Utf8Value str3(info[2]);
|
|
tts_name = js_safe_str(*str1);
|
|
voice_name = js_safe_str(*str2);
|
|
text = js_safe_str(*str3);
|
|
|
|
if (zstr(tts_name)) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid TTS Name"));
|
|
return;
|
|
}
|
|
|
|
if (zstr(text)) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid Text"));
|
|
return;
|
|
}
|
|
|
|
if (this->_speech && this->_speech->speaking) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Recursive call not allowed"));
|
|
return;
|
|
}
|
|
|
|
|
|
if (this->_speech && strcasecmp(this->_speech->sh.name, tts_name)) {
|
|
DestroySpeechEngine();
|
|
}
|
|
|
|
if (this->_speech) {
|
|
const char *s = "voice";
|
|
switch_core_speech_text_param_tts(&this->_speech->sh, (char *)s, voice_name);
|
|
} else {
|
|
this->_speech = (js_session_speech_t *)switch_core_session_alloc(this->_session, sizeof(*this->_speech));
|
|
switch_assert(this->_speech != NULL);
|
|
if (InitSpeechEngine(tts_name, voice_name) != SWITCH_STATUS_SUCCESS) {
|
|
this->_speech = NULL;
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Cannot allocate speech engine!"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (info.Length() > 3) {
|
|
Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[3]);
|
|
|
|
if (!func.IsEmpty()) {
|
|
cb_state.function.Reset(info.GetIsolate(), func);
|
|
|
|
if (info.Length() > 4 && !info[4].IsEmpty()) {
|
|
cb_state.arg.Reset(info.GetIsolate(), info[4]);
|
|
}
|
|
|
|
cb_state.session_state = this;
|
|
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
|
|
dtmf_func = CollectInputCallback;
|
|
bp = &cb_state;
|
|
len = sizeof(cb_state);
|
|
}
|
|
}
|
|
|
|
cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
|
|
args.input_callback = dtmf_func;
|
|
args.buf = bp;
|
|
args.buflen = len;
|
|
|
|
switch_core_speech_flush_tts(&this->_speech->sh);
|
|
if (switch_core_codec_ready(&this->_speech->codec)) {
|
|
this->_speech->speaking = 1;
|
|
JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_speak_text_handle(this->_session, &this->_speech->sh, &this->_speech->codec, NULL, (char *)text, &args));
|
|
this->_speech->speaking = 0;
|
|
}
|
|
|
|
info.GetReturnValue().Set(cb_state.ret);
|
|
|
|
CheckHangupHook(this, &ret);
|
|
if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(GetDigits)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
string terminators;
|
|
char buf[513] = { 0 };
|
|
int32_t digits = 0, timeout = 5000, digit_timeout = 0, abs_timeout = 0;
|
|
switch_channel_t *channel;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
CHANNEL_SANITY_CHECK();
|
|
|
|
if (info.Length() > 0) {
|
|
char term;
|
|
digits = info[0]->Int32Value();
|
|
|
|
if (digits > sizeof(buf) - 1) {
|
|
char *err = switch_mprintf("Exceeded max digits of %" SWITCH_SIZE_T_FMT, sizeof(buf) - 1);
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), err));
|
|
free(err);
|
|
return;
|
|
}
|
|
|
|
if (info.Length() > 1) {
|
|
String::Utf8Value str(info[1]);
|
|
terminators = js_safe_str(*str);
|
|
}
|
|
|
|
if (info.Length() > 2) {
|
|
timeout = info[2]->Int32Value();
|
|
}
|
|
|
|
if (info.Length() > 3) {
|
|
digit_timeout = info[3]->Int32Value();
|
|
}
|
|
|
|
if (info.Length() > 4) {
|
|
abs_timeout = info[4]->Int32Value();
|
|
}
|
|
|
|
switch_ivr_collect_digits_count(this->_session, buf, sizeof(buf), digits, terminators.c_str(), &term, timeout, digit_timeout, abs_timeout);
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), buf));
|
|
}
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(SetAutoHangup)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
info.GetReturnValue().Set(false);
|
|
|
|
METHOD_SANITY_CHECK();
|
|
|
|
if (info.Length() > 0) {
|
|
bool tf = info[0]->BooleanValue();
|
|
if (tf) {
|
|
switch_set_flag(this, S_HUP);
|
|
} else {
|
|
switch_clear_flag(this, S_HUP);
|
|
}
|
|
info.GetReturnValue().Set(tf);
|
|
}
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(Answer)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
CHANNEL_SANITY_CHECK_ANSWER();
|
|
|
|
switch_channel_answer(channel);
|
|
|
|
info.GetReturnValue().Set(true);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(PreAnswer)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
CHANNEL_SANITY_CHECK_ANSWER();
|
|
|
|
switch_channel_pre_answer(channel);
|
|
|
|
info.GetReturnValue().Set(true);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(GenerateXmlCdr)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_xml_t cdr = NULL;
|
|
|
|
info.GetReturnValue().Set(false);
|
|
|
|
if (switch_ivr_generate_xml_cdr(this->_session, &cdr) == SWITCH_STATUS_SUCCESS) {
|
|
char *xml_text;
|
|
if ((xml_text = switch_xml_toxml(cdr, SWITCH_FALSE))) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), xml_text));
|
|
}
|
|
switch_safe_free(xml_text);
|
|
switch_xml_free(cdr);
|
|
}
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(Ready)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
|
|
info.GetReturnValue().Set((this->_session && switch_channel_ready(switch_core_session_get_channel(this->_session))) ? true : false);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(MediaReady)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
|
|
info.GetReturnValue().Set((this->_session && switch_channel_media_ready(switch_core_session_get_channel(this->_session))) ? true : false);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(RingReady)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
|
|
info.GetReturnValue().Set((this->_session && switch_channel_test_flag(switch_core_session_get_channel(this->_session), CF_RING_READY)) ? true : false);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(Answered)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
|
|
info.GetReturnValue().Set((this->_session && switch_channel_test_flag(switch_core_session_get_channel(this->_session), CF_ANSWERED)) ? true : false);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(WaitForMedia)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
switch_time_t started;
|
|
unsigned int elapsed;
|
|
int32_t timeout = 60000;
|
|
bool ret = true;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
CHANNEL_MEDIA_SANITY_CHECK();
|
|
|
|
started = switch_micro_time_now();
|
|
|
|
if (info.Length() > 0) {
|
|
timeout = info[0]->Int32Value();
|
|
if (timeout < 1000) {
|
|
timeout = 1000;
|
|
}
|
|
}
|
|
|
|
if (!CheckHangupHook(this, NULL)) {
|
|
JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
return;
|
|
}
|
|
|
|
for (;;) {
|
|
if (((elapsed = (unsigned int) ((switch_micro_time_now() - started) / 1000)) > (switch_time_t) timeout)
|
|
|| switch_channel_down(channel)) {
|
|
info.GetReturnValue().Set(false);
|
|
break;
|
|
}
|
|
|
|
if (switch_channel_ready(channel)
|
|
&& (switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag(channel, CF_EARLY_MEDIA))) {
|
|
info.GetReturnValue().Set(true);
|
|
break;
|
|
}
|
|
|
|
switch_cond_next();
|
|
}
|
|
|
|
CheckHangupHook(this, &ret);
|
|
if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(WaitForAnswer)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
switch_time_t started;
|
|
unsigned int elapsed;
|
|
int32_t timeout = 60000;
|
|
bool ret = true;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
|
|
started = switch_micro_time_now();
|
|
|
|
if (info.Length() > 0) {
|
|
timeout = info[0]->Int32Value();
|
|
if (timeout < 1000) {
|
|
timeout = 1000;
|
|
}
|
|
}
|
|
|
|
if (!CheckHangupHook(this, NULL)) {
|
|
JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
return;
|
|
}
|
|
|
|
for (;;) {
|
|
if (((elapsed = (unsigned int) ((switch_micro_time_now() - started) / 1000)) > (switch_time_t) timeout)
|
|
|| switch_channel_down(channel)) {
|
|
info.GetReturnValue().Set(false);
|
|
break;
|
|
}
|
|
|
|
if (switch_channel_ready(channel) && switch_channel_test_flag(channel, CF_ANSWERED)) {
|
|
info.GetReturnValue().Set(true);
|
|
break;
|
|
}
|
|
|
|
switch_cond_next();
|
|
}
|
|
|
|
CheckHangupHook(this, &ret);
|
|
if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(Detach)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_call_cause_t cause = SWITCH_CAUSE_NONE;
|
|
switch_channel_t *channel;
|
|
switch_core_session_t *session;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
|
|
if ((session = this->_session)) {
|
|
this->_session = NULL;
|
|
|
|
if (info.Length() > 0) {
|
|
if (info[0]->IsInt32()) {
|
|
int32_t i = 0;
|
|
i = info[0]->Int32Value();
|
|
cause = (switch_call_cause_t)i;
|
|
} else {
|
|
String::Utf8Value str(info[0]);
|
|
const char *cause_name = js_safe_str(*str);
|
|
cause = switch_channel_str2cause(cause_name);
|
|
}
|
|
}
|
|
|
|
if (cause != SWITCH_CAUSE_NONE) {
|
|
channel = switch_core_session_get_channel(session);
|
|
switch_channel_hangup(channel, cause);
|
|
}
|
|
|
|
switch_core_session_rwunlock(session);
|
|
info.GetReturnValue().Set(true);
|
|
} else {
|
|
info.GetReturnValue().Set(false);
|
|
}
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(Execute)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
bool retval = false;
|
|
bool ret = true;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
|
|
/* you can execute some apps before you answer CHANNEL_SANITY_CHECK(); */
|
|
|
|
if (info.Length() > 0) {
|
|
switch_application_interface_t *application_interface;
|
|
String::Utf8Value str(info[0]);
|
|
const char *app_name = js_safe_str(*str);
|
|
string app_arg;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
|
|
if (info.Length() > 1) {
|
|
String::Utf8Value str2(info[1]);
|
|
app_arg = js_safe_str(*str2);
|
|
}
|
|
|
|
if ((application_interface = switch_loadable_module_get_application_interface(app_name))) {
|
|
if (application_interface->application_function) {
|
|
if (!CheckHangupHook(this, NULL)) {
|
|
JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
UNPROTECT_INTERFACE(application_interface);
|
|
return;
|
|
}
|
|
|
|
switch_core_session_exec(this->_session, application_interface, app_arg.c_str());
|
|
CheckHangupHook(this, &ret);
|
|
retval = true;
|
|
}
|
|
UNPROTECT_INTERFACE(application_interface);
|
|
}
|
|
}
|
|
|
|
info.GetReturnValue().Set(retval);
|
|
if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(GetEvent)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_event_t *event;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
|
|
if (switch_core_session_dequeue_event(this->_session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
|
|
Handle<Object> Event;
|
|
FSEvent *evt;
|
|
|
|
if ((evt = new FSEvent(info))) {
|
|
evt->SetEvent(event, 0);
|
|
evt->RegisterInstance(info.GetIsolate(), "", true);
|
|
Event = evt->GetJavaScriptObject();
|
|
|
|
if (!Event.IsEmpty()) {
|
|
info.GetReturnValue().Set(Event);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
info.GetReturnValue().Set(false);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(SendEvent)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
Handle<Object> Event;
|
|
FSEvent *eo;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
|
|
if (info.Length() > 0 && info[0]->IsObject()) {
|
|
Handle<Object> jso = Handle<Object>::Cast(info[0]);
|
|
|
|
if (!jso.IsEmpty()) {
|
|
switch_event_t **evt;
|
|
|
|
if ((eo = JSBase::GetInstance<FSEvent>(jso)) && (evt = eo->GetEvent())) {
|
|
if (switch_core_session_receive_event(this->_session, evt) != SWITCH_STATUS_SUCCESS) {
|
|
info.GetReturnValue().Set(false);
|
|
return;
|
|
}
|
|
|
|
delete eo;
|
|
}
|
|
}
|
|
}
|
|
|
|
info.GetReturnValue().Set(true);
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(Hangup)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel;
|
|
switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING;
|
|
|
|
METHOD_SANITY_CHECK();
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
|
|
if (switch_channel_up(channel)) {
|
|
if (info.Length() > 0) {
|
|
if (info[0]->IsInt32()) {
|
|
int32_t i = info[0]->Int32Value();
|
|
cause = (switch_call_cause_t)i;
|
|
} else {
|
|
String::Utf8Value str(info[0]);
|
|
const char *cause_name = js_safe_str(*str);
|
|
cause = switch_channel_str2cause(cause_name);
|
|
}
|
|
}
|
|
|
|
switch_channel_hangup(channel, cause);
|
|
switch_core_session_kill_channel(this->_session, SWITCH_SIG_KILL);
|
|
|
|
this->_hook_state = CS_HANGUP;
|
|
CheckHangupHook(this, NULL);
|
|
|
|
info.GetReturnValue().Set(true);
|
|
} else {
|
|
info.GetReturnValue().Set(false);
|
|
}
|
|
}
|
|
|
|
JS_SESSION_GET_PROPERTY_IMPL(GetProperty)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_channel_t *channel = NULL;
|
|
switch_caller_profile_t *caller_profile = NULL;
|
|
|
|
if (this->_session) {
|
|
channel = switch_core_session_get_channel(this->_session);
|
|
caller_profile = switch_channel_get_caller_profile(channel);
|
|
}
|
|
|
|
String::Utf8Value str(property);
|
|
const char *prop = js_safe_str(*str);
|
|
|
|
if (!strcmp(prop, "cause")) {
|
|
if (channel) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_channel_cause2str(switch_channel_get_cause(channel))));
|
|
} else {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_channel_cause2str(this->_cause)));
|
|
}
|
|
} else if (!strcmp(prop, "causecode")) {
|
|
if (channel) {
|
|
info.GetReturnValue().Set(Integer::New(info.GetIsolate(), switch_channel_get_cause(channel)));
|
|
} else {
|
|
info.GetReturnValue().Set(Integer::New(info.GetIsolate(), this->_cause));
|
|
}
|
|
} else if (!strcmp(prop, "name")) {
|
|
if (channel) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_channel_get_name(channel)));
|
|
} else {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
|
|
}
|
|
} else if (!strcmp(prop, "uuid")) {
|
|
if (channel) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_channel_get_uuid(channel)));
|
|
} else {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
|
|
}
|
|
} else if (!strcmp(prop, "state")) {
|
|
if (channel) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_channel_state_name(switch_channel_get_state(channel))));
|
|
} else {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
|
|
}
|
|
} else if (!strcmp(prop, "dialplan")) {
|
|
if (caller_profile) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->dialplan));
|
|
} else {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
|
|
}
|
|
} else if (!strcmp(prop, "caller_id_name")) {
|
|
if (caller_profile) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->caller_id_name));
|
|
} else {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
|
|
}
|
|
} else if (!strcmp(prop, "caller_id_num") || !strcmp(prop, "caller_id_number")) {
|
|
if (caller_profile) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->caller_id_number));
|
|
} else {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
|
|
}
|
|
} else if (!strcmp(prop, "network_addr") || !strcasecmp(prop, "network_address")) {
|
|
if (caller_profile) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->network_addr));
|
|
} else {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
|
|
}
|
|
} else if (!strcmp(prop, "ani")) {
|
|
if (caller_profile) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->ani));
|
|
} else {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
|
|
}
|
|
} else if (!strcmp(prop, "aniii")) {
|
|
if (caller_profile) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->aniii));
|
|
} else {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
|
|
}
|
|
} else if (!strcmp(prop, "destination")) {
|
|
if (caller_profile) {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->destination_number));
|
|
} else {
|
|
info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
|
|
}
|
|
} else {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Bad property"));
|
|
}
|
|
}
|
|
|
|
void *FSSession::Construct(const v8::FunctionCallbackInfo<Value>& info)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
FSSession *session_obj = new FSSession(info);
|
|
|
|
switch_assert(session_obj);
|
|
|
|
if (info.Length() > 0) {
|
|
String::Utf8Value str(info[0]);
|
|
const char *uuid = js_safe_str(*str);
|
|
|
|
if (!strchr(uuid, '/')) {
|
|
session_obj->_session = switch_core_session_locate(uuid);
|
|
switch_set_flag(session_obj, S_HUP);
|
|
} else {
|
|
FSSession *old_obj = NULL;
|
|
|
|
if (info.Length() > 1 && info[1]->IsObject()) {
|
|
old_obj = JSBase::GetInstance<FSSession>(Handle<Object>::Cast(info[1]));
|
|
}
|
|
if (switch_ivr_originate(old_obj ? old_obj->_session : NULL,
|
|
&session_obj->_session, &session_obj->_cause, uuid, 60,
|
|
NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL) == SWITCH_STATUS_SUCCESS) {
|
|
switch_set_flag(session_obj, S_HUP);
|
|
} else {
|
|
/* This will return the Session object, but with no C++ instance related to it */
|
|
/* After each call to [new Session("/chan/test")] you should check the property originateCause, which will hold a value if origination failed */
|
|
info.This()->Set(String::NewFromUtf8(info.GetIsolate(), "originateCause"), String::NewFromUtf8(info.GetIsolate(), switch_channel_cause2str(session_obj->_cause)));
|
|
delete session_obj;
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return session_obj;
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(SetCallerdata)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
info.GetReturnValue().Set(false);
|
|
|
|
if (info.Length() > 1) {
|
|
const char *var, *val;
|
|
char **toset = NULL;
|
|
String::Utf8Value str1(info[0]);
|
|
String::Utf8Value str2(info[1]);
|
|
var = js_safe_str(*str1);
|
|
val = js_safe_str(*str2);
|
|
|
|
if (!strcasecmp(var, "dialplan")) {
|
|
toset = &this->_dialplan;
|
|
} else if (!strcasecmp(var, "username")) {
|
|
toset = &this->_username;
|
|
} else if (!strcasecmp(var, "caller_id_name")) {
|
|
toset = &this->_caller_id_name;
|
|
} else if (!strcasecmp(var, "ani")) {
|
|
toset = &this->_ani;
|
|
} else if (!strcasecmp(var, "aniii")) {
|
|
toset = &this->_aniii;
|
|
} else if (!strcasecmp(var, "caller_id_number")) {
|
|
toset = &this->_caller_id_number;
|
|
} else if (!strcasecmp(var, "network_addr")) {
|
|
toset = &this->_network_addr;
|
|
} else if (!strcasecmp(var, "rdnis")) {
|
|
toset = &this->_rdnis;
|
|
} else if (!strcasecmp(var, "destination_number")) {
|
|
toset = &this->_destination_number;
|
|
} else if (!strcasecmp(var, "context")) {
|
|
toset = &this->_context;
|
|
}
|
|
|
|
if (toset) {
|
|
switch_safe_free(*toset);
|
|
*toset = strdup(val);
|
|
info.GetReturnValue().Set(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(Originate)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
switch_memory_pool_t *pool = NULL;
|
|
|
|
this->_cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
|
|
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "This method is deprecated, please use new Session(\"<dial string>\", a_leg) \n");
|
|
|
|
if (this->_session) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "cannot call this method on an initialized session"));
|
|
return;
|
|
}
|
|
|
|
if (info.Length() > 1) {
|
|
Handle<Object> session_obj;
|
|
switch_core_session_t *session = NULL, *peer_session = NULL;
|
|
switch_caller_profile_t *caller_profile = NULL, *orig_caller_profile = NULL;
|
|
string dest;
|
|
const char *dialplan = NULL;
|
|
const char *cid_name = "";
|
|
const char *cid_num = "";
|
|
const char *network_addr = "";
|
|
const char *ani = "";
|
|
const char *aniii = "";
|
|
const char *rdnis = "";
|
|
const char *context = "";
|
|
const char *username = NULL;
|
|
string to;
|
|
char *tmp;
|
|
switch_status_t status;
|
|
|
|
info.GetReturnValue().Set(false);
|
|
|
|
if (info[0]->IsObject()) {
|
|
session_obj = Handle<Object>::Cast(info[0]);
|
|
FSSession *old_obj = NULL;
|
|
if (!session_obj.IsEmpty() && (old_obj = JSBase::GetInstance<FSSession>(session_obj))) {
|
|
|
|
if (old_obj == this) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Supplied a_leg session is the same as our session"));
|
|
return;
|
|
}
|
|
|
|
if (!old_obj->_session) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Supplied a_leg session is not initilaized!"));
|
|
return;
|
|
}
|
|
|
|
session = old_obj->_session;
|
|
orig_caller_profile = switch_channel_get_caller_profile(switch_core_session_get_channel(session));
|
|
dialplan = orig_caller_profile->dialplan;
|
|
cid_name = orig_caller_profile->caller_id_name;
|
|
cid_num = orig_caller_profile->caller_id_number;
|
|
ani = orig_caller_profile->ani;
|
|
aniii = orig_caller_profile->aniii;
|
|
rdnis = orig_caller_profile->rdnis;
|
|
context = orig_caller_profile->context;
|
|
username = orig_caller_profile->username;
|
|
}
|
|
}
|
|
|
|
if (!zstr(this->_dialplan) && zstr(dialplan))
|
|
dialplan = this->_dialplan;
|
|
if (!zstr(this->_caller_id_name) && zstr(cid_name))
|
|
cid_name = this->_caller_id_name;
|
|
if (!zstr(this->_caller_id_number) && zstr(cid_num))
|
|
cid_num = this->_caller_id_number;
|
|
if (!zstr(this->_ani) && zstr(ani))
|
|
ani = this->_ani;
|
|
if (!zstr(this->_aniii) && zstr(aniii))
|
|
aniii = this->_aniii;
|
|
if (!zstr(this->_rdnis) && zstr(rdnis))
|
|
rdnis = this->_rdnis;
|
|
if (!zstr(this->_context) && zstr(context))
|
|
context = this->_context;
|
|
if (!zstr(this->_username) && zstr(username))
|
|
username = this->_username;
|
|
|
|
String::Utf8Value str(info[1]);
|
|
dest = js_safe_str(*str);
|
|
|
|
if (!dest.c_str() || !strchr(dest.c_str(), '/')) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Channel String\n");
|
|
return;
|
|
}
|
|
|
|
if (info.Length() > 2) {
|
|
String::Utf8Value strTmp(info[2]);
|
|
tmp = *strTmp;
|
|
if (!zstr(tmp)) {
|
|
to = tmp;
|
|
}
|
|
}
|
|
|
|
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n");
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Could not create new pool"));
|
|
return;
|
|
}
|
|
|
|
caller_profile = switch_caller_profile_new(pool, username, dialplan, cid_name, cid_num, network_addr, ani, aniii, rdnis, "mod_v8", context, dest.c_str());
|
|
|
|
status =
|
|
switch_ivr_originate(session, &peer_session, &this->_cause,
|
|
dest.c_str(), to.length() > 0 ? atoi(to.c_str()) : 60, NULL, NULL, NULL, caller_profile, NULL, SOF_NONE, NULL, NULL);
|
|
|
|
if (status != SWITCH_STATUS_SUCCESS) {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot Create Outgoing Channel! [%s]\n", dest.c_str());
|
|
return;
|
|
}
|
|
|
|
this->_session = peer_session;
|
|
switch_set_flag(this, S_HUP);
|
|
info.GetReturnValue().Set(true);
|
|
} else {
|
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing Args\n");
|
|
}
|
|
}
|
|
|
|
JS_SESSION_FUNCTION_IMPL(Bridge)
|
|
{
|
|
HandleScope handle_scope(info.GetIsolate());
|
|
FSSession *jss_b = NULL;
|
|
Handle<Object> obj_b;
|
|
void *bp = NULL;
|
|
switch_input_callback_function_t dtmf_func = NULL;
|
|
FSInputCallbackState cb_state;
|
|
|
|
info.GetReturnValue().Set(false);
|
|
|
|
if (info.Length() > 0) {
|
|
if (info[0]->IsObject()) {
|
|
obj_b = Handle<Object>::Cast(info[0]);
|
|
|
|
if (!(jss_b = JSBase::GetInstance<FSSession>(obj_b))) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Cannot find session B"));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!_session) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "session A is not ready!"));
|
|
return;
|
|
}
|
|
|
|
if (!(jss_b && jss_b->_session)) {
|
|
info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "session B is not ready!"));
|
|
return;
|
|
}
|
|
|
|
if (info.Length() > 1) {
|
|
Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[1]);
|
|
|
|
if (!func.IsEmpty()) {
|
|
cb_state.function.Reset(info.GetIsolate(), func);
|
|
|
|
if (info.Length() > 2 && !info[2].IsEmpty()) {
|
|
cb_state.arg.Reset(info.GetIsolate(), info[2]);
|
|
}
|
|
|
|
cb_state.jss_a = this;
|
|
cb_state.jss_b = jss_b;
|
|
cb_state.session_obj_a.Reset(info.GetIsolate(), info.Holder());
|
|
cb_state.session_obj_b.Reset(info.GetIsolate(), obj_b);
|
|
cb_state.session_state = this;
|
|
cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
|
|
|
|
dtmf_func = CollectInputCallback;
|
|
bp = &cb_state;
|
|
}
|
|
}
|
|
|
|
JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_multi_threaded_bridge(_session, jss_b->_session, dtmf_func, bp, bp));
|
|
|
|
info.GetReturnValue().Set(true);
|
|
}
|
|
|
|
static const js_function_t session_proc[] = {
|
|
{"originate", FSSession::Originate}, // Deprecated - use constructor instead
|
|
{"setCallerData", FSSession::SetCallerdata},
|
|
{"setHangupHook", FSSession::SetHangupHook},
|
|
{"setAutoHangup", FSSession::SetAutoHangup},
|
|
{"sayPhrase", FSSession::SayPhrase},
|
|
{"streamFile", FSSession::StreamFile},
|
|
{"collectInput", FSSession::CollectInput},
|
|
{"recordFile", FSSession::RecordFile},
|
|
{"flushEvents", FSSession::FlushEvents},
|
|
{"flushDigits", FSSession::FlushDigits},
|
|
{"speak", FSSession::Speak},
|
|
{"setVariable", FSSession::SetVariable},
|
|
{"getVariable", FSSession::GetVariable},
|
|
{"getDigits", FSSession::GetDigits},
|
|
{"answer", FSSession::Answer},
|
|
{"preAnswer", FSSession::PreAnswer},
|
|
{"generateXmlCdr", FSSession::GenerateXmlCdr},
|
|
{"ready", FSSession::Ready},
|
|
{"answered", FSSession::Answered},
|
|
{"mediaReady", FSSession::MediaReady},
|
|
{"ringReady", FSSession::RingReady},
|
|
{"waitForAnswer", FSSession::WaitForAnswer}, // Deprecated
|
|
{"waitForMedia", FSSession::WaitForMedia}, // Deprecated
|
|
{"getEvent", FSSession::GetEvent},
|
|
{"sendEvent", FSSession::SendEvent},
|
|
{"hangup", FSSession::Hangup},
|
|
{"execute", FSSession::Execute},
|
|
{"destroy", FSSession::Detach},
|
|
{"sleep", FSSession::Sleep},
|
|
{"bridge", FSSession::Bridge},
|
|
{0}
|
|
};
|
|
|
|
static const js_property_t session_prop[] = {
|
|
{"name", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"state", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"dialplan", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"caller_id_name", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"caller_id_num", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"caller_id_number", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"network_addr", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"network_address", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"ani", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"aniii", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"destination", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"uuid", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"cause", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{"causecode", FSSession::GetProperty, JSBase::DefaultSetProperty},
|
|
{0}
|
|
};
|
|
|
|
static const js_class_definition_t session_desc = {
|
|
js_class_name,
|
|
FSSession::Construct,
|
|
session_proc,
|
|
session_prop
|
|
};
|
|
|
|
const js_class_definition_t *FSSession::GetClassDefinition()
|
|
{
|
|
return &session_desc;
|
|
}
|
|
|
|
/* For Emacs:
|
|
* Local Variables:
|
|
* mode:c
|
|
* indent-tabs-mode:t
|
|
* tab-width:4
|
|
* c-basic-offset:4
|
|
* End:
|
|
* For VIM:
|
|
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
|
|
*/
|