#include "asterisk.h"
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/linkedlists.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/tcptls.h"
#include "asterisk/astobj2.h"
Go to the source code of this file.
Data Structures | |
struct | gen_state |
struct | ivr_localuser |
struct | ivr_localuser::finishlist |
struct | ivr_localuser::playlist |
struct | playlist_entry |
Defines | |
#define | ast_chan_log(level, channel, format,) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__) |
Enumerations | |
enum | { noanswer = (1 << 0), ignore_hangup = (1 << 1), run_dead = (1 << 2) } |
Functions | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static int | app_exec (struct ast_channel *chan, void *data) |
static void | ast_eivr_getvariable (struct ast_channel *chan, char *data, char *outbuf, int outbuflen) |
static void | ast_eivr_setvariable (struct ast_channel *chan, char *data) |
static int | eivr_comm (struct ast_channel *chan, struct ivr_localuser *u, int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, const struct ast_str *args, const struct ast_flags flags) |
int | eivr_connect_socket (struct ast_channel *chan, const char *host, int port) |
static void * | gen_alloc (struct ast_channel *chan, void *params) |
static void | gen_closestream (struct gen_state *state) |
static int | gen_generate (struct ast_channel *chan, void *data, int len, int samples) |
static int | gen_nextfile (struct gen_state *state) |
static struct ast_frame * | gen_readframe (struct gen_state *state) |
static void | gen_release (struct ast_channel *chan, void *data) |
static int | load_module (void) |
static struct playlist_entry * | make_entry (const char *filename) |
static void | send_eivr_event (FILE *handle, const char event, const char *data, const struct ast_channel *chan) |
static int | unload_module (void) |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "External IVR Interface Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } |
static const char * | app = "ExternalIVR" |
static struct ast_app_option | app_opts [128] = { [ 'n' ] = { .flag = noanswer }, [ 'i' ] = { .flag = ignore_hangup }, [ 'd' ] = { .flag = run_dead },} |
static struct ast_module_info * | ast_module_info = &__mod_info |
static const char * | descrip |
static struct ast_generator | gen |
enum { ... } | options_flags |
static const char * | synopsis = "Interfaces with an external IVR application" |
Definition in file app_externalivr.c.
#define ast_chan_log | ( | level, | |||
channel, | |||||
format | ) | ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__) |
Definition at line 72 of file app_externalivr.c.
Referenced by app_exec(), eivr_comm(), gen_generate(), and gen_nextfile().
anonymous enum |
Definition at line 74 of file app_externalivr.c.
00074 { 00075 noanswer = (1 << 0), 00076 ignore_hangup = (1 << 1), 00077 run_dead = (1 << 2), 00078 } options_flags;
static void __reg_module | ( | void | ) | [static] |
Definition at line 807 of file app_externalivr.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 807 of file app_externalivr.c.
static int app_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 314 of file app_externalivr.c.
References ast_channel::_state, ivr_localuser::abort_current_sound, ast_activate_generator(), ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_chan_log, ast_debug, AST_DECLARE_APP_ARGS, AST_LIST_HEAD_INIT_VALUE, ast_log(), AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_str_append(), ast_str_create(), ast_str_reset(), ast_strdupa, ast_strlen_zero(), ast_test_flag, buf, chan, ivr_localuser::chan, ast_flags::flags, gen, ivr_localuser::gen_active, hostname, ignore_hangup, LOG_ERROR, LOG_WARNING, noanswer, option_debug, ivr_localuser::playlist, run_dead, and s.
00315 { 00316 struct ast_flags flags = { 0, }; 00317 char *opts[0]; 00318 struct playlist_entry *entry; 00319 int child_stdin[2] = { -1, -1 }; 00320 int child_stdout[2] = { -1, -1 }; 00321 int child_stderr[2] = { -1, -1 }; 00322 int res = -1; 00323 int pid; 00324 00325 char hostname[1024]; 00326 char *port_str = NULL; 00327 int port = 0; 00328 struct ast_tcptls_session_instance *ser = NULL; 00329 00330 struct ivr_localuser foo = { 00331 .playlist = AST_LIST_HEAD_INIT_VALUE, 00332 .finishlist = AST_LIST_HEAD_INIT_VALUE, 00333 .gen_active = 0, 00334 }; 00335 struct ivr_localuser *u = &foo; 00336 00337 char *buf; 00338 int j; 00339 char *s, **app_args, *e; 00340 struct ast_str *pipe_delim_args = ast_str_create(100); 00341 00342 AST_DECLARE_APP_ARGS(eivr_args, 00343 AST_APP_ARG(cmd)[32]; 00344 ); 00345 AST_DECLARE_APP_ARGS(application_args, 00346 AST_APP_ARG(cmd)[32]; 00347 ); 00348 00349 u->abort_current_sound = 0; 00350 u->chan = chan; 00351 00352 if (ast_strlen_zero(data)) { 00353 ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n"); 00354 return -1; 00355 } 00356 00357 buf = ast_strdupa(data); 00358 AST_STANDARD_APP_ARGS(eivr_args, buf); 00359 00360 if ((s = strchr(eivr_args.cmd[0], '('))) { 00361 s[0] = ','; 00362 if (( e = strrchr(s, ')')) ) { 00363 *e = '\0'; 00364 } else { 00365 ast_log(LOG_ERROR, "Parse error, no closing paren?\n"); 00366 } 00367 AST_STANDARD_APP_ARGS(application_args, eivr_args.cmd[0]); 00368 app_args = application_args.argv; 00369 00370 /* Put the application + the arguments in a | delimited list */ 00371 ast_str_reset(pipe_delim_args); 00372 for (j = 0; application_args.cmd[j] != NULL; j++) { 00373 ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]); 00374 } 00375 00376 /* Parse the ExternalIVR() arguments */ 00377 if (option_debug) 00378 ast_debug(1, "Parsing options from: [%s]\n", eivr_args.cmd[1]); 00379 ast_app_parse_options(app_opts, &flags, opts, eivr_args.cmd[1]); 00380 if (option_debug) { 00381 if (ast_test_flag(&flags, noanswer)) 00382 ast_debug(1, "noanswer is set\n"); 00383 if (ast_test_flag(&flags, ignore_hangup)) 00384 ast_debug(1, "ignore_hangup is set\n"); 00385 if (ast_test_flag(&flags, run_dead)) 00386 ast_debug(1, "run_dead is set\n"); 00387 } 00388 00389 } else { 00390 app_args = eivr_args.argv; 00391 for (j = 0; eivr_args.cmd[j] != NULL; j++) { 00392 ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", eivr_args.cmd[j]); 00393 } 00394 } 00395 00396 if (ast_strlen_zero(data)) { 00397 ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n"); 00398 return -1; 00399 } 00400 00401 if (!(ast_test_flag(&flags, noanswer))) { 00402 ast_chan_log(LOG_WARNING, chan, "Answering channel and starting generator\n"); 00403 if (chan->_state != AST_STATE_UP) { 00404 if (ast_test_flag(&flags, run_dead)) { 00405 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00406 goto exit; 00407 } 00408 ast_answer(chan); 00409 } 00410 if (ast_activate_generator(chan, &gen, u) < 0) { 00411 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); 00412 goto exit; 00413 } else { 00414 u->gen_active = 1; 00415 } 00416 } 00417 00418 if (!strncmp(app_args[0], "ivr://", 6)) { 00419 struct ast_tcptls_session_args ivr_desc = { 00420 .accept_fd = -1, 00421 .name = "IVR", 00422 }; 00423 struct ast_hostent hp; 00424 00425 /*communicate through socket to server*/ 00426 ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]); 00427 ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname)); 00428 if ((port_str = strchr(hostname, ':')) != NULL) { 00429 port_str[0] = 0; 00430 port_str += 1; 00431 port = atoi(port_str); 00432 } 00433 if (!port) { 00434 port = 2949; /* default port, if one is not provided */ 00435 } 00436 00437 ast_gethostbyname(hostname, &hp); 00438 ivr_desc.local_address.sin_family = AF_INET; 00439 ivr_desc.local_address.sin_port = htons(port); 00440 memcpy(&ivr_desc.local_address.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length); 00441 if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) { 00442 goto exit; 00443 } 00444 res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, pipe_delim_args, flags); 00445 00446 } else { 00447 if (pipe(child_stdin)) { 00448 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno)); 00449 goto exit; 00450 } 00451 if (pipe(child_stdout)) { 00452 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno)); 00453 goto exit; 00454 } 00455 if (pipe(child_stderr)) { 00456 ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno)); 00457 goto exit; 00458 } 00459 00460 pid = ast_safe_fork(0); 00461 if (pid < 0) { 00462 ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno)); 00463 goto exit; 00464 } 00465 00466 if (!pid) { 00467 /* child process */ 00468 if (ast_opt_high_priority) 00469 ast_set_priority(0); 00470 00471 dup2(child_stdin[0], STDIN_FILENO); 00472 dup2(child_stdout[1], STDOUT_FILENO); 00473 dup2(child_stderr[1], STDERR_FILENO); 00474 ast_close_fds_above_n(STDERR_FILENO); 00475 execv(app_args[0], app_args); 00476 fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno)); 00477 _exit(1); 00478 } else { 00479 /* parent process */ 00480 close(child_stdin[0]); 00481 child_stdin[0] = -1; 00482 close(child_stdout[1]); 00483 child_stdout[1] = -1; 00484 close(child_stderr[1]); 00485 child_stderr[1] = -1; 00486 res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], pipe_delim_args, flags); 00487 } 00488 } 00489 00490 exit: 00491 if (u->gen_active) { 00492 ast_deactivate_generator(chan); 00493 } 00494 if (child_stdin[0] > -1) { 00495 close(child_stdin[0]); 00496 } 00497 if (child_stdin[1] > -1) { 00498 close(child_stdin[1]); 00499 } 00500 if (child_stdout[0] > -1) { 00501 close(child_stdout[0]); 00502 } 00503 if (child_stdout[1] > -1) { 00504 close(child_stdout[1]); 00505 } 00506 if (child_stderr[0] > -1) { 00507 close(child_stderr[0]); 00508 } 00509 if (child_stderr[1] > -1) { 00510 close(child_stderr[1]); 00511 } 00512 if (ser) { 00513 ao2_ref(ser, -1); 00514 } 00515 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00516 ast_free(entry); 00517 } 00518 return res; 00519 }
static void ast_eivr_getvariable | ( | struct ast_channel * | chan, | |
char * | data, | |||
char * | outbuf, | |||
int | outbuflen | |||
) | [static] |
Definition at line 250 of file app_externalivr.c.
References ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_str_alloca, ast_str_append(), chan, inbuf(), pbx_builtin_getvar_helper(), ast_str::str, and strsep().
Referenced by eivr_comm().
00251 { 00252 /* original input data: "G,var1,var2," */ 00253 /* data passed as "data": "var1,var2" */ 00254 00255 char *inbuf, *variable; 00256 const char *value; 00257 int j; 00258 struct ast_str *newstring = ast_str_alloca(outbuflen); 00259 00260 outbuf[0] = '\0'; 00261 00262 for (j = 1, inbuf = data; ; j++) { 00263 variable = strsep(&inbuf, ","); 00264 if (variable == NULL) { 00265 int outstrlen = strlen(outbuf); 00266 if (outstrlen && outbuf[outstrlen - 1] == ',') { 00267 outbuf[outstrlen - 1] = 0; 00268 } 00269 break; 00270 } 00271 00272 ast_channel_lock(chan); 00273 if (!(value = pbx_builtin_getvar_helper(chan, variable))) { 00274 value = ""; 00275 } 00276 00277 ast_str_append(&newstring, 0, "%s=%s,", variable, value); 00278 ast_channel_unlock(chan); 00279 ast_copy_string(outbuf, newstring->str, outbuflen); 00280 } 00281 }
static void ast_eivr_setvariable | ( | struct ast_channel * | chan, | |
char * | data | |||
) | [static] |
Definition at line 283 of file app_externalivr.c.
References ast_debug, ast_strdupa, chan, inbuf(), pbx_builtin_setvar_helper(), and strsep().
Referenced by eivr_comm().
00284 { 00285 char *value; 00286 00287 char *inbuf = ast_strdupa(data), *variable; 00288 00289 for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) { 00290 ast_debug(1, "Setting up a variable: %s\n", variable); 00291 /* variable contains "varname=value" */ 00292 value = strchr(variable, '='); 00293 if (!value) { 00294 value = ""; 00295 } else { 00296 *value++ = '\0'; 00297 } 00298 pbx_builtin_setvar_helper(chan, variable, value); 00299 } 00300 }
static int eivr_comm | ( | struct ast_channel * | chan, | |
struct ivr_localuser * | u, | |||
int * | eivr_events_fd, | |||
int * | eivr_commands_fd, | |||
int * | eivr_errors_fd, | |||
const struct ast_str * | args, | |||
const struct ast_flags | flags | |||
) | [static] |
Definition at line 521 of file app_externalivr.c.
References ast_channel::_state, ivr_localuser::abort_current_sound, ast_activate_generator(), ast_answer(), ast_chan_log, ast_check_hangup(), AST_CONTROL_HANGUP, ast_debug, ast_eivr_getvariable(), ast_eivr_setvariable(), ast_fileexists(), AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_free, ast_frfree, AST_LIST_EMPTY, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_read(), AST_STATE_UP, ast_strip(), ast_test_flag, ast_waitfor_nandfds(), ivr_localuser::chan, chan, errno, f, playlist_entry::filename, ivr_localuser::finishlist, ast_channel::flags, gen, ivr_localuser::gen_active, ast_channel::hangupcause, ignore_hangup, input(), ast_channel::language, playlist_entry::list, LOG_NOTICE, LOG_WARNING, make_entry(), ivr_localuser::option_autoclear, option_debug, ivr_localuser::playing_silence, ivr_localuser::playlist, run_dead, send_eivr_event(), and ast_str::str.
00524 { 00525 struct playlist_entry *entry; 00526 struct ast_frame *f; 00527 int ms; 00528 int exception; 00529 int ready_fd; 00530 int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 }; 00531 struct ast_channel *rchan; 00532 char *command; 00533 int res = -1; 00534 int test_available_fd = -1; 00535 int hangup_info_sent = 0; 00536 00537 FILE *eivr_commands = NULL; 00538 FILE *eivr_errors = NULL; 00539 FILE *eivr_events = NULL; 00540 00541 if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) { 00542 ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n"); 00543 goto exit; 00544 } 00545 if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) { 00546 ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n"); 00547 goto exit; 00548 } 00549 if (eivr_errors_fd) { /* if opening a socket connection, error stream will not be used */ 00550 if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) { 00551 ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n"); 00552 goto exit; 00553 } 00554 } 00555 00556 test_available_fd = open("/dev/null", O_RDONLY); 00557 00558 setvbuf(eivr_events, NULL, _IONBF, 0); 00559 setvbuf(eivr_commands, NULL, _IONBF, 0); 00560 if (eivr_errors) { 00561 setvbuf(eivr_errors, NULL, _IONBF, 0); 00562 } 00563 00564 res = 0; 00565 00566 while (1) { 00567 if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { 00568 ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n"); 00569 res = -1; 00570 break; 00571 } 00572 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) { 00573 if (ast_test_flag(&flags, ignore_hangup)) { 00574 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup, but ignore_hangup set so sending 'I' command\n"); 00575 send_eivr_event(eivr_events, 'I', "HANGUP", chan); 00576 hangup_info_sent = 1; 00577 } else { 00578 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n"); 00579 send_eivr_event(eivr_events, 'H', NULL, chan); 00580 res = -1; 00581 break; 00582 } 00583 } 00584 00585 ready_fd = 0; 00586 ms = 100; 00587 errno = 0; 00588 exception = 0; 00589 00590 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms); 00591 00592 if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) { 00593 AST_LIST_LOCK(&u->finishlist); 00594 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { 00595 send_eivr_event(eivr_events, 'F', entry->filename, chan); 00596 ast_free(entry); 00597 } 00598 AST_LIST_UNLOCK(&u->finishlist); 00599 } 00600 00601 if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) { 00602 /* the channel has something */ 00603 f = ast_read(chan); 00604 if (!f) { 00605 ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n"); 00606 send_eivr_event(eivr_events, 'H', NULL, chan); 00607 res = -1; 00608 break; 00609 } 00610 if (f->frametype == AST_FRAME_DTMF) { 00611 send_eivr_event(eivr_events, f->subclass, NULL, chan); 00612 if (u->option_autoclear) { 00613 if (!u->abort_current_sound && !u->playing_silence) 00614 send_eivr_event(eivr_events, 'T', NULL, chan); 00615 AST_LIST_LOCK(&u->playlist); 00616 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00617 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00618 ast_free(entry); 00619 } 00620 if (!u->playing_silence) 00621 u->abort_current_sound = 1; 00622 AST_LIST_UNLOCK(&u->playlist); 00623 } 00624 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) { 00625 ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n"); 00626 send_eivr_event(eivr_events, 'H', NULL, chan); 00627 if (f->data.uint32) { 00628 chan->hangupcause = f->data.uint32; 00629 } 00630 ast_frfree(f); 00631 res = -1; 00632 break; 00633 } 00634 ast_frfree(f); 00635 } else if (ready_fd == *eivr_commands_fd) { 00636 char input[1024]; 00637 00638 if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) { 00639 ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); 00640 res = -1; 00641 break; 00642 } 00643 00644 if (!fgets(input, sizeof(input), eivr_commands)) 00645 continue; 00646 00647 command = ast_strip(input); 00648 00649 if (option_debug) 00650 ast_debug(1, "got command '%s'\n", input); 00651 00652 if (strlen(input) < 4) 00653 continue; 00654 00655 if (input[0] == 'P') { 00656 send_eivr_event(eivr_events, 'P', args->str, chan); 00657 } else if ( input[0] == 'T' ) { 00658 ast_chan_log(LOG_WARNING, chan, "Answering channel if needed and starting generator\n"); 00659 if (chan->_state != AST_STATE_UP) { 00660 if (ast_test_flag(&flags, run_dead)) { 00661 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00662 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan); 00663 continue; 00664 } 00665 ast_answer(chan); 00666 } 00667 if (!(u->gen_active)) { 00668 if (ast_activate_generator(chan, &gen, u) < 0) { 00669 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); 00670 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan); 00671 } else { 00672 u->gen_active = 1; 00673 } 00674 } 00675 } else if (input[0] == 'S') { 00676 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00677 ast_chan_log(LOG_WARNING, chan, "Queue 'S'et called on unanswered channel\n"); 00678 send_eivr_event(eivr_events, 'Z', NULL, chan); 00679 continue; 00680 } 00681 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { 00682 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00683 send_eivr_event(eivr_events, 'Z', NULL, chan); 00684 strcpy(&input[2], "exception"); 00685 } 00686 if (!u->abort_current_sound && !u->playing_silence) 00687 send_eivr_event(eivr_events, 'T', NULL, chan); 00688 AST_LIST_LOCK(&u->playlist); 00689 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00690 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00691 ast_free(entry); 00692 } 00693 if (!u->playing_silence) 00694 u->abort_current_sound = 1; 00695 entry = make_entry(&input[2]); 00696 if (entry) 00697 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00698 AST_LIST_UNLOCK(&u->playlist); 00699 } else if (input[0] == 'A') { 00700 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00701 ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n"); 00702 send_eivr_event(eivr_events, 'Z', NULL, chan); 00703 continue; 00704 } 00705 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) { 00706 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00707 send_eivr_event(eivr_events, 'Z', NULL, chan); 00708 strcpy(&input[2], "exception"); 00709 } 00710 entry = make_entry(&input[2]); 00711 if (entry) { 00712 AST_LIST_LOCK(&u->playlist); 00713 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00714 AST_LIST_UNLOCK(&u->playlist); 00715 } 00716 } else if (input[0] == 'G') { 00717 /* A get variable message: "G,variable1,variable2,..." */ 00718 char response[2048]; 00719 00720 ast_chan_log(LOG_NOTICE, chan, "Getting a Variable out of the channel: %s\n", &input[2]); 00721 ast_eivr_getvariable(chan, &input[2], response, sizeof(response)); 00722 send_eivr_event(eivr_events, 'G', response, chan); 00723 } else if (input[0] == 'V') { 00724 /* A set variable message: "V,variablename=foo" */ 00725 ast_chan_log(LOG_NOTICE, chan, "Setting a Variable up: %s\n", &input[2]); 00726 ast_eivr_setvariable(chan, &input[2]); 00727 } else if (input[0] == 'L') { 00728 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]); 00729 } else if (input[0] == 'X') { 00730 ast_chan_log(LOG_NOTICE, chan, "Exiting ExternalIVR: %s\n", &input[2]); 00731 /*! \todo add deprecation debug message for X command here */ 00732 res = 0; 00733 break; 00734 } else if (input[0] == 'E') { 00735 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); 00736 send_eivr_event(eivr_events, 'E', NULL, chan); 00737 res = 0; 00738 break; 00739 } else if (input[0] == 'H') { 00740 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); 00741 send_eivr_event(eivr_events, 'H', NULL, chan); 00742 res = -1; 00743 break; 00744 } else if (input[0] == 'O') { 00745 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00746 ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n"); 00747 send_eivr_event(eivr_events, 'Z', NULL, chan); 00748 continue; 00749 } 00750 if (!strcasecmp(&input[2], "autoclear")) 00751 u->option_autoclear = 1; 00752 else if (!strcasecmp(&input[2], "noautoclear")) 00753 u->option_autoclear = 0; 00754 else 00755 ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]); 00756 } 00757 } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) { 00758 char input[1024]; 00759 00760 if (exception || feof(eivr_errors)) { 00761 ast_chan_log(LOG_WARNING, chan, "Child process went away\n"); 00762 res = -1; 00763 break; 00764 } 00765 if (fgets(input, sizeof(input), eivr_errors)) { 00766 command = ast_strip(input); 00767 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command); 00768 } 00769 } else if ((ready_fd < 0) && ms) { 00770 if (errno == 0 || errno == EINTR) 00771 continue; 00772 00773 ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno)); 00774 break; 00775 } 00776 } 00777 00778 exit: 00779 if (test_available_fd > -1) { 00780 close(test_available_fd); 00781 } 00782 if (eivr_events) { 00783 fclose(eivr_events); 00784 *eivr_events_fd = -1; 00785 } 00786 if (eivr_commands) { 00787 fclose(eivr_commands); 00788 *eivr_commands_fd = -1; 00789 } 00790 if (eivr_errors) { 00791 fclose(eivr_errors); 00792 *eivr_errors_fd = -1; 00793 } 00794 return res; 00795 }
int eivr_connect_socket | ( | struct ast_channel * | chan, | |
const char * | host, | |||
int | port | |||
) |
static void* gen_alloc | ( | struct ast_channel * | chan, | |
void * | params | |||
) | [static] |
Definition at line 129 of file app_externalivr.c.
References ast_calloc, and gen_state::u.
00130 { 00131 struct ivr_localuser *u = params; 00132 struct gen_state *state; 00133 00134 if (!(state = ast_calloc(1, sizeof(*state)))) 00135 return NULL; 00136 00137 state->u = u; 00138 00139 return state; 00140 }
static void gen_closestream | ( | struct gen_state * | state | ) | [static] |
Definition at line 142 of file app_externalivr.c.
References ast_closestream(), ivr_localuser::chan, ast_channel::stream, gen_state::stream, and gen_state::u.
Referenced by gen_nextfile(), gen_readframe(), and gen_release().
00143 { 00144 if (!state->stream) 00145 return; 00146 00147 ast_closestream(state->stream); 00148 state->u->chan->stream = NULL; 00149 state->stream = NULL; 00150 }
static int gen_generate | ( | struct ast_channel * | chan, | |
void * | data, | |||
int | len, | |||
int | samples | |||
) | [static] |
Definition at line 219 of file app_externalivr.c.
References ast_chan_log, ast_frfree, ast_write(), chan, errno, f, gen_readframe(), LOG_WARNING, and gen_state::sample_queue.
00220 { 00221 struct gen_state *state = data; 00222 struct ast_frame *f = NULL; 00223 int res = 0; 00224 00225 state->sample_queue += samples; 00226 00227 while (state->sample_queue > 0) { 00228 if (!(f = gen_readframe(state))) 00229 return -1; 00230 00231 res = ast_write(chan, f); 00232 ast_frfree(f); 00233 if (res < 0) { 00234 ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno)); 00235 return -1; 00236 } 00237 state->sample_queue -= f->samples; 00238 } 00239 00240 return res; 00241 }
static int gen_nextfile | ( | struct gen_state * | state | ) | [static] |
Definition at line 161 of file app_externalivr.c.
References ivr_localuser::abort_current_sound, ast_chan_log, AST_LIST_REMOVE_HEAD, ast_openstream_full(), ivr_localuser::chan, gen_state::current, errno, playlist_entry::filename, gen_closestream(), ast_channel::language, playlist_entry::list, LOG_WARNING, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.
Referenced by gen_readframe().
00162 { 00163 struct ivr_localuser *u = state->u; 00164 char *file_to_stream; 00165 00166 u->abort_current_sound = 0; 00167 u->playing_silence = 0; 00168 gen_closestream(state); 00169 00170 while (!state->stream) { 00171 state->current = AST_LIST_REMOVE_HEAD(&u->playlist, list); 00172 if (state->current) { 00173 file_to_stream = state->current->filename; 00174 } else { 00175 file_to_stream = "silence/10"; 00176 u->playing_silence = 1; 00177 } 00178 00179 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) { 00180 ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno)); 00181 if (!u->playing_silence) { 00182 continue; 00183 } else { 00184 break; 00185 } 00186 } 00187 } 00188 00189 return (!state->stream); 00190 }
Definition at line 192 of file app_externalivr.c.
References ivr_localuser::abort_current_sound, AST_LIST_FIRST, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_readframe(), gen_state::current, f, ivr_localuser::finishlist, gen_closestream(), gen_nextfile(), playlist_entry::list, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.
Referenced by gen_generate().
00193 { 00194 struct ast_frame *f = NULL; 00195 struct ivr_localuser *u = state->u; 00196 00197 if (u->abort_current_sound || 00198 (u->playing_silence && AST_LIST_FIRST(&u->playlist))) { 00199 gen_closestream(state); 00200 AST_LIST_LOCK(&u->playlist); 00201 gen_nextfile(state); 00202 AST_LIST_UNLOCK(&u->playlist); 00203 } 00204 00205 if (!(state->stream && (f = ast_readframe(state->stream)))) { 00206 if (state->current) { 00207 AST_LIST_LOCK(&u->finishlist); 00208 AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list); 00209 AST_LIST_UNLOCK(&u->finishlist); 00210 state->current = NULL; 00211 } 00212 if (!gen_nextfile(state)) 00213 f = ast_readframe(state->stream); 00214 } 00215 00216 return f; 00217 }
static void gen_release | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 152 of file app_externalivr.c.
References ast_free, and gen_closestream().
00153 { 00154 struct gen_state *state = data; 00155 00156 gen_closestream(state); 00157 ast_free(data); 00158 }
static int load_module | ( | void | ) | [static] |
Definition at line 802 of file app_externalivr.c.
References app_exec, and ast_register_application.
00803 { 00804 return ast_register_application(app, app_exec, synopsis, descrip); 00805 }
static struct playlist_entry* make_entry | ( | const char * | filename | ) | [static] |
Definition at line 302 of file app_externalivr.c.
References ast_calloc.
Referenced by eivr_comm().
00303 { 00304 struct playlist_entry *entry; 00305 00306 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */ 00307 return NULL; 00308 00309 strcpy(entry->filename, filename); 00310 00311 return entry; 00312 }
static void send_eivr_event | ( | FILE * | handle, | |
const char | event, | |||
const char * | data, | |||
const struct ast_channel * | chan | |||
) | [static] |
Definition at line 115 of file app_externalivr.c.
References ast_debug, ast_str_append(), ast_str_create(), and ast_str::str.
Referenced by eivr_comm().
00117 { 00118 struct ast_str *tmp = ast_str_create(12); 00119 00120 ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL)); 00121 if (data) { 00122 ast_str_append(&tmp, 0, ",%s", data); 00123 } 00124 00125 fprintf(handle, "%s\n", tmp->str); 00126 ast_debug(1, "sent '%s'\n", tmp->str); 00127 }
static int unload_module | ( | void | ) | [static] |
Definition at line 797 of file app_externalivr.c.
References ast_unregister_application().
00798 { 00799 return ast_unregister_application(app); 00800 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "External IVR Interface Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 807 of file app_externalivr.c.
const char* app = "ExternalIVR" [static] |
Definition at line 51 of file app_externalivr.c.
struct ast_app_option app_opts[128] = { [ 'n' ] = { .flag = noanswer }, [ 'i' ] = { .flag = ignore_hangup }, [ 'd' ] = { .flag = run_dead },} [static] |
Definition at line 84 of file app_externalivr.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 807 of file app_externalivr.c.
const char* descrip [static] |
Definition at line 54 of file app_externalivr.c.
struct ast_generator gen [static] |
Definition at line 243 of file app_externalivr.c.
Referenced by app_exec(), ast_activate_generator(), eivr_comm(), reload_config(), and set_config().
enum { ... } options_flags |
const char* synopsis = "Interfaces with an external IVR application" [static] |
Definition at line 53 of file app_externalivr.c.