diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index b67c388573..31f45a09ce 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -80,7 +80,9 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio unsigned int buflen, unsigned int maxdigits, const char *terminators, - char *terminator); + char *terminator, + unsigned int timeout, + unsigned int poll_channel); /*! \brief play a file from the disk to the session @@ -121,6 +123,18 @@ SWITCH_DECLARE(switch_status) switch_ivr_record_file(switch_core_session *sessio unsigned int buflen); +/*! + \brief Speak given text with given tts engine + \param session the session to speak on + \param voice_name the desired voice + \param timer_name optional timer to use for async behaviour + \param rate the sample rate + \param dtmf_callback code to execute if any dtmf is dialed during the audio + \param text the text to speak + \param buf an option data pointer to pass to the callback or a string to put encountered digits in + \param buflen the len of buf + \return SWITCH_STATUS_SUCCESS if all is well +*/ SWITCH_DECLARE(switch_status) switch_ivr_speak_text(switch_core_session *session, char *tts_name, char *voice_name, @@ -131,6 +145,17 @@ SWITCH_DECLARE(switch_status) switch_ivr_speak_text(switch_core_session *session void *buf, unsigned int buflen); +/*! + \brief Bridge Audio from one session to another + \param session one session + \param peer_session the other session + \param timelimit maximum number of seconds to wait for both channels to be answered + \return SWITCH_STATUS_SUCCESS if all is well +*/ +SWITCH_DECLARE(switch_status) switch_ivr_multi_threaded_bridge(switch_core_session *session, + switch_core_session *peer_session, + unsigned int timelimit); + /** @} */ #ifdef __cplusplus diff --git a/src/mod/applications/mod_bridgecall/mod_bridgecall.c b/src/mod/applications/mod_bridgecall/mod_bridgecall.c index a5c3ec210f..89051a02e4 100644 --- a/src/mod/applications/mod_bridgecall/mod_bridgecall.c +++ b/src/mod/applications/mod_bridgecall/mod_bridgecall.c @@ -36,159 +36,13 @@ static const char modname[] = "mod_bridgecall"; -struct audio_bridge_data { - switch_core_session *session_a; - switch_core_session *session_b; - int running; -}; - -static void *audio_bridge_thread(switch_thread *thread, void *obj) -{ - struct switch_core_thread_session *data = obj; - int *stream_id_p; - int stream_id = 0, ans_a = 0, ans_b = 0; - - switch_channel *chan_a, *chan_b; - switch_frame *read_frame; - switch_core_session *session_a, *session_b; - - session_a = data->objs[0]; - session_b = data->objs[1]; - - stream_id_p = data->objs[2]; - if (stream_id_p) { - stream_id = *stream_id_p; - } - - chan_a = switch_core_session_get_channel(session_a); - chan_b = switch_core_session_get_channel(session_b); - - - ans_a = switch_channel_test_flag(chan_a, CF_ANSWERED); - ans_b = switch_channel_test_flag(chan_b, CF_ANSWERED); - - - while (data->running > 0) { - switch_channel_state b_state = switch_channel_get_state(chan_b); - - switch (b_state) { - case CS_HANGUP: - data->running = -1; - continue; - break; - default: - break; - } - - /* If this call is running on early media and it answers for real, pass it along... */ - if (!ans_b && switch_channel_test_flag(chan_a, CF_ANSWERED)) { - if (!switch_channel_test_flag(chan_b, CF_ANSWERED)) { - switch_channel_answer(chan_b); - } - ans_b++; - } - - if (!ans_a && switch_channel_test_flag(chan_b, CF_ANSWERED)) { - if (!switch_channel_test_flag(chan_a, CF_ANSWERED)) { - switch_channel_answer(chan_a); - } - ans_a++; - } - - /* if 1 channel has DTMF pass it to the other */ - if (switch_channel_has_dtmf(chan_a)) { - char dtmf[128]; - switch_channel_dequeue_dtmf(chan_a, dtmf, sizeof(dtmf)); - switch_core_session_send_dtmf(session_b, dtmf); - } - - /* read audio from 1 channel and write it to the other */ - if (switch_core_session_read_frame(session_a, &read_frame, -1, stream_id) == SWITCH_STATUS_SUCCESS - && read_frame->datalen) { - if (switch_core_session_write_frame(session_b, read_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) { - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "write: %s Bad Frame.... Bubye!\n", switch_channel_get_name(chan_b)); - data->running = -1; - } - } else { - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "read: %s Bad Frame.... Bubye!\n", switch_channel_get_name(chan_a)); - data->running = -1; - } - - } - switch_channel_hangup(chan_b); - data->running = 0; - - return NULL; -} - - -static switch_status audio_bridge_on_hangup(switch_core_session *session) -{ - switch_core_session *other_session; - switch_channel *channel = NULL, *other_channel = NULL; - - channel = switch_core_session_get_channel(session); - assert(channel != NULL); - - other_session = switch_channel_get_private(channel); - assert(other_session != NULL); - - other_channel = switch_core_session_get_channel(other_session); - assert(other_channel != NULL); - - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CUSTOM HANGUP %s kill %s\n", switch_channel_get_name(channel), - switch_channel_get_name(other_channel)); - - switch_core_session_kill_channel(other_session, SWITCH_SIG_KILL); - switch_core_session_kill_channel(session, SWITCH_SIG_KILL); - - return SWITCH_STATUS_SUCCESS; -} - -static switch_status audio_bridge_on_ring(switch_core_session *session) -{ - switch_channel *channel = NULL; - - channel = switch_core_session_get_channel(session); - assert(channel != NULL); - - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CUSTOM RING\n"); - - /* put the channel in a passive state so we can loop audio to it */ - if (switch_channel_test_flag(channel, CF_OUTBOUND)) { - switch_channel_set_state(channel, CS_TRANSMIT); - return SWITCH_STATUS_FALSE; - } - - - return SWITCH_STATUS_SUCCESS; -} - -static const switch_state_handler_table audio_bridge_peer_state_handlers = { - /*.on_init */ NULL, - /*.on_ring */ audio_bridge_on_ring, - /*.on_execute */ NULL, - /*.on_hangup */ audio_bridge_on_hangup, - /*.on_loopback */ NULL, - /*.on_transmit */ NULL -}; - -static const switch_state_handler_table audio_bridge_caller_state_handlers = { - /*.on_init */ NULL, - /*.on_ring */ NULL, - /*.on_execute */ NULL, - /*.on_hangup */ audio_bridge_on_hangup, - /*.on_loopback */ NULL, - /*.on_transmit */ NULL -}; - static void audio_bridge_function(switch_core_session *session, char *data) { - switch_channel *caller_channel, *peer_channel; + switch_channel *caller_channel; switch_core_session *peer_session; switch_caller_profile *caller_profile, *caller_caller_profile; char chan_type[128] = { '\0' }, *chan_data; - int timelimit = 60; /* probably a useful option to pass in when there's time */ + unsigned int timelimit = 60; /* probably a useful option to pass in when there's time */ caller_channel = switch_core_session_get_channel(session); assert(caller_channel != NULL); @@ -211,76 +65,12 @@ static void audio_bridge_function(switch_core_session *session, char *data) if (switch_core_session_outgoing_channel(session, chan_type, caller_profile, &peer_session) != SWITCH_STATUS_SUCCESS) { - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "DOH!\n"); + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Cannot Create Outgoing Channel!\n"); switch_channel_hangup(caller_channel); return; } else { - struct switch_core_thread_session this_audio_thread, other_audio_thread; - time_t start; - int stream_id = 0; - - peer_channel = switch_core_session_get_channel(peer_session); - memset(&other_audio_thread, 0, sizeof(other_audio_thread)); - memset(&this_audio_thread, 0, sizeof(this_audio_thread)); - other_audio_thread.objs[0] = session; - other_audio_thread.objs[1] = peer_session; - other_audio_thread.objs[2] = &stream_id; - other_audio_thread.running = 5; - - this_audio_thread.objs[0] = peer_session; - this_audio_thread.objs[1] = session; - this_audio_thread.objs[2] = &stream_id; - this_audio_thread.running = 2; - - - switch_channel_set_private(caller_channel, peer_session); - switch_channel_set_private(peer_channel, session); - switch_channel_add_state_handler(caller_channel, &audio_bridge_caller_state_handlers); - switch_channel_add_state_handler(peer_channel, &audio_bridge_peer_state_handlers); - switch_core_session_thread_launch(peer_session); - - for (;;) { - int state = switch_channel_get_state(peer_channel); - if (state > CS_RING) { - break; - } - switch_yield(1000); - } - - time(&start); - while (switch_channel_get_state(caller_channel) == CS_EXECUTE && - switch_channel_get_state(peer_channel) == CS_TRANSMIT && - !switch_channel_test_flag(peer_channel, CF_ANSWERED) && - !switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA) && - ((time(NULL) - start) < timelimit)) { - switch_yield(20000); - } - - if (switch_channel_test_flag(peer_channel, CF_ANSWERED)) { - switch_channel_answer(caller_channel); - } - - if (switch_channel_test_flag(peer_channel, CF_ANSWERED) || - switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA)) { - - switch_core_session_launch_thread(session, audio_bridge_thread, (void *) &other_audio_thread); - audio_bridge_thread(NULL, (void *) &this_audio_thread); - - switch_channel_hangup(peer_channel); - if (other_audio_thread.running > 0) { - other_audio_thread.running = -1; - /* wait for the other audio thread */ - while (other_audio_thread.running) { - switch_yield(1000); - } - } - - - } + switch_ivr_multi_threaded_bridge(session, peer_session, timelimit); } - - switch_channel_hangup(caller_channel); - switch_channel_hangup(peer_channel); } diff --git a/src/mod/applications/mod_ivrtest/mod_ivrtest.c b/src/mod/applications/mod_ivrtest/mod_ivrtest.c index f617b1d9b0..4c5fdc3f15 100644 --- a/src/mod/applications/mod_ivrtest/mod_ivrtest.c +++ b/src/mod/applications/mod_ivrtest/mod_ivrtest.c @@ -157,7 +157,7 @@ static void ivrtest_function(switch_core_session *session, char *data) } } - if (switch_ivr_collect_digits_count(session, buf, sizeof(buf), 10, "#*", &term) != SWITCH_STATUS_SUCCESS) { + if (switch_ivr_collect_digits_count(session, buf, sizeof(buf), 10, "#*", &term, 10000, 1) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel); break; } diff --git a/src/mod/endpoints/mod_iax/mod_iax.c b/src/mod/endpoints/mod_iax/mod_iax.c index de72c57b73..a1952674fb 100644 --- a/src/mod/endpoints/mod_iax/mod_iax.c +++ b/src/mod/endpoints/mod_iax/mod_iax.c @@ -857,7 +857,9 @@ SWITCH_MOD_DECLARE(switch_status) switch_module_runtime(void) int refresh; struct iax_event *iaxevent = NULL; switch_event *s_event; - load_config(); + if (load_config() != SWITCH_STATUS_SUCCESS) { + return SWITCH_STATUS_TERM; + } if (globals.debug) { iax_enable_debug(); diff --git a/src/mod/languages/mod_spidermonkey/Makefile b/src/mod/languages/mod_spidermonkey/Makefile index fb0ae3d9f4..e4933d5bcc 100644 --- a/src/mod/languages/mod_spidermonkey/Makefile +++ b/src/mod/languages/mod_spidermonkey/Makefile @@ -69,6 +69,7 @@ all: depends $(MODNAME).so depends: MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) mozilla --prefix=$(PREFIX) + MAKE=$(MAKE) $(BASE)/build/buildlib.sh $(BASE) install curl-7.15.2.tar.gz --prefix=$(PREFIX) $(MODNAME).so: $(MODNAME).c $(CC) $(CFLAGS) -fPIC -c $(MODNAME).c -o $(MODNAME).o diff --git a/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c index b84351f7e1..c78d902d02 100644 --- a/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c +++ b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c @@ -29,6 +29,8 @@ * mod_spidermonkey.c -- Javascript Module * */ +#define HAVE_CURL + #include "jstypes.h" #include "jsarena.h" #include "jsutil.h" @@ -47,6 +49,9 @@ #include "jsscript.h" #include #include +#ifdef HAVE_CURL +#include +#endif #ifdef _MSC_VER #pragma warning(disable: 4311) @@ -68,7 +73,6 @@ static struct { } globals; -//extern JSClass global_class; static JSClass global_class = { "Global", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, @@ -303,11 +307,12 @@ static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *arg char *tts_name = NULL; char *voice_name = NULL; char *text = NULL; + char *dtmf_callback = NULL; switch_codec *codec; - char buf[10] = ""; void *bp = NULL; int len = 0; - //switch_dtmf_callback_function dtmf_func = NULL; + struct dtmf_callback_state cb_state = {0}; + switch_dtmf_callback_function dtmf_func = NULL; channel = switch_core_session_get_channel(jss->session); assert(channel != NULL); @@ -322,8 +327,18 @@ static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *arg text = JS_GetStringBytes(JS_ValueToString(cx, argv[2])); } if (argc > 3) { - bp = buf; - len = sizeof(buf); + dtmf_callback = JS_GetStringBytes(JS_ValueToString(cx, argv[3])); + if (switch_strlen_zero(dtmf_callback)) { + dtmf_callback = NULL; + } else { + memset(&cb_state, 0, sizeof(cb_state)); + switch_copy_string(cb_state.code_buffer, dtmf_callback, sizeof(cb_state.code_buffer)); + cb_state.code_buffer_len = strlen(cb_state.code_buffer); + cb_state.session_state = jss; + dtmf_func = js_dtmf_callback; + bp = &cb_state; + len = sizeof(cb_state); + } } if (!tts_name && text) { @@ -336,15 +351,14 @@ static JSBool session_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *arg voice_name && strlen(voice_name) ? voice_name : NULL, NULL, codec->implementation->samples_per_second, - NULL, + dtmf_func, text, bp, len); - if(len) { - *rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) ); - } - return JS_TRUE; - + + *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, cb_state.ret_buffer)); + + return (switch_channel_get_state(channel) == CS_EXECUTE) ? JS_TRUE : JS_FALSE; } static JSBool session_get_digits(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) @@ -353,15 +367,24 @@ static JSBool session_get_digits(JSContext *cx, JSObject *obj, uintN argc, jsval char *terminators = NULL; char *buf; int digits; - + int32 timeout = 5000; + int32 poll_chan = 1; + + if (argc > 0) { char term; digits = atoi(JS_GetStringBytes(JS_ValueToString(cx, argv[0]))); if (argc > 1) { terminators = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); } + if (argc > 2) { + JS_ValueToInt32(cx, argv[2], &timeout); + } + if (argc > 3) { + JS_ValueToInt32(cx, argv[3], &poll_chan); + } buf = switch_core_session_alloc(jss->session, digits); - switch_ivr_collect_digits_count(jss->session, buf, digits, digits, terminators, &term); + switch_ivr_collect_digits_count(jss->session, buf, digits, digits, terminators, &term, timeout, poll_chan ? SWITCH_TRUE : SWITCH_FALSE); *rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) ); return JS_TRUE; } @@ -381,6 +404,95 @@ static JSBool session_answer(JSContext *cx, JSObject *obj, uintN argc, jsval *ar return JS_TRUE; } +#ifdef HAVE_CURL + +struct config_data { + JSContext *cx; + JSObject *obj; + char *name; +}; + +static size_t realtime_callback(void *ptr, size_t size, size_t nmemb, void *data) +{ + register int realsize = size * nmemb; + char *line, lineb[2048], *nextline = NULL, *val = NULL, *p = NULL; + jsval rval; + struct config_data *config_data = data; + char code[256]; + + if (config_data->name) { + switch_copy_string(lineb, (char *) ptr, sizeof(lineb)); + line = lineb; + while (line) { + if ((nextline = strchr(line, '\n'))) { + *nextline = '\0'; + nextline++; + } + + if ((val = strchr(line, '='))) { + *val = '\0'; + val++; + if (val[0] == '>') { + *val = '\0'; + val++; + } + + for (p = line; p && *p == ' '; p++); + line = p; + for (p=line+strlen(line)-1;*p == ' '; p--) + *p = '\0'; + for (p = val; p && *p == ' '; p++); + val = p; + for (p=val+strlen(val)-1;*p == ' '; p--) + *p = '\0'; + + snprintf(code, sizeof(code), "~%s[\"%s\"] = \"%s\"", config_data->name, line, val); + eval_some_js(code, config_data->cx, config_data->obj, &rval); + + } + + line = nextline; + } + } + return realsize; + +} + + +static JSBool js_fetchurl(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *url = NULL, *name = NULL; + CURL *curl_handle = NULL; + struct config_data config_data; + + if ( argc > 0 && (url = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) { + if (argc > 1) + name = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); + curl_global_init(CURL_GLOBAL_ALL); + curl_handle = curl_easy_init(); + if (!strncasecmp(url, "https", 5)) { + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0); + } + config_data.cx = cx; + config_data.obj = obj; + if (name) + config_data.name = name; + curl_easy_setopt(curl_handle, CURLOPT_URL, url); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, realtime_callback); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)&config_data); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "asterisk-js/1.0"); + curl_easy_perform(curl_handle); + curl_easy_cleanup(curl_handle); + } else { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Error!\n"); + return JS_FALSE; + } + + return JS_TRUE; +} +#endif + /* Session Object */ /*********************************************************************************/ enum session_tinyid { @@ -643,7 +755,6 @@ static JSBool teletone_generate(JSContext *cx, JSObject *obj, uintN argc, jsval write_frame.samples = write_frame.datalen / 2; for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) { - if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) { switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Bad Write\n"); break; @@ -651,7 +762,9 @@ static JSBool teletone_generate(JSContext *cx, JSObject *obj, uintN argc, jsval } } - switch_core_thread_session_end(&thread_session); + if (tto->timer) { + switch_core_thread_session_end(&thread_session); + } return JS_TRUE; } @@ -704,9 +817,158 @@ static JSBool js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsva return JS_FALSE; } +static JSBool js_include(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *code; + if ( argc > 0 && (code = JS_GetStringBytes(JS_ValueToString(cx, argv[0])))) { + eval_some_js(code, cx, obj, rval); + return JS_TRUE; + } + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Invalid Arguements\n"); + return JS_FALSE; +} + +#define B64BUFFLEN 1024 +static const char c64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static int write_buf(int fd, char *buf) { + + size_t len = strlen(buf); + if (fd && write(fd, buf, len) != len) { + close(fd); + return 0; + } + + return 1; +} + +static JSBool js_email(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + char *to = NULL, *from = NULL, *headers, *body = NULL, *file = NULL; + char *bound = "XXXX_boundary_XXXX"; + char filename[80], buf[B64BUFFLEN]; + int fd = 0, ifd = 0; + int x=0, y=0, bytes=0, ilen=0; + unsigned int b=0, l=0; + unsigned char in[B64BUFFLEN]; + unsigned char out[B64BUFFLEN+512]; + char *path = NULL; + + + if ( + argc > 3 && + (from = JS_GetStringBytes(JS_ValueToString(cx, argv[0]))) && + (to = JS_GetStringBytes(JS_ValueToString(cx, argv[1]))) && + (headers = JS_GetStringBytes(JS_ValueToString(cx, argv[2]))) && + (body = JS_GetStringBytes(JS_ValueToString(cx, argv[3]))) + ) { + if ( argc > 4) + file = JS_GetStringBytes(JS_ValueToString(cx, argv[4])); + + + snprintf(filename, 80, "/tmp/mail.%ld", switch_time_now()); + + if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644))) { + if (file) { + path = file; + if ((ifd = open(path, O_RDONLY)) < 1) { + return JS_FALSE; + } + + snprintf(buf, B64BUFFLEN, "MIME-Version: 1.0\nContent-Type: multipart/mixed; boundary=\"%s\"\n", bound); + if (!write_buf(fd, buf)) { + return JS_FALSE; + } + } + + if (!write_buf(fd, headers)) + return JS_FALSE; + + if (!write_buf(fd, "\n\n")) + return JS_FALSE; + + if (file) { + snprintf(buf, B64BUFFLEN, "--%s\nContent-Type: text/plain\n\n", bound); + if (!write_buf(fd, buf)) + return JS_FALSE; + } + + if (!write_buf(fd, body)) + return JS_FALSE; + + if (file) { + snprintf(buf, B64BUFFLEN, "\n\n--%s\nContent-Type: application/octet-stream\nContent-Transfer-Encoding: base64\nContent-Description: Sound attachment.\nContent-Disposition: attachment; filename=\"%s\"\n\n", bound, file); + if (!write_buf(fd, buf)) + return JS_FALSE; + + while((ilen=read(ifd, in, B64BUFFLEN))) { + for (x=0;x= 6) { + out[bytes++] = c64[(b>>(l-=6))%64]; + if (++y!=72) + continue; + out[bytes++] = '\n'; + y=0; + } + } + if (write(fd, &out, bytes) != bytes) { + return -1; + } else + bytes=0; + + } + + if (l > 0) + out[bytes++] = c64[((b%16)<<(6-l))%64]; + if (l != 0) while (l < 6) + out[bytes++] = '=', l += 2; + + if (write(fd, &out, bytes) != bytes) { + return -1; + } + + } + + + + if (file) { + snprintf(buf, B64BUFFLEN, "\n\n--%s--\n.\n", bound); + if (!write_buf(fd, buf)) + return JS_FALSE; + } + } + + if (fd) + close(fd); + if (ifd) + close(ifd); + + snprintf(buf, B64BUFFLEN, "/bin/cat %s | /usr/sbin/sendmail -tf \"%s\" %s", filename, from, to); + system(buf); + unlink(filename); + + + if (file) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Emailed file [%s] to [%s]\n", filename, to); + } else { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Emailed data to [%s]\n", to); + } + return JS_TRUE; + } + + + return JS_FALSE; +} static JSFunctionSpec fs_functions[] = { {"console_log", js_log, 2}, + {"include", js_include, 1}, + {"email", js_email, 2}, +#ifdef HAVE_CURL + {"fetchURL", js_fetchurl, 1}, +#endif {0} }; diff --git a/src/switch_ivr.c b/src/switch_ivr.c index d1693e3cf6..81a546223e 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -77,27 +77,44 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio unsigned int buflen, unsigned int maxdigits, const char *terminators, - char *terminator) + char *terminator, + unsigned int timeout, + unsigned int poll_channel + ) { unsigned int i = 0, x = (unsigned int) strlen(buf); switch_channel *channel; switch_status status = SWITCH_STATUS_SUCCESS; - + switch_time_t started = 0; + unsigned int elapsed; + channel = switch_core_session_get_channel(session); assert(channel != NULL); *terminator = '\0'; - for (i = 0 ; i < x; i++) { - if (strchr(terminators, buf[i])) { - *terminator = buf[i]; - return SWITCH_STATUS_SUCCESS; + if (terminators) { + for (i = 0 ; i < x; i++) { + if (strchr(terminators, buf[i])) { + *terminator = buf[i]; + return SWITCH_STATUS_SUCCESS; + } } } + if (timeout) { + started = switch_time_now(); + } while (switch_channel_get_state(channel) == CS_EXECUTE) { switch_frame *read_frame; + if (timeout) { + elapsed = (switch_time_now() - started) / 1000; + if (elapsed >= timeout) { + break; + } + } + if (switch_channel_has_dtmf(channel)) { char dtmf[128]; switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf)); @@ -116,9 +133,12 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio } } } - - if ((status = switch_core_session_read_frame(session, &read_frame, -1, 0)) != SWITCH_STATUS_SUCCESS) { - break; + if (poll_channel) { + if ((status = switch_core_session_read_frame(session, &read_frame, -1, 0)) != SWITCH_STATUS_SUCCESS) { + break; + } + } else { + switch_yield(1000); } } @@ -639,7 +659,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_speak_text(switch_core_session *session } } - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "done playing file\n"); + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "done speaking text\n"); switch_core_codec_destroy(&codec); flags = 0; switch_core_codec_destroy(&codec); @@ -654,4 +674,225 @@ SWITCH_DECLARE(switch_status) switch_ivr_speak_text(switch_core_session *session } +/* Bridge Related Stuff*/ +/*********************************************************************************/ +struct audio_bridge_data { + switch_core_session *session_a; + switch_core_session *session_b; + int running; +}; +static void *audio_bridge_thread(switch_thread *thread, void *obj) +{ + struct switch_core_thread_session *data = obj; + int *stream_id_p; + int stream_id = 0, ans_a = 0, ans_b = 0; + + switch_channel *chan_a, *chan_b; + switch_frame *read_frame; + switch_core_session *session_a, *session_b; + + session_a = data->objs[0]; + session_b = data->objs[1]; + + stream_id_p = data->objs[2]; + if (stream_id_p) { + stream_id = *stream_id_p; + } + + chan_a = switch_core_session_get_channel(session_a); + chan_b = switch_core_session_get_channel(session_b); + + + ans_a = switch_channel_test_flag(chan_a, CF_ANSWERED); + ans_b = switch_channel_test_flag(chan_b, CF_ANSWERED); + + + while (data->running > 0) { + switch_channel_state b_state = switch_channel_get_state(chan_b); + + switch (b_state) { + case CS_HANGUP: + data->running = -1; + continue; + break; + default: + break; + } + + /* If this call is running on early media and it answers for real, pass it along... */ + if (!ans_b && switch_channel_test_flag(chan_a, CF_ANSWERED)) { + if (!switch_channel_test_flag(chan_b, CF_ANSWERED)) { + switch_channel_answer(chan_b); + } + ans_b++; + } + + if (!ans_a && switch_channel_test_flag(chan_b, CF_ANSWERED)) { + if (!switch_channel_test_flag(chan_a, CF_ANSWERED)) { + switch_channel_answer(chan_a); + } + ans_a++; + } + + /* if 1 channel has DTMF pass it to the other */ + if (switch_channel_has_dtmf(chan_a)) { + char dtmf[128]; + switch_channel_dequeue_dtmf(chan_a, dtmf, sizeof(dtmf)); + switch_core_session_send_dtmf(session_b, dtmf); + } + + /* read audio from 1 channel and write it to the other */ + if (switch_core_session_read_frame(session_a, &read_frame, -1, stream_id) == SWITCH_STATUS_SUCCESS + && read_frame->datalen) { + if (switch_core_session_write_frame(session_b, read_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "write: %s Bad Frame.... Bubye!\n", switch_channel_get_name(chan_b)); + data->running = -1; + } + } else { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "read: %s Bad Frame.... Bubye!\n", switch_channel_get_name(chan_a)); + data->running = -1; + } + + } + switch_channel_hangup(chan_b); + data->running = 0; + + return NULL; +} + + +static switch_status audio_bridge_on_hangup(switch_core_session *session) +{ + switch_core_session *other_session; + switch_channel *channel = NULL, *other_channel = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + other_session = switch_channel_get_private(channel); + assert(other_session != NULL); + + other_channel = switch_core_session_get_channel(other_session); + assert(other_channel != NULL); + + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CUSTOM HANGUP %s kill %s\n", switch_channel_get_name(channel), + switch_channel_get_name(other_channel)); + + switch_core_session_kill_channel(other_session, SWITCH_SIG_KILL); + switch_core_session_kill_channel(session, SWITCH_SIG_KILL); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status audio_bridge_on_ring(switch_core_session *session) +{ + switch_channel *channel = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "CUSTOM RING\n"); + + /* put the channel in a passive state so we can loop audio to it */ + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + switch_channel_set_state(channel, CS_TRANSMIT); + return SWITCH_STATUS_FALSE; + } + + + return SWITCH_STATUS_SUCCESS; +} + +static const switch_state_handler_table audio_bridge_peer_state_handlers = { + /*.on_init */ NULL, + /*.on_ring */ audio_bridge_on_ring, + /*.on_execute */ NULL, + /*.on_hangup */ audio_bridge_on_hangup, + /*.on_loopback */ NULL, + /*.on_transmit */ NULL +}; + +static const switch_state_handler_table audio_bridge_caller_state_handlers = { + /*.on_init */ NULL, + /*.on_ring */ NULL, + /*.on_execute */ NULL, + /*.on_hangup */ audio_bridge_on_hangup, + /*.on_loopback */ NULL, + /*.on_transmit */ NULL +}; + +SWITCH_DECLARE(switch_status) switch_ivr_multi_threaded_bridge(switch_core_session *session, + switch_core_session *peer_session, + unsigned int timelimit) +{ + struct switch_core_thread_session this_audio_thread, other_audio_thread; + switch_channel *caller_channel, *peer_channel; + time_t start; + int stream_id = 0; + + caller_channel = switch_core_session_get_channel(session); + assert(caller_channel != NULL); + + peer_channel = switch_core_session_get_channel(peer_session); + assert(peer_channel != NULL); + + memset(&other_audio_thread, 0, sizeof(other_audio_thread)); + memset(&this_audio_thread, 0, sizeof(this_audio_thread)); + other_audio_thread.objs[0] = session; + other_audio_thread.objs[1] = peer_session; + other_audio_thread.objs[2] = &stream_id; + other_audio_thread.running = 5; + + this_audio_thread.objs[0] = peer_session; + this_audio_thread.objs[1] = session; + this_audio_thread.objs[2] = &stream_id; + this_audio_thread.running = 2; + + + switch_channel_set_private(caller_channel, peer_session); + switch_channel_set_private(peer_channel, session); + switch_channel_add_state_handler(caller_channel, &audio_bridge_caller_state_handlers); + switch_channel_add_state_handler(peer_channel, &audio_bridge_peer_state_handlers); + switch_core_session_thread_launch(peer_session); + + for (;;) { + int state = switch_channel_get_state(peer_channel); + if (state > CS_RING) { + break; + } + switch_yield(1000); + } + + time(&start); + while (switch_channel_get_state(caller_channel) == CS_EXECUTE && + switch_channel_get_state(peer_channel) == CS_TRANSMIT && + !switch_channel_test_flag(peer_channel, CF_ANSWERED) && + !switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA) && + ((time(NULL) - start) < timelimit)) { + switch_yield(20000); + } + + if (switch_channel_test_flag(peer_channel, CF_ANSWERED)) { + switch_channel_answer(caller_channel); + } + + if (switch_channel_test_flag(peer_channel, CF_ANSWERED) || + switch_channel_test_flag(peer_channel, CF_EARLY_MEDIA)) { + + switch_core_session_launch_thread(session, audio_bridge_thread, (void *) &other_audio_thread); + audio_bridge_thread(NULL, (void *) &this_audio_thread); + + switch_channel_hangup(peer_channel); + if (other_audio_thread.running > 0) { + other_audio_thread.running = -1; + /* wait for the other audio thread */ + while (other_audio_thread.running) { + switch_yield(1000); + } + } + + + } + return SWITCH_STATUS_SUCCESS; +}