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