1 /*
2 * mod_v8 for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2013-2014, Peter Olsson <peter@olssononline.se>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * Ported from the Original Code in FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * Anthony Minessale II <anthm@freeswitch.org>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 * Peter Olsson <peter@olssononline.se>
26 * Anthony Minessale II <anthm@freeswitch.org>
27 * William King <william.king@quentustech.com>
28 *
29 * fssession.cpp -- JavaScript Session class
30 *
31 */
32
33 #include "fssession.hpp"
34 #include "fsevent.hpp"
35 #include "fsdtmf.hpp"
36
37 using namespace std;
38 using namespace v8;
39
40 static const char js_class_name[] = "Session";
41
42 #define METHOD_SANITY_CHECK() if (!this->_session) {\
43 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "No session is active, you must have an active session before calling this method"));\
44 return;\
45 } else CheckHangupHook(this, NULL)
46
47 #define CHANNEL_SANITY_CHECK() do {\
48 if (!switch_channel_ready(channel)) {\
49 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Session is not active!"));\
50 return;\
51 }\
52 if (!((switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag(channel, CF_EARLY_MEDIA)))) {\
53 switch_channel_pre_answer(channel);\
54 if (!((switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag(channel, CF_EARLY_MEDIA)))) {\
55 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Session is not answered!"));\
56 return;\
57 }\
58 }\
59 } while (foo == 1)
60
61 #define CHANNEL_SANITY_CHECK_ANSWER() do {\
62 if (!switch_channel_ready(channel)) {\
63 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Session is not active!"));\
64 return;\
65 }\
66 } while (foo == 1)
67
68 #define CHANNEL_MEDIA_SANITY_CHECK() do {\
69 if (!switch_channel_media_ready(channel)) {\
70 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Session is not in media mode!"));\
71 return;\
72 }\
73 } while (foo == 1)
74
75 #define LOCK_ISOLATE_ON_CALLBACK_MACRO() \
76 FSSession *obj = cb_state->session_state;\
77 Isolate *isolate = obj->GetOwner()->GetIsolate();\
78 Locker lock(isolate);\
79 Isolate::Scope isolate_scope(isolate);\
80 HandleScope handle_scope(isolate);\
81 Local<Context> context = Local<Context>::New(isolate, cb_state->context);\
82 Context::Scope context_scope(context);
83
84 static int foo = 0;
85
GetJSClassName()86 string FSSession::GetJSClassName()
87 {
88 return js_class_name;
89 }
90
Init(void)91 void FSSession::Init(void)
92 {
93 _session = NULL;
94 flags = 0;
95 _cause = SWITCH_CAUSE_NONE;
96 _stack_depth = 0;
97 _hook_state = CS_EXECUTE;
98 _destination_number = NULL;
99 _dialplan = NULL;
100 _caller_id_name = NULL;
101 _caller_id_number = NULL;
102 _network_addr = NULL;
103 _ani = NULL;
104 _aniii = NULL;
105 _rdnis = NULL;
106 _context = NULL;
107 _username = NULL;
108 _check_state = 1;
109 _speech = NULL;
110 }
111
GetSession()112 switch_core_session_t *FSSession::GetSession()
113 {
114 return _session;
115 }
116
Init(switch_core_session_t * session,unsigned int flags)117 void FSSession::Init(switch_core_session_t *session, unsigned int flags)
118 {
119 this->_session = session;
120 this->flags = flags;
121 }
122
DestroySpeechEngine()123 void FSSession::DestroySpeechEngine()
124 {
125 if (_speech) {
126 switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
127 switch_core_codec_destroy(&_speech->codec);
128 switch_core_speech_close(&_speech->sh, &flags);
129 _speech = NULL;
130 }
131 }
132
~FSSession(void)133 FSSession::~FSSession(void)
134 {
135 _on_hangup.Reset();
136
137 if (_speech && *_speech->sh.name) {
138 DestroySpeechEngine();
139 }
140
141 if (_session) {
142 switch_channel_t *channel = switch_core_session_get_channel(_session);
143
144 switch_channel_set_private(channel, "jsobject", NULL);
145 switch_core_event_hook_remove_state_change(_session, HangupHook);
146
147 if (switch_test_flag(this, S_HUP)) {
148 switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
149 }
150
151 switch_safe_free(_dialplan);
152 switch_safe_free(_username);
153 switch_safe_free(_caller_id_name);
154 switch_safe_free(_ani);
155 switch_safe_free(_aniii);
156 switch_safe_free(_caller_id_number);
157 switch_safe_free(_network_addr);
158 switch_safe_free(_rdnis);
159 switch_safe_free(_destination_number);
160 switch_safe_free(_context);
161 switch_core_session_rwunlock(_session);
162 }
163 }
164
FSInputCallbackState(void)165 FSInputCallbackState::FSInputCallbackState(void)
166 {
167 session_state = NULL;
168 memset(&code_buffer, 0, sizeof(code_buffer));
169 code_buffer_len = 0;
170 memset(&ret_buffer, 0, sizeof(ret_buffer));
171 ret_buffer_len = 0;
172 digit_count = 0;
173 extra = NULL;
174 jss_a = NULL;
175 jss_b = NULL;
176 }
177
~FSInputCallbackState(void)178 FSInputCallbackState::~FSInputCallbackState(void)
179 {
180 function.Reset();
181 arg.Reset();
182 ret.Reset();
183 session_obj_a.Reset();
184 session_obj_b.Reset();
185 context.Reset();
186 }
187
188 #define MAX_STACK_DEPTH 2
189
CommonCallback(switch_core_session_t * session,void * input,switch_input_type_t itype,void * buf,unsigned int buflen)190 switch_status_t FSSession::CommonCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
191 {
192 FSInputCallbackState *cb_state = (FSInputCallbackState *)buf;
193 switch_event_t *event = NULL;
194 int argc = 0;
195 Handle<Value> argv[4];
196 Handle<Object> Event;
197 bool ret = true;
198 switch_status_t status = SWITCH_STATUS_FALSE;
199
200 /* Session sanity check first */
201 if (!cb_state->session_state || !cb_state->session_state->_session) {
202 if (cb_state->session_state && cb_state->session_state->GetIsolate()) {
203 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"));
204 } else {
205 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No session is active, you must have an active session before calling this method\n");
206 }
207 return SWITCH_STATUS_FALSE;
208 } else {
209 /* Check hangup hook before we continue */
210 if (!CheckHangupHook(cb_state->session_state, NULL)) {
211 JSMain::ExitScript(cb_state->session_state->GetIsolate(), NULL);
212 return SWITCH_STATUS_FALSE;
213 }
214 }
215
216 if (++cb_state->session_state->_stack_depth > MAX_STACK_DEPTH) {
217 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Maximum recursive callback limit %d reached.\n", MAX_STACK_DEPTH);
218 cb_state->session_state->_stack_depth--;
219 return SWITCH_STATUS_FALSE;
220 }
221
222 Isolate *isolate = cb_state->session_state->GetIsolate();
223 HandleScope handle_scope(isolate);
224
225 if (cb_state->jss_a && cb_state->jss_a->_session && cb_state->jss_a->_session == session) {
226 argv[argc++] = Local<Object>::New(isolate, cb_state->session_obj_a);
227 } else if (cb_state->jss_b && cb_state->jss_b->_session && cb_state->jss_b->_session == session) {
228 argv[argc++] = Local<Object>::New(isolate, cb_state->session_obj_b);
229 } else {
230 argv[argc++] = Local<Object>::New(isolate, cb_state->session_state->GetJavaScriptObject());
231 }
232
233 switch (itype) {
234 case SWITCH_INPUT_TYPE_EVENT:
235 if ((event = (switch_event_t *) input)) {
236 Event = FSEvent::New(event, "", cb_state->session_state->GetOwner());
237 if (!Event.IsEmpty()) {
238 argv[argc++] = String::NewFromUtf8(isolate, "event");
239 argv[argc++] = Local<Object>::New(isolate, Event);
240 }
241 }
242 if (Event.IsEmpty()) {
243 goto done;
244 }
245 break;
246 case SWITCH_INPUT_TYPE_DTMF:
247 {
248 switch_dtmf_t *dtmf = (switch_dtmf_t *) input;
249
250 if (dtmf) {
251 Event = FSDTMF::New(dtmf, "", cb_state->session_state->GetOwner());
252 if (!Event.IsEmpty()) {
253 argv[argc++] = String::NewFromUtf8(isolate, "dtmf");
254 argv[argc++] = Local<Object>::New(isolate, Event);
255 } else {
256 goto done;
257 }
258 }
259 }
260 break;
261 }
262
263 if (!cb_state->arg.IsEmpty()) {
264 argv[argc++] = Local<Value>::New(isolate, cb_state->arg);
265 }
266
267 CheckHangupHook(cb_state->session_state, &ret);
268
269 if (ret) {
270 if (!cb_state->function.IsEmpty()) {
271 Handle<Function> func = Local<Function>::New(isolate, cb_state->function);
272
273 if (func->IsFunction()) {
274 Handle<Value> res = func->Call(isolate->GetCurrentContext()->Global(), argc, argv);
275
276 if (!res.IsEmpty()) {
277 cb_state->ret.Reset(isolate, res);
278 } else {
279 cb_state->ret.Reset();
280 }
281 }
282 }
283 } else {
284 JSMain::ExitScript(cb_state->session_state->GetIsolate(), NULL);
285 status = SWITCH_STATUS_FALSE;
286 goto done;
287 }
288
289 status = SWITCH_STATUS_SUCCESS;
290 done:
291 cb_state->session_state->_stack_depth--;
292 return status;
293 }
294
StreamInputCallback(switch_core_session_t * session,void * input,switch_input_type_t itype,void * buf,unsigned int buflen)295 switch_status_t FSSession::StreamInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
296 {
297 FSInputCallbackState *cb_state = (FSInputCallbackState *)buf;
298 LOCK_ISOLATE_ON_CALLBACK_MACRO();
299
300 switch_status_t status;
301 switch_file_handle_t *fh = (switch_file_handle_t *)cb_state->extra;
302
303 if (!switch_test_flag(fh, SWITCH_FILE_OPEN)) {
304 return SWITCH_STATUS_FALSE;
305 }
306
307 if ((status = CommonCallback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) {
308 return status;
309 }
310
311 if (!cb_state->ret.IsEmpty()) {
312 Handle<Value> tmp = Local<Value>::New(obj->GetOwner()->GetIsolate(), cb_state->ret);
313 String::Utf8Value str(tmp);
314 const char *ret = js_safe_str(*str);
315
316 if (!strncasecmp(ret, "speed", 5)) {
317 const char *p;
318
319 if ((p = strchr(ret, ':'))) {
320 p++;
321 if (*p == '+' || *p == '-') {
322 int step;
323 if (!(step = atoi(p))) {
324 step = 1;
325 }
326 fh->speed += step;
327 } else {
328 int speed = atoi(p);
329 fh->speed = speed;
330 }
331 return SWITCH_STATUS_SUCCESS;
332 }
333
334 return SWITCH_STATUS_FALSE;
335 } else if (!strncasecmp(ret, "volume", 6)) {
336 const char *p;
337
338 if ((p = strchr(ret, ':'))) {
339 p++;
340 if (*p == '+' || *p == '-') {
341 int step;
342 if (!(step = atoi(p))) {
343 step = 1;
344 }
345 fh->vol += step;
346 } else {
347 int vol = atoi(p);
348 fh->vol = vol;
349 }
350 return SWITCH_STATUS_SUCCESS;
351 }
352
353 if (fh->vol) {
354 switch_normalize_volume(fh->vol);
355 }
356
357 return SWITCH_STATUS_FALSE;
358 } else if (!strcasecmp(ret, "pause")) {
359 if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) {
360 switch_clear_flag_locked(fh, SWITCH_FILE_PAUSE);
361 } else {
362 switch_set_flag_locked(fh, SWITCH_FILE_PAUSE);
363 }
364 return SWITCH_STATUS_SUCCESS;
365 } else if (!strcasecmp(ret, "truncate")) {
366 switch_core_file_truncate(fh, 0);
367 } else if (!strcasecmp(ret, "restart")) {
368 uint32_t pos = 0;
369 fh->speed = 0;
370 switch_core_file_seek(fh, &pos, 0, SEEK_SET);
371 return SWITCH_STATUS_SUCCESS;
372 } else if (!strncasecmp(ret, "seek", 4)) {
373 uint32_t samps = 0;
374 uint32_t pos = 0;
375 const char *p;
376
377 if ((p = strchr(ret, ':'))) {
378 p++;
379 if (*p == '+' || *p == '-') {
380 int step;
381 if (!(step = atoi(p))) {
382 step = 1000;
383 }
384 if (step > 0) {
385 samps = step * (fh->native_rate / 1000);
386 switch_core_file_seek(fh, &pos, samps, SEEK_CUR);
387 } else {
388 samps = abs(step) * (fh->native_rate / 1000);
389 switch_core_file_seek(fh, &pos, fh->pos - samps, SEEK_SET);
390 }
391 } else {
392 samps = atoi(p) * (fh->native_rate / 1000);
393 switch_core_file_seek(fh, &pos, samps, SEEK_SET);
394 }
395 }
396
397 return SWITCH_STATUS_SUCCESS;
398 }
399
400 if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) {
401 return SWITCH_STATUS_SUCCESS;
402 }
403
404 return SWITCH_STATUS_BREAK;
405 }
406
407 return SWITCH_STATUS_SUCCESS;
408 }
409
RecordInputCallback(switch_core_session_t * session,void * input,switch_input_type_t itype,void * buf,unsigned int buflen)410 switch_status_t FSSession::RecordInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
411 {
412 FSInputCallbackState *cb_state = (FSInputCallbackState *)buf;
413 LOCK_ISOLATE_ON_CALLBACK_MACRO();
414
415 switch_status_t status;
416 switch_file_handle_t *fh = (switch_file_handle_t *)cb_state->extra;
417
418 if ((status = CommonCallback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) {
419 return status;
420 }
421
422 if (!cb_state->ret.IsEmpty()) {
423 Handle<Value> tmp = Local<Value>::New(obj->GetOwner()->GetIsolate(), cb_state->ret);
424 String::Utf8Value str(tmp);
425 const char *ret = js_safe_str(*str);
426
427 if (!strcasecmp(ret, "pause")) {
428 if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) {
429 switch_clear_flag_locked(fh, SWITCH_FILE_PAUSE);
430 } else {
431 switch_set_flag_locked(fh, SWITCH_FILE_PAUSE);
432 }
433 return SWITCH_STATUS_SUCCESS;
434 } else if (!strcasecmp(ret, "restart")) {
435 unsigned int pos = 0;
436 fh->speed = 0;
437 switch_core_file_seek(fh, &pos, 0, SEEK_SET);
438 return SWITCH_STATUS_SUCCESS;
439 }
440
441 if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) {
442 return SWITCH_STATUS_SUCCESS;
443 }
444
445 return SWITCH_STATUS_BREAK;
446 }
447
448 return SWITCH_STATUS_SUCCESS;
449 }
450
CollectInputCallback(switch_core_session_t * session,void * input,switch_input_type_t itype,void * buf,unsigned int buflen)451 switch_status_t FSSession::CollectInputCallback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
452 {
453 FSInputCallbackState *cb_state = (FSInputCallbackState *)buf;
454 LOCK_ISOLATE_ON_CALLBACK_MACRO();
455
456 const char *ret;
457 switch_status_t status;
458
459 if ((status = CommonCallback(session, input, itype, buf, buflen)) != SWITCH_STATUS_SUCCESS) {
460 return status;
461 }
462
463 if (!cb_state->ret.IsEmpty()) {
464 Handle<Value> tmp = Local<Value>::New(obj->GetOwner()->GetIsolate(), cb_state->ret);
465 String::Utf8Value str(tmp);
466 ret = js_safe_str(*str);
467
468 if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) {
469 return SWITCH_STATUS_SUCCESS;
470 }
471 }
472
473 return SWITCH_STATUS_BREAK;
474 }
475
JS_SESSION_FUNCTION_IMPL(FlushDigits)476 JS_SESSION_FUNCTION_IMPL(FlushDigits)
477 {
478 HandleScope handle_scope(info.GetIsolate());
479 switch_channel_t *channel;
480
481 METHOD_SANITY_CHECK();
482 channel = switch_core_session_get_channel(this->_session);
483 CHANNEL_MEDIA_SANITY_CHECK();
484
485 switch_channel_flush_dtmf(switch_core_session_get_channel(this->_session));
486
487 info.GetReturnValue().Set(true);
488 }
489
JS_SESSION_FUNCTION_IMPL(FlushEvents)490 JS_SESSION_FUNCTION_IMPL(FlushEvents)
491 {
492 HandleScope handle_scope(info.GetIsolate());
493 switch_event_t *event;
494
495 if (!this->_session) {
496 info.GetReturnValue().Set(false);
497 return;
498 }
499
500 while (switch_core_session_dequeue_event(this->_session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
501 switch_event_destroy(&event);
502 }
503
504 info.GetReturnValue().Set(true);
505 }
506
JS_SESSION_FUNCTION_IMPL(RecordFile)507 JS_SESSION_FUNCTION_IMPL(RecordFile)
508 {
509 HandleScope handle_scope(info.GetIsolate());
510 switch_channel_t *channel;
511 string file_name;
512 void *bp = NULL;
513 int len = 0;
514 switch_input_callback_function_t dtmf_func = NULL;
515 FSInputCallbackState cb_state;
516 switch_file_handle_t fh = { 0 };
517 int32_t limit = 0;
518 switch_input_args_t args = { 0 };
519 bool ret = true;
520
521 METHOD_SANITY_CHECK();
522 channel = switch_core_session_get_channel(this->_session);
523 CHANNEL_SANITY_CHECK();
524
525 CHANNEL_MEDIA_SANITY_CHECK();
526
527 if (info.Length() > 0) {
528 String::Utf8Value str(info[0]);
529 file_name = js_safe_str(*str);
530 if (zstr(file_name.c_str())) {
531 info.GetReturnValue().Set(false);
532 return;
533 }
534 }
535
536 if (info.Length() > 1) {
537 Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[1]);
538
539 if (!func.IsEmpty()) {
540 cb_state.session_state = this;
541 cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
542 cb_state.function.Reset(info.GetIsolate(), func);
543 if (info.Length() > 2 && !info[2].IsEmpty()) {
544 cb_state.arg.Reset(info.GetIsolate(), info[2]);
545 }
546
547 dtmf_func = RecordInputCallback;
548 bp = &cb_state;
549 len = sizeof(cb_state);
550 }
551
552 if (info.Length() > 3) {
553 limit = info[3]->Int32Value();
554 }
555
556 if (info.Length() > 4) {
557 fh.thresh = info[4]->Int32Value();
558 }
559
560 if (info.Length() > 5) {
561 fh.silence_hits = info[5]->Int32Value();
562 }
563 }
564
565 cb_state.extra = &fh;
566 cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
567 args.input_callback = dtmf_func;
568 args.buf = bp;
569 args.buflen = len;
570
571 JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_record_file(this->_session, &fh, file_name.c_str(), &args, limit));
572 info.GetReturnValue().Set(cb_state.ret);
573
574 CheckHangupHook(this, &ret);
575 if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
576 }
577
JS_SESSION_FUNCTION_IMPL(CollectInput)578 JS_SESSION_FUNCTION_IMPL(CollectInput)
579 {
580 HandleScope handle_scope(info.GetIsolate());
581 switch_channel_t *channel;
582 void *bp = NULL;
583 int len = 0;
584 int32_t abs_timeout = 0;
585 int32_t digit_timeout = 0;
586 switch_input_callback_function_t dtmf_func = NULL;
587 FSInputCallbackState cb_state;
588 switch_input_args_t args = { 0 };
589 bool ret = true;
590
591 METHOD_SANITY_CHECK();
592 channel = switch_core_session_get_channel(this->_session);
593 CHANNEL_SANITY_CHECK();
594 CHANNEL_MEDIA_SANITY_CHECK();
595
596 if (info.Length() > 0) {
597 Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[0]);
598
599 if (!func.IsEmpty()) {
600 cb_state.function.Reset(info.GetIsolate(), func);
601
602 if (info.Length() > 1 && !info[1].IsEmpty()) {
603 cb_state.arg.Reset(info.GetIsolate(), info[1]);
604 }
605
606 cb_state.session_state = this;
607 cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
608 dtmf_func = CollectInputCallback;
609 bp = &cb_state;
610 len = sizeof(cb_state);
611 }
612 }
613
614 if (info.Length() == 3) {
615 abs_timeout = info[2]->Int32Value();
616 } else if (info.Length() > 3) {
617 digit_timeout = info[2]->Int32Value();
618 abs_timeout = info[3]->Int32Value();
619 }
620
621 cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
622 args.input_callback = dtmf_func;
623 args.buf = bp;
624 args.buflen = len;
625
626 JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_collect_digits_callback(this->_session, &args, digit_timeout, abs_timeout));
627 info.GetReturnValue().Set(cb_state.ret);
628
629 CheckHangupHook(this, &ret);
630 if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
631 }
632
JS_SESSION_FUNCTION_IMPL(SayPhrase)633 JS_SESSION_FUNCTION_IMPL(SayPhrase)
634 {
635 HandleScope handle_scope(info.GetIsolate());
636 switch_channel_t *channel;
637 string phrase_name;
638 string phrase_data;
639 string phrase_lang;
640 string tmp;
641 void *bp = NULL;
642 int len = 0;
643 switch_input_callback_function_t dtmf_func = NULL;
644 FSInputCallbackState cb_state;
645 switch_input_args_t args = { 0 };
646 bool ret = true;
647
648 METHOD_SANITY_CHECK();
649 channel = switch_core_session_get_channel(this->_session);
650 CHANNEL_SANITY_CHECK();
651 CHANNEL_MEDIA_SANITY_CHECK();
652
653 if (info.Length() > 0) {
654 String::Utf8Value str(info[0]);
655 phrase_name = js_safe_str(*str);
656 if (zstr(phrase_name.c_str())) {
657 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid phrase name"));
658 return;
659 }
660 } else {
661 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid arguments"));
662 return;
663 }
664
665 if (info.Length() > 1) {
666 String::Utf8Value str(info[1]);
667 phrase_data = js_safe_str(*str);
668 }
669
670 if (info.Length() > 2) {
671 String::Utf8Value str(info[2]);
672 tmp = js_safe_str(*str);
673 if (!zstr(tmp.c_str())) {
674 phrase_lang = tmp;
675 }
676 }
677
678 if (info.Length() > 3) {
679 Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[3]);
680
681 if (!func.IsEmpty()) {
682 cb_state.function.Reset(info.GetIsolate(), func);
683
684 if (info.Length() > 4 && !info[4].IsEmpty()) {
685 cb_state.arg.Reset(info.GetIsolate(), info[4]);
686 }
687
688 cb_state.session_state = this;
689 cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
690 dtmf_func = CollectInputCallback;
691 bp = &cb_state;
692 len = sizeof(cb_state);
693 }
694 }
695
696 cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
697 args.input_callback = dtmf_func;
698 args.buf = bp;
699 args.buflen = len;
700
701 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));
702 info.GetReturnValue().Set(cb_state.ret);
703
704 CheckHangupHook(this, &ret);
705 if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
706 }
707
CheckHangupHook(FSSession * obj,bool * ret)708 bool FSSession::CheckHangupHook(FSSession *obj, bool *ret)
709 {
710 if (!obj) {
711 return true;
712 }
713
714 Isolate *isolate = obj->GetIsolate();
715 HandleScope handle_scope(isolate);
716 Handle<Value> argv[2];
717 int argc = 0;
718 bool res = true;
719 string resp;
720
721 if (!obj->_check_state && !obj->_on_hangup.IsEmpty() && (obj->_hook_state == CS_HANGUP || obj->_hook_state == CS_ROUTING)) {
722 obj->_check_state++;
723 argv[argc++] = Local<Object>::New(obj->GetOwner()->GetIsolate(), obj->GetJavaScriptObject());
724
725 if (obj->_hook_state == CS_HANGUP) {
726 argv[argc++] = String::NewFromUtf8(isolate, "hangup");
727 } else {
728 argv[argc++] = String::NewFromUtf8(isolate, "transfer");
729 }
730
731 // Run the hangup hook
732 Handle<Function> func = Local<Function>::New(isolate, obj->_on_hangup);
733
734 if (!func.IsEmpty() && func->IsFunction()) {
735 Handle<Value> res = func->Call(isolate->GetCurrentContext()->Global(), argc, argv);
736
737 if (!res.IsEmpty()) {
738 String::Utf8Value str(res);
739 resp = js_safe_str(*str);
740 }
741 }
742
743 if (resp.length() > 0) {
744 res = !strcasecmp(resp.c_str(), "exit") ? false : true;
745 }
746 }
747
748 if (ret) {
749 *ret = res;
750 }
751
752 return res;
753 }
754
HangupHook(switch_core_session_t * session)755 switch_status_t FSSession::HangupHook(switch_core_session_t *session)
756 {
757 switch_channel_t *channel = switch_core_session_get_channel(session);
758 switch_channel_state_t state = switch_channel_get_state(channel);
759 FSSession *obj = NULL;
760
761 if (state == CS_HANGUP || state == CS_ROUTING) {
762 if ((obj = (FSSession *)switch_channel_get_private(channel, "jsobject"))) {
763 obj->_hook_state = state;
764 obj->_check_state = 0;
765 }
766 }
767
768 return SWITCH_STATUS_SUCCESS;
769 }
770
JS_SESSION_FUNCTION_IMPL(SetHangupHook)771 JS_SESSION_FUNCTION_IMPL(SetHangupHook)
772 {
773 HandleScope handle_scope(info.GetIsolate());
774 info.GetReturnValue().Set(false);
775
776 if (this->_session) {
777 switch_channel_t *channel = switch_core_session_get_channel(this->_session);
778
779 /* Reset hangup hook first */
780 if (!this->_on_hangup.IsEmpty()) {
781 switch_channel_set_private(channel, "jsobject", NULL);
782 switch_core_event_hook_remove_state_change(this->_session, HangupHook);
783 this->_on_hangup.Reset();
784 this->_hook_state = switch_channel_get_state(channel);
785 }
786
787 if (info.Length() > 0) {
788 Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[0]);
789
790 if (!func.IsEmpty()) {
791 this->_on_hangup.Reset(info.GetIsolate(), func);
792 this->_hook_state = switch_channel_get_state(channel);
793 switch_channel_set_private(channel, "jsobject", this);
794 switch_core_event_hook_add_state_change(this->_session, HangupHook);
795 info.GetReturnValue().Set(true);
796 }
797 }
798 }
799 }
800
JS_SESSION_FUNCTION_IMPL(StreamFile)801 JS_SESSION_FUNCTION_IMPL(StreamFile)
802 {
803 HandleScope handle_scope(info.GetIsolate());
804 switch_channel_t *channel;
805 string file_name;
806 void *bp = NULL;
807 int len = 0;
808 switch_input_callback_function_t dtmf_func = NULL;
809 FSInputCallbackState cb_state;
810 switch_file_handle_t fh = { 0 };
811 switch_input_args_t args = { 0 };
812 const char *prebuf;
813 char posbuf[35] = "";
814 bool ret = true;
815
816 METHOD_SANITY_CHECK();
817 channel = switch_core_session_get_channel(this->_session);
818 CHANNEL_SANITY_CHECK();
819 CHANNEL_MEDIA_SANITY_CHECK();
820
821 if (info.Length() > 0) {
822 String::Utf8Value str(info[0]);
823 file_name = js_safe_str(*str);
824 if (zstr(file_name.c_str())) {
825 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid filename"));
826 return;
827 }
828 }
829
830 if (info.Length() > 1) {
831 Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[1]);
832
833 if (!func.IsEmpty()) {
834 cb_state.function.Reset(info.GetIsolate(), func);
835
836 if (info.Length() > 2 && !info[2].IsEmpty()) {
837 cb_state.arg.Reset(info.GetIsolate(), info[2]);
838 }
839
840 cb_state.session_state = this;
841 cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
842 dtmf_func = StreamInputCallback;
843 bp = &cb_state;
844 len = sizeof(cb_state);
845 }
846 }
847
848 if (info.Length() > 3) {
849 fh.samples = info[3]->Int32Value();
850 }
851
852 if ((prebuf = switch_channel_get_variable(channel, "stream_prebuffer"))) {
853 int maybe = atoi(prebuf);
854 if (maybe > 0) {
855 fh.prebuf = maybe;
856 }
857 }
858
859 cb_state.extra = &fh;
860 cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
861 args.input_callback = dtmf_func;
862 args.buf = bp;
863 args.buflen = len;
864 JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_play_file(this->_session, &fh, file_name.c_str(), &args));
865 info.GetReturnValue().Set(cb_state.ret);
866
867 switch_snprintf(posbuf, sizeof(posbuf), "%u", fh.offset_pos);
868 switch_channel_set_variable(channel, "last_file_position", posbuf);
869
870 CheckHangupHook(this, &ret);
871 if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
872 }
873
JS_SESSION_FUNCTION_IMPL(Sleep)874 JS_SESSION_FUNCTION_IMPL(Sleep)
875 {
876 HandleScope handle_scope(info.GetIsolate());
877 switch_channel_t *channel;
878 void *bp = NULL;
879 int len = 0;
880 switch_input_callback_function_t dtmf_func = NULL;
881 FSInputCallbackState cb_state;
882 switch_input_args_t args = { 0 };
883 int32_t ms = 0;
884 bool ret = true;
885 int32_t sync = 0;
886
887 METHOD_SANITY_CHECK();
888 channel = switch_core_session_get_channel(this->_session);
889 CHANNEL_SANITY_CHECK();
890 CHANNEL_MEDIA_SANITY_CHECK();
891
892 if (info.Length() > 0) {
893 ms = info[0]->Int32Value();
894 }
895
896 if (ms <= 0) {
897 return;
898 }
899
900 if (info.Length() > 1) {
901 Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[1]);
902
903 if (!func.IsEmpty()) {
904 cb_state.function.Reset(info.GetIsolate(), func);
905
906 if (info.Length() > 2 && !info[2].IsEmpty()) {
907 cb_state.arg.Reset(info.GetIsolate(), info[2]);
908 }
909
910 cb_state.session_state = this;
911 cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
912 dtmf_func = CollectInputCallback;
913 bp = &cb_state;
914 len = sizeof(cb_state);
915 }
916 }
917
918 if (info.Length() > 2) {
919 sync = info[2]->Int32Value();
920 }
921
922 cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
923 args.input_callback = dtmf_func;
924 args.buf = bp;
925 args.buflen = len;
926 JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_sleep(this->_session, ms, (switch_bool_t)sync, &args));
927 info.GetReturnValue().Set(cb_state.ret);
928
929 CheckHangupHook(this, &ret);
930 if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
931 }
932
JS_SESSION_FUNCTION_IMPL(SetVariable)933 JS_SESSION_FUNCTION_IMPL(SetVariable)
934 {
935 HandleScope handle_scope(info.GetIsolate());
936 switch_channel_t *channel;
937
938 METHOD_SANITY_CHECK();
939 channel = switch_core_session_get_channel(this->_session);
940
941 if (info.Length() > 1) {
942 const char *var, *val;
943 String::Utf8Value str1(info[0]);
944 String::Utf8Value str2(info[1]);
945
946 var = js_safe_str(*str1);
947 val = *str2;
948 switch_channel_set_variable_var_check(channel, var, val, SWITCH_FALSE);
949 info.GetReturnValue().Set(true);
950 } else {
951 info.GetReturnValue().Set(false);
952 }
953 }
954
JS_SESSION_FUNCTION_IMPL(GetVariable)955 JS_SESSION_FUNCTION_IMPL(GetVariable)
956 {
957 HandleScope handle_scope(info.GetIsolate());
958 switch_channel_t *channel;
959
960 METHOD_SANITY_CHECK();
961 channel = switch_core_session_get_channel(this->_session);
962
963 if (info.Length() > 0) {
964 const char *var, *val;
965 String::Utf8Value str(info[0]);
966
967 var = js_safe_str(*str);
968 val = switch_channel_get_variable(channel, var);
969
970 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), js_safe_str(val)));
971 } else {
972 info.GetReturnValue().Set(false);
973 }
974 }
975
InitSpeechEngine(const char * engine,const char * voice)976 switch_status_t FSSession::InitSpeechEngine(const char *engine, const char *voice)
977 {
978 switch_codec_t *read_codec;
979 switch_speech_flag_t flags = SWITCH_SPEECH_FLAG_NONE;
980 uint32_t rate = 0;
981 int interval = 0;
982
983 read_codec = switch_core_session_get_read_codec(this->_session);
984 rate = read_codec->implementation->actual_samples_per_second;
985 interval = read_codec->implementation->microseconds_per_packet / 1000;
986
987 if (switch_core_codec_init(&this->_speech->codec,
988 "L16",
989 NULL,
990 NULL,
991 rate,
992 interval,
993 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,
994 switch_core_session_get_pool(this->_session)) == SWITCH_STATUS_SUCCESS) {
995 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Success L16@%uhz 1 channel %dms\n", rate, interval);
996 } else {
997 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Failed L16@%uhz 1 channel %dms\n", rate, interval);
998 return SWITCH_STATUS_FALSE;
999 }
1000
1001 if (switch_core_speech_open(&this->_speech->sh, engine, voice, rate, interval, read_codec->implementation->number_of_channels,
1002 &flags, switch_core_session_get_pool(this->_session)) != SWITCH_STATUS_SUCCESS) {
1003 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid TTS module!\n");
1004 switch_core_codec_destroy(&this->_speech->codec);
1005 return SWITCH_STATUS_FALSE;
1006 }
1007
1008 return SWITCH_STATUS_SUCCESS;
1009 }
1010
JS_SESSION_FUNCTION_IMPL(Speak)1011 JS_SESSION_FUNCTION_IMPL(Speak)
1012 {
1013 HandleScope handle_scope(info.GetIsolate());
1014 switch_channel_t *channel;
1015 const char *tts_name = NULL;
1016 const char *voice_name = NULL;
1017 const char *text = NULL;
1018 void *bp = NULL;
1019 int len = 0;
1020 FSInputCallbackState cb_state;
1021 switch_input_callback_function_t dtmf_func = NULL;
1022 switch_input_args_t args = { 0 };
1023 bool ret = true;
1024
1025 METHOD_SANITY_CHECK();
1026 info.GetReturnValue().Set(false);
1027 channel = switch_core_session_get_channel(this->_session);
1028 CHANNEL_SANITY_CHECK();
1029 CHANNEL_MEDIA_SANITY_CHECK();
1030
1031 if (info.Length() < 3) {
1032 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid arguments"));
1033 return;
1034 }
1035
1036 String::Utf8Value str1(info[0]);
1037 String::Utf8Value str2(info[1]);
1038 String::Utf8Value str3(info[2]);
1039 tts_name = js_safe_str(*str1);
1040 voice_name = js_safe_str(*str2);
1041 text = js_safe_str(*str3);
1042
1043 if (zstr(tts_name)) {
1044 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid TTS Name"));
1045 return;
1046 }
1047
1048 if (zstr(text)) {
1049 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Invalid Text"));
1050 return;
1051 }
1052
1053 if (this->_speech && this->_speech->speaking) {
1054 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Recursive call not allowed"));
1055 return;
1056 }
1057
1058
1059 if (this->_speech && strcasecmp(this->_speech->sh.name, tts_name)) {
1060 DestroySpeechEngine();
1061 }
1062
1063 if (this->_speech) {
1064 const char *s = "voice";
1065 switch_core_speech_text_param_tts(&this->_speech->sh, (char *)s, voice_name);
1066 } else {
1067 this->_speech = (js_session_speech_t *)switch_core_session_alloc(this->_session, sizeof(*this->_speech));
1068 switch_assert(this->_speech != NULL);
1069 if (InitSpeechEngine(tts_name, voice_name) != SWITCH_STATUS_SUCCESS) {
1070 this->_speech = NULL;
1071 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Cannot allocate speech engine!"));
1072 return;
1073 }
1074 }
1075
1076 if (info.Length() > 3) {
1077 Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[3]);
1078
1079 if (!func.IsEmpty()) {
1080 cb_state.function.Reset(info.GetIsolate(), func);
1081
1082 if (info.Length() > 4 && !info[4].IsEmpty()) {
1083 cb_state.arg.Reset(info.GetIsolate(), info[4]);
1084 }
1085
1086 cb_state.session_state = this;
1087 cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
1088 dtmf_func = CollectInputCallback;
1089 bp = &cb_state;
1090 len = sizeof(cb_state);
1091 }
1092 }
1093
1094 cb_state.ret.Reset(info.GetIsolate(), Boolean::New(info.GetIsolate(), false));
1095 args.input_callback = dtmf_func;
1096 args.buf = bp;
1097 args.buflen = len;
1098
1099 switch_core_speech_flush_tts(&this->_speech->sh);
1100 if (switch_core_codec_ready(&this->_speech->codec)) {
1101 this->_speech->speaking = 1;
1102 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));
1103 this->_speech->speaking = 0;
1104 }
1105
1106 info.GetReturnValue().Set(cb_state.ret);
1107
1108 CheckHangupHook(this, &ret);
1109 if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
1110 }
1111
JS_SESSION_FUNCTION_IMPL(GetDigits)1112 JS_SESSION_FUNCTION_IMPL(GetDigits)
1113 {
1114 HandleScope handle_scope(info.GetIsolate());
1115 string terminators;
1116 char buf[513] = { 0 };
1117 int32_t digits = 0, timeout = 5000, digit_timeout = 0, abs_timeout = 0;
1118 switch_channel_t *channel;
1119
1120 METHOD_SANITY_CHECK();
1121 channel = switch_core_session_get_channel(this->_session);
1122 CHANNEL_SANITY_CHECK();
1123
1124 if (info.Length() > 0) {
1125 char term;
1126 digits = info[0]->Int32Value();
1127
1128 if (digits > sizeof(buf) - 1) {
1129 char *err = switch_mprintf("Exceeded max digits of %" SWITCH_SIZE_T_FMT, sizeof(buf) - 1);
1130 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), err));
1131 free(err);
1132 return;
1133 }
1134
1135 if (info.Length() > 1) {
1136 String::Utf8Value str(info[1]);
1137 terminators = js_safe_str(*str);
1138 }
1139
1140 if (info.Length() > 2) {
1141 timeout = info[2]->Int32Value();
1142 }
1143
1144 if (info.Length() > 3) {
1145 digit_timeout = info[3]->Int32Value();
1146 }
1147
1148 if (info.Length() > 4) {
1149 abs_timeout = info[4]->Int32Value();
1150 }
1151
1152 switch_ivr_collect_digits_count(this->_session, buf, sizeof(buf), digits, terminators.c_str(), &term, timeout, digit_timeout, abs_timeout);
1153 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), buf));
1154 }
1155 }
1156
JS_SESSION_FUNCTION_IMPL(SetAutoHangup)1157 JS_SESSION_FUNCTION_IMPL(SetAutoHangup)
1158 {
1159 HandleScope handle_scope(info.GetIsolate());
1160 info.GetReturnValue().Set(false);
1161
1162 METHOD_SANITY_CHECK();
1163
1164 if (info.Length() > 0) {
1165 bool tf = info[0]->BooleanValue();
1166 if (tf) {
1167 switch_set_flag(this, S_HUP);
1168 } else {
1169 switch_clear_flag(this, S_HUP);
1170 }
1171 info.GetReturnValue().Set(tf);
1172 }
1173 }
1174
JS_SESSION_FUNCTION_IMPL(Answer)1175 JS_SESSION_FUNCTION_IMPL(Answer)
1176 {
1177 HandleScope handle_scope(info.GetIsolate());
1178 switch_channel_t *channel;
1179
1180 METHOD_SANITY_CHECK();
1181 channel = switch_core_session_get_channel(this->_session);
1182 CHANNEL_SANITY_CHECK_ANSWER();
1183
1184 switch_channel_answer(channel);
1185
1186 info.GetReturnValue().Set(true);
1187 }
1188
JS_SESSION_FUNCTION_IMPL(PreAnswer)1189 JS_SESSION_FUNCTION_IMPL(PreAnswer)
1190 {
1191 HandleScope handle_scope(info.GetIsolate());
1192 switch_channel_t *channel;
1193
1194 METHOD_SANITY_CHECK();
1195 channel = switch_core_session_get_channel(this->_session);
1196 CHANNEL_SANITY_CHECK_ANSWER();
1197
1198 switch_channel_pre_answer(channel);
1199
1200 info.GetReturnValue().Set(true);
1201 }
1202
JS_SESSION_FUNCTION_IMPL(GenerateXmlCdr)1203 JS_SESSION_FUNCTION_IMPL(GenerateXmlCdr)
1204 {
1205 HandleScope handle_scope(info.GetIsolate());
1206 switch_xml_t cdr = NULL;
1207
1208 info.GetReturnValue().Set(false);
1209
1210 if (switch_ivr_generate_xml_cdr(this->_session, &cdr) == SWITCH_STATUS_SUCCESS) {
1211 char *xml_text;
1212 if ((xml_text = switch_xml_toxml(cdr, SWITCH_FALSE))) {
1213 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), xml_text));
1214 }
1215 switch_safe_free(xml_text);
1216 switch_xml_free(cdr);
1217 }
1218 }
1219
JS_SESSION_FUNCTION_IMPL(Ready)1220 JS_SESSION_FUNCTION_IMPL(Ready)
1221 {
1222 HandleScope handle_scope(info.GetIsolate());
1223
1224 info.GetReturnValue().Set((this->_session && switch_channel_ready(switch_core_session_get_channel(this->_session))) ? true : false);
1225 }
1226
JS_SESSION_FUNCTION_IMPL(MediaReady)1227 JS_SESSION_FUNCTION_IMPL(MediaReady)
1228 {
1229 HandleScope handle_scope(info.GetIsolate());
1230
1231 info.GetReturnValue().Set((this->_session && switch_channel_media_ready(switch_core_session_get_channel(this->_session))) ? true : false);
1232 }
1233
JS_SESSION_FUNCTION_IMPL(RingReady)1234 JS_SESSION_FUNCTION_IMPL(RingReady)
1235 {
1236 HandleScope handle_scope(info.GetIsolate());
1237
1238 info.GetReturnValue().Set((this->_session && switch_channel_test_flag(switch_core_session_get_channel(this->_session), CF_RING_READY)) ? true : false);
1239 }
1240
JS_SESSION_FUNCTION_IMPL(Answered)1241 JS_SESSION_FUNCTION_IMPL(Answered)
1242 {
1243 HandleScope handle_scope(info.GetIsolate());
1244
1245 info.GetReturnValue().Set((this->_session && switch_channel_test_flag(switch_core_session_get_channel(this->_session), CF_ANSWERED)) ? true : false);
1246 }
1247
JS_SESSION_FUNCTION_IMPL(WaitForMedia)1248 JS_SESSION_FUNCTION_IMPL(WaitForMedia)
1249 {
1250 HandleScope handle_scope(info.GetIsolate());
1251 switch_channel_t *channel;
1252 switch_time_t started;
1253 unsigned int elapsed;
1254 int32_t timeout = 60000;
1255 bool ret = true;
1256
1257 METHOD_SANITY_CHECK();
1258 channel = switch_core_session_get_channel(this->_session);
1259 CHANNEL_MEDIA_SANITY_CHECK();
1260
1261 started = switch_micro_time_now();
1262
1263 if (info.Length() > 0) {
1264 timeout = info[0]->Int32Value();
1265 if (timeout < 1000) {
1266 timeout = 1000;
1267 }
1268 }
1269
1270 if (!CheckHangupHook(this, NULL)) {
1271 JSMain::ExitScript(info.GetIsolate(), NULL);
1272 return;
1273 }
1274
1275 for (;;) {
1276 if (((elapsed = (unsigned int) ((switch_micro_time_now() - started) / 1000)) > (switch_time_t) timeout)
1277 || switch_channel_down(channel)) {
1278 info.GetReturnValue().Set(false);
1279 break;
1280 }
1281
1282 if (switch_channel_ready(channel)
1283 && (switch_channel_test_flag(channel, CF_ANSWERED) || switch_channel_test_flag(channel, CF_EARLY_MEDIA))) {
1284 info.GetReturnValue().Set(true);
1285 break;
1286 }
1287
1288 switch_cond_next();
1289 }
1290
1291 CheckHangupHook(this, &ret);
1292 if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
1293 }
1294
JS_SESSION_FUNCTION_IMPL(WaitForAnswer)1295 JS_SESSION_FUNCTION_IMPL(WaitForAnswer)
1296 {
1297 HandleScope handle_scope(info.GetIsolate());
1298 switch_channel_t *channel;
1299 switch_time_t started;
1300 unsigned int elapsed;
1301 int32_t timeout = 60000;
1302 bool ret = true;
1303
1304 METHOD_SANITY_CHECK();
1305 channel = switch_core_session_get_channel(this->_session);
1306
1307 started = switch_micro_time_now();
1308
1309 if (info.Length() > 0) {
1310 timeout = info[0]->Int32Value();
1311 if (timeout < 1000) {
1312 timeout = 1000;
1313 }
1314 }
1315
1316 if (!CheckHangupHook(this, NULL)) {
1317 JSMain::ExitScript(info.GetIsolate(), NULL);
1318 return;
1319 }
1320
1321 for (;;) {
1322 if (((elapsed = (unsigned int) ((switch_micro_time_now() - started) / 1000)) > (switch_time_t) timeout)
1323 || switch_channel_down(channel)) {
1324 info.GetReturnValue().Set(false);
1325 break;
1326 }
1327
1328 if (switch_channel_ready(channel) && switch_channel_test_flag(channel, CF_ANSWERED)) {
1329 info.GetReturnValue().Set(true);
1330 break;
1331 }
1332
1333 switch_cond_next();
1334 }
1335
1336 CheckHangupHook(this, &ret);
1337 if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
1338 }
1339
JS_SESSION_FUNCTION_IMPL(Detach)1340 JS_SESSION_FUNCTION_IMPL(Detach)
1341 {
1342 HandleScope handle_scope(info.GetIsolate());
1343 switch_call_cause_t cause = SWITCH_CAUSE_NONE;
1344 switch_channel_t *channel;
1345 switch_core_session_t *session;
1346
1347 METHOD_SANITY_CHECK();
1348
1349 if ((session = this->_session)) {
1350 this->_session = NULL;
1351
1352 if (info.Length() > 0) {
1353 if (info[0]->IsInt32()) {
1354 int32_t i = 0;
1355 i = info[0]->Int32Value();
1356 cause = (switch_call_cause_t)i;
1357 } else {
1358 String::Utf8Value str(info[0]);
1359 const char *cause_name = js_safe_str(*str);
1360 cause = switch_channel_str2cause(cause_name);
1361 }
1362 }
1363
1364 if (cause != SWITCH_CAUSE_NONE) {
1365 channel = switch_core_session_get_channel(session);
1366 switch_channel_hangup(channel, cause);
1367 }
1368
1369 switch_core_session_rwunlock(session);
1370 info.GetReturnValue().Set(true);
1371 } else {
1372 info.GetReturnValue().Set(false);
1373 }
1374 }
1375
JS_SESSION_FUNCTION_IMPL(Execute)1376 JS_SESSION_FUNCTION_IMPL(Execute)
1377 {
1378 HandleScope handle_scope(info.GetIsolate());
1379 bool retval = false;
1380 bool ret = true;
1381
1382 METHOD_SANITY_CHECK();
1383
1384 /* you can execute some apps before you answer CHANNEL_SANITY_CHECK(); */
1385
1386 if (info.Length() > 0) {
1387 switch_application_interface_t *application_interface;
1388 String::Utf8Value str(info[0]);
1389 const char *app_name = js_safe_str(*str);
1390 string app_arg;
1391
1392 METHOD_SANITY_CHECK();
1393
1394 if (info.Length() > 1) {
1395 String::Utf8Value str2(info[1]);
1396 app_arg = js_safe_str(*str2);
1397 }
1398
1399 if ((application_interface = switch_loadable_module_get_application_interface(app_name))) {
1400 if (application_interface->application_function) {
1401 if (!CheckHangupHook(this, NULL)) {
1402 JSMain::ExitScript(info.GetIsolate(), NULL);
1403 UNPROTECT_INTERFACE(application_interface);
1404 return;
1405 }
1406
1407 switch_core_session_exec(this->_session, application_interface, app_arg.c_str());
1408 CheckHangupHook(this, &ret);
1409 retval = true;
1410 }
1411 UNPROTECT_INTERFACE(application_interface);
1412 }
1413 }
1414
1415 info.GetReturnValue().Set(retval);
1416 if (!ret) JSMain::ExitScript(info.GetIsolate(), NULL);
1417 }
1418
JS_SESSION_FUNCTION_IMPL(GetEvent)1419 JS_SESSION_FUNCTION_IMPL(GetEvent)
1420 {
1421 HandleScope handle_scope(info.GetIsolate());
1422 switch_event_t *event;
1423
1424 METHOD_SANITY_CHECK();
1425
1426 if (switch_core_session_dequeue_event(this->_session, &event, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
1427 Handle<Object> Event;
1428 FSEvent *evt;
1429
1430 if ((evt = new FSEvent(info))) {
1431 evt->SetEvent(event, 0);
1432 evt->RegisterInstance(info.GetIsolate(), "", true);
1433 Event = evt->GetJavaScriptObject();
1434
1435 if (!Event.IsEmpty()) {
1436 info.GetReturnValue().Set(Event);
1437 return;
1438 }
1439 }
1440 }
1441
1442 info.GetReturnValue().Set(false);
1443 }
1444
JS_SESSION_FUNCTION_IMPL(SendEvent)1445 JS_SESSION_FUNCTION_IMPL(SendEvent)
1446 {
1447 HandleScope handle_scope(info.GetIsolate());
1448 Handle<Object> Event;
1449 FSEvent *eo;
1450
1451 METHOD_SANITY_CHECK();
1452
1453 if (info.Length() > 0 && info[0]->IsObject()) {
1454 Handle<Object> jso = Handle<Object>::Cast(info[0]);
1455
1456 if (!jso.IsEmpty()) {
1457 switch_event_t **evt;
1458
1459 if ((eo = JSBase::GetInstance<FSEvent>(jso)) && (evt = eo->GetEvent())) {
1460 if (switch_core_session_receive_event(this->_session, evt) != SWITCH_STATUS_SUCCESS) {
1461 info.GetReturnValue().Set(false);
1462 return;
1463 }
1464
1465 delete eo;
1466 }
1467 }
1468 }
1469
1470 info.GetReturnValue().Set(true);
1471 }
1472
JS_SESSION_FUNCTION_IMPL(Hangup)1473 JS_SESSION_FUNCTION_IMPL(Hangup)
1474 {
1475 HandleScope handle_scope(info.GetIsolate());
1476 switch_channel_t *channel;
1477 switch_call_cause_t cause = SWITCH_CAUSE_NORMAL_CLEARING;
1478
1479 METHOD_SANITY_CHECK();
1480 channel = switch_core_session_get_channel(this->_session);
1481
1482 if (switch_channel_up(channel)) {
1483 if (info.Length() > 0) {
1484 if (info[0]->IsInt32()) {
1485 int32_t i = info[0]->Int32Value();
1486 cause = (switch_call_cause_t)i;
1487 } else {
1488 String::Utf8Value str(info[0]);
1489 const char *cause_name = js_safe_str(*str);
1490 cause = switch_channel_str2cause(cause_name);
1491 }
1492 }
1493
1494 switch_channel_hangup(channel, cause);
1495 switch_core_session_kill_channel(this->_session, SWITCH_SIG_KILL);
1496
1497 this->_hook_state = CS_HANGUP;
1498 CheckHangupHook(this, NULL);
1499
1500 info.GetReturnValue().Set(true);
1501 } else {
1502 info.GetReturnValue().Set(false);
1503 }
1504 }
1505
JS_SESSION_GET_PROPERTY_IMPL(GetProperty)1506 JS_SESSION_GET_PROPERTY_IMPL(GetProperty)
1507 {
1508 HandleScope handle_scope(info.GetIsolate());
1509 switch_channel_t *channel = NULL;
1510 switch_caller_profile_t *caller_profile = NULL;
1511
1512 if (this->_session) {
1513 channel = switch_core_session_get_channel(this->_session);
1514 caller_profile = switch_channel_get_caller_profile(channel);
1515 }
1516
1517 String::Utf8Value str(property);
1518 const char *prop = js_safe_str(*str);
1519
1520 if (!strcmp(prop, "cause")) {
1521 if (channel) {
1522 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_channel_cause2str(switch_channel_get_cause(channel))));
1523 } else {
1524 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_channel_cause2str(this->_cause)));
1525 }
1526 } else if (!strcmp(prop, "causecode")) {
1527 if (channel) {
1528 info.GetReturnValue().Set(Integer::New(info.GetIsolate(), switch_channel_get_cause(channel)));
1529 } else {
1530 info.GetReturnValue().Set(Integer::New(info.GetIsolate(), this->_cause));
1531 }
1532 } else if (!strcmp(prop, "name")) {
1533 if (channel) {
1534 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_channel_get_name(channel)));
1535 } else {
1536 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
1537 }
1538 } else if (!strcmp(prop, "uuid")) {
1539 if (channel) {
1540 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_channel_get_uuid(channel)));
1541 } else {
1542 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
1543 }
1544 } else if (!strcmp(prop, "state")) {
1545 if (channel) {
1546 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), switch_channel_state_name(switch_channel_get_state(channel))));
1547 } else {
1548 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
1549 }
1550 } else if (!strcmp(prop, "dialplan")) {
1551 if (caller_profile) {
1552 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->dialplan));
1553 } else {
1554 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
1555 }
1556 } else if (!strcmp(prop, "caller_id_name")) {
1557 if (caller_profile) {
1558 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->caller_id_name));
1559 } else {
1560 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
1561 }
1562 } else if (!strcmp(prop, "caller_id_num") || !strcmp(prop, "caller_id_number")) {
1563 if (caller_profile) {
1564 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->caller_id_number));
1565 } else {
1566 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
1567 }
1568 } else if (!strcmp(prop, "network_addr") || !strcasecmp(prop, "network_address")) {
1569 if (caller_profile) {
1570 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->network_addr));
1571 } else {
1572 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
1573 }
1574 } else if (!strcmp(prop, "ani")) {
1575 if (caller_profile) {
1576 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->ani));
1577 } else {
1578 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
1579 }
1580 } else if (!strcmp(prop, "aniii")) {
1581 if (caller_profile) {
1582 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->aniii));
1583 } else {
1584 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
1585 }
1586 } else if (!strcmp(prop, "destination")) {
1587 if (caller_profile) {
1588 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), caller_profile->destination_number));
1589 } else {
1590 info.GetReturnValue().Set(String::NewFromUtf8(info.GetIsolate(), ""));
1591 }
1592 } else {
1593 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Bad property"));
1594 }
1595 }
1596
Construct(const v8::FunctionCallbackInfo<Value> & info)1597 void *FSSession::Construct(const v8::FunctionCallbackInfo<Value>& info)
1598 {
1599 HandleScope handle_scope(info.GetIsolate());
1600 FSSession *session_obj = new FSSession(info);
1601
1602 switch_assert(session_obj);
1603
1604 if (info.Length() > 0) {
1605 String::Utf8Value str(info[0]);
1606 const char *uuid = js_safe_str(*str);
1607
1608 if (!strchr(uuid, '/')) {
1609 session_obj->_session = switch_core_session_locate(uuid);
1610 switch_set_flag(session_obj, S_HUP);
1611 } else {
1612 FSSession *old_obj = NULL;
1613
1614 if (info.Length() > 1 && info[1]->IsObject()) {
1615 old_obj = JSBase::GetInstance<FSSession>(Handle<Object>::Cast(info[1]));
1616 }
1617 if (switch_ivr_originate(old_obj ? old_obj->_session : NULL,
1618 &session_obj->_session, &session_obj->_cause, uuid, 60,
1619 NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL) == SWITCH_STATUS_SUCCESS) {
1620 switch_set_flag(session_obj, S_HUP);
1621 } else {
1622 /* This will return the Session object, but with no C++ instance related to it */
1623 /* After each call to [new Session("/chan/test")] you should check the property originateCause, which will hold a value if origination failed */
1624 info.This()->Set(String::NewFromUtf8(info.GetIsolate(), "originateCause"), String::NewFromUtf8(info.GetIsolate(), switch_channel_cause2str(session_obj->_cause)));
1625 delete session_obj;
1626 return NULL;
1627 }
1628 }
1629 }
1630
1631 return session_obj;
1632 }
1633
JS_SESSION_FUNCTION_IMPL(SetCallerdata)1634 JS_SESSION_FUNCTION_IMPL(SetCallerdata)
1635 {
1636 HandleScope handle_scope(info.GetIsolate());
1637 info.GetReturnValue().Set(false);
1638
1639 if (info.Length() > 1) {
1640 const char *var, *val;
1641 char **toset = NULL;
1642 String::Utf8Value str1(info[0]);
1643 String::Utf8Value str2(info[1]);
1644 var = js_safe_str(*str1);
1645 val = js_safe_str(*str2);
1646
1647 if (!strcasecmp(var, "dialplan")) {
1648 toset = &this->_dialplan;
1649 } else if (!strcasecmp(var, "username")) {
1650 toset = &this->_username;
1651 } else if (!strcasecmp(var, "caller_id_name")) {
1652 toset = &this->_caller_id_name;
1653 } else if (!strcasecmp(var, "ani")) {
1654 toset = &this->_ani;
1655 } else if (!strcasecmp(var, "aniii")) {
1656 toset = &this->_aniii;
1657 } else if (!strcasecmp(var, "caller_id_number")) {
1658 toset = &this->_caller_id_number;
1659 } else if (!strcasecmp(var, "network_addr")) {
1660 toset = &this->_network_addr;
1661 } else if (!strcasecmp(var, "rdnis")) {
1662 toset = &this->_rdnis;
1663 } else if (!strcasecmp(var, "destination_number")) {
1664 toset = &this->_destination_number;
1665 } else if (!strcasecmp(var, "context")) {
1666 toset = &this->_context;
1667 }
1668
1669 if (toset) {
1670 switch_safe_free(*toset);
1671 *toset = strdup(val);
1672 info.GetReturnValue().Set(true);
1673 }
1674 }
1675 }
1676
JS_SESSION_FUNCTION_IMPL(Originate)1677 JS_SESSION_FUNCTION_IMPL(Originate)
1678 {
1679 HandleScope handle_scope(info.GetIsolate());
1680 switch_memory_pool_t *pool = NULL;
1681
1682 this->_cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
1683
1684 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "This method is deprecated, please use new Session(\"<dial string>\", a_leg) \n");
1685
1686 if (this->_session) {
1687 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "cannot call this method on an initialized session"));
1688 return;
1689 }
1690
1691 if (info.Length() > 1) {
1692 Handle<Object> session_obj;
1693 switch_core_session_t *session = NULL, *peer_session = NULL;
1694 switch_caller_profile_t *caller_profile = NULL, *orig_caller_profile = NULL;
1695 string dest;
1696 const char *dialplan = NULL;
1697 const char *cid_name = "";
1698 const char *cid_num = "";
1699 const char *network_addr = "";
1700 const char *ani = "";
1701 const char *aniii = "";
1702 const char *rdnis = "";
1703 const char *context = "";
1704 const char *username = NULL;
1705 string to;
1706 char *tmp;
1707 switch_status_t status;
1708
1709 info.GetReturnValue().Set(false);
1710
1711 if (info[0]->IsObject()) {
1712 session_obj = Handle<Object>::Cast(info[0]);
1713 FSSession *old_obj = NULL;
1714 if (!session_obj.IsEmpty() && (old_obj = JSBase::GetInstance<FSSession>(session_obj))) {
1715
1716 if (old_obj == this) {
1717 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Supplied a_leg session is the same as our session"));
1718 return;
1719 }
1720
1721 if (!old_obj->_session) {
1722 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Supplied a_leg session is not initilaized!"));
1723 return;
1724 }
1725
1726 session = old_obj->_session;
1727 orig_caller_profile = switch_channel_get_caller_profile(switch_core_session_get_channel(session));
1728 dialplan = orig_caller_profile->dialplan;
1729 cid_name = orig_caller_profile->caller_id_name;
1730 cid_num = orig_caller_profile->caller_id_number;
1731 ani = orig_caller_profile->ani;
1732 aniii = orig_caller_profile->aniii;
1733 rdnis = orig_caller_profile->rdnis;
1734 context = orig_caller_profile->context;
1735 username = orig_caller_profile->username;
1736 }
1737 }
1738
1739 if (!zstr(this->_dialplan) && zstr(dialplan))
1740 dialplan = this->_dialplan;
1741 if (!zstr(this->_caller_id_name) && zstr(cid_name))
1742 cid_name = this->_caller_id_name;
1743 if (!zstr(this->_caller_id_number) && zstr(cid_num))
1744 cid_num = this->_caller_id_number;
1745 if (!zstr(this->_ani) && zstr(ani))
1746 ani = this->_ani;
1747 if (!zstr(this->_aniii) && zstr(aniii))
1748 aniii = this->_aniii;
1749 if (!zstr(this->_rdnis) && zstr(rdnis))
1750 rdnis = this->_rdnis;
1751 if (!zstr(this->_context) && zstr(context))
1752 context = this->_context;
1753 if (!zstr(this->_username) && zstr(username))
1754 username = this->_username;
1755
1756 String::Utf8Value str(info[1]);
1757 dest = js_safe_str(*str);
1758
1759 if (!dest.c_str() || !strchr(dest.c_str(), '/')) {
1760 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid Channel String\n");
1761 return;
1762 }
1763
1764 if (info.Length() > 2) {
1765 String::Utf8Value strTmp(info[2]);
1766 tmp = *strTmp;
1767 if (!zstr(tmp)) {
1768 to = tmp;
1769 }
1770 }
1771
1772 if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
1773 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "OH OH no pool\n");
1774 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Could not create new pool"));
1775 return;
1776 }
1777
1778 caller_profile = switch_caller_profile_new(pool, username, dialplan, cid_name, cid_num, network_addr, ani, aniii, rdnis, "mod_v8", context, dest.c_str());
1779
1780 status =
1781 switch_ivr_originate(session, &peer_session, &this->_cause,
1782 dest.c_str(), to.length() > 0 ? atoi(to.c_str()) : 60, NULL, NULL, NULL, caller_profile, NULL, SOF_NONE, NULL, NULL);
1783
1784 if (status != SWITCH_STATUS_SUCCESS) {
1785 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot Create Outgoing Channel! [%s]\n", dest.c_str());
1786 return;
1787 }
1788
1789 this->_session = peer_session;
1790 switch_set_flag(this, S_HUP);
1791 info.GetReturnValue().Set(true);
1792 } else {
1793 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing Args\n");
1794 }
1795 }
1796
JS_SESSION_FUNCTION_IMPL(Bridge)1797 JS_SESSION_FUNCTION_IMPL(Bridge)
1798 {
1799 HandleScope handle_scope(info.GetIsolate());
1800 FSSession *jss_b = NULL;
1801 Handle<Object> obj_b;
1802 void *bp = NULL;
1803 switch_input_callback_function_t dtmf_func = NULL;
1804 FSInputCallbackState cb_state;
1805
1806 info.GetReturnValue().Set(false);
1807
1808 if (info.Length() > 0) {
1809 if (info[0]->IsObject()) {
1810 obj_b = Handle<Object>::Cast(info[0]);
1811
1812 if (!(jss_b = JSBase::GetInstance<FSSession>(obj_b))) {
1813 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "Cannot find session B"));
1814 return;
1815 }
1816 }
1817 }
1818
1819 if (!_session) {
1820 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "session A is not ready!"));
1821 return;
1822 }
1823
1824 if (!(jss_b && jss_b->_session)) {
1825 info.GetIsolate()->ThrowException(String::NewFromUtf8(info.GetIsolate(), "session B is not ready!"));
1826 return;
1827 }
1828
1829 if (info.Length() > 1) {
1830 Handle<Function> func = JSBase::GetFunctionFromArg(info.GetIsolate(), info[1]);
1831
1832 if (!func.IsEmpty()) {
1833 cb_state.function.Reset(info.GetIsolate(), func);
1834
1835 if (info.Length() > 2 && !info[2].IsEmpty()) {
1836 cb_state.arg.Reset(info.GetIsolate(), info[2]);
1837 }
1838
1839 cb_state.jss_a = this;
1840 cb_state.jss_b = jss_b;
1841 cb_state.session_obj_a.Reset(info.GetIsolate(), info.Holder());
1842 cb_state.session_obj_b.Reset(info.GetIsolate(), obj_b);
1843 cb_state.session_state = this;
1844 cb_state.context.Reset(info.GetIsolate(), info.GetIsolate()->GetCurrentContext());
1845
1846 dtmf_func = CollectInputCallback;
1847 bp = &cb_state;
1848 }
1849 }
1850
1851 JS_EXECUTE_LONG_RUNNING_C_CALL_WITH_UNLOCKER(switch_ivr_multi_threaded_bridge(_session, jss_b->_session, dtmf_func, bp, bp));
1852
1853 info.GetReturnValue().Set(true);
1854 }
1855
1856 static const js_function_t session_proc[] = {
1857 {"originate", FSSession::Originate}, // Deprecated - use constructor instead
1858 {"setCallerData", FSSession::SetCallerdata},
1859 {"setHangupHook", FSSession::SetHangupHook},
1860 {"setAutoHangup", FSSession::SetAutoHangup},
1861 {"sayPhrase", FSSession::SayPhrase},
1862 {"streamFile", FSSession::StreamFile},
1863 {"collectInput", FSSession::CollectInput},
1864 {"recordFile", FSSession::RecordFile},
1865 {"flushEvents", FSSession::FlushEvents},
1866 {"flushDigits", FSSession::FlushDigits},
1867 {"speak", FSSession::Speak},
1868 {"setVariable", FSSession::SetVariable},
1869 {"getVariable", FSSession::GetVariable},
1870 {"getDigits", FSSession::GetDigits},
1871 {"answer", FSSession::Answer},
1872 {"preAnswer", FSSession::PreAnswer},
1873 {"generateXmlCdr", FSSession::GenerateXmlCdr},
1874 {"ready", FSSession::Ready},
1875 {"answered", FSSession::Answered},
1876 {"mediaReady", FSSession::MediaReady},
1877 {"ringReady", FSSession::RingReady},
1878 {"waitForAnswer", FSSession::WaitForAnswer}, // Deprecated
1879 {"waitForMedia", FSSession::WaitForMedia}, // Deprecated
1880 {"getEvent", FSSession::GetEvent},
1881 {"sendEvent", FSSession::SendEvent},
1882 {"hangup", FSSession::Hangup},
1883 {"execute", FSSession::Execute},
1884 {"destroy", FSSession::Detach},
1885 {"sleep", FSSession::Sleep},
1886 {"bridge", FSSession::Bridge},
1887 {0}
1888 };
1889
1890 static const js_property_t session_prop[] = {
1891 {"name", FSSession::GetProperty, JSBase::DefaultSetProperty},
1892 {"state", FSSession::GetProperty, JSBase::DefaultSetProperty},
1893 {"dialplan", FSSession::GetProperty, JSBase::DefaultSetProperty},
1894 {"caller_id_name", FSSession::GetProperty, JSBase::DefaultSetProperty},
1895 {"caller_id_num", FSSession::GetProperty, JSBase::DefaultSetProperty},
1896 {"caller_id_number", FSSession::GetProperty, JSBase::DefaultSetProperty},
1897 {"network_addr", FSSession::GetProperty, JSBase::DefaultSetProperty},
1898 {"network_address", FSSession::GetProperty, JSBase::DefaultSetProperty},
1899 {"ani", FSSession::GetProperty, JSBase::DefaultSetProperty},
1900 {"aniii", FSSession::GetProperty, JSBase::DefaultSetProperty},
1901 {"destination", FSSession::GetProperty, JSBase::DefaultSetProperty},
1902 {"uuid", FSSession::GetProperty, JSBase::DefaultSetProperty},
1903 {"cause", FSSession::GetProperty, JSBase::DefaultSetProperty},
1904 {"causecode", FSSession::GetProperty, JSBase::DefaultSetProperty},
1905 {0}
1906 };
1907
1908 static const js_class_definition_t session_desc = {
1909 js_class_name,
1910 FSSession::Construct,
1911 session_proc,
1912 session_prop
1913 };
1914
GetClassDefinition()1915 const js_class_definition_t *FSSession::GetClassDefinition()
1916 {
1917 return &session_desc;
1918 }
1919
1920 /* For Emacs:
1921 * Local Variables:
1922 * mode:c
1923 * indent-tabs-mode:t
1924 * tab-width:4
1925 * c-basic-offset:4
1926 * End:
1927 * For VIM:
1928 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
1929 */
1930