#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__) |
#define | EIVR_CMD_ANS 'T' |
#define | EIVR_CMD_APND 'A' |
#define | EIVR_CMD_DTMF 'D' |
#define | EIVR_CMD_EXIT 'E' |
#define | EIVR_CMD_GET 'G' |
#define | EIVR_CMD_HGUP 'H' |
#define | EIVR_CMD_LOG 'L' |
#define | EIVR_CMD_OPT 'O' |
#define | EIVR_CMD_PARM 'P' |
#define | EIVR_CMD_SQUE 'S' |
#define | EIVR_CMD_SVAR 'V' |
#define | EIVR_CMD_XIT 'X' |
Enumerations | |
enum | options_flags { 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, const char *data) |
static void | ast_eivr_getvariable (struct ast_channel *chan, char *data, char *outbuf, int outbuflen) |
static void | ast_eivr_senddtmf (struct ast_channel *chan, char *vdata) |
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_LOAD_ORDER , .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 = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } |
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 struct ast_generator | gen |
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 100 of file app_externalivr.c.
Referenced by eivr_comm(), gen_generate(), and gen_nextfile().
#define EIVR_CMD_ANS 'T' |
#define EIVR_CMD_APND 'A' |
#define EIVR_CMD_DTMF 'D' |
#define EIVR_CMD_EXIT 'E' |
#define EIVR_CMD_GET 'G' |
#define EIVR_CMD_HGUP 'H' |
#define EIVR_CMD_LOG 'L' |
#define EIVR_CMD_OPT 'O' |
#define EIVR_CMD_PARM 'P' |
#define EIVR_CMD_SQUE 'S' |
#define EIVR_CMD_SVAR 'V' |
#define EIVR_CMD_XIT 'X' |
enum options_flags |
Definition at line 116 of file app_externalivr.c.
00116 { 00117 noanswer = (1 << 0), 00118 ignore_hangup = (1 << 1), 00119 run_dead = (1 << 2), 00120 };
static void __reg_module | ( | void | ) | [static] |
Definition at line 889 of file app_externalivr.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 889 of file app_externalivr.c.
static int app_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
Definition at line 389 of file app_externalivr.c.
References ivr_localuser::abort_current_sound, AST_APP_ARG, AST_DECLARE_APP_ARGS, AST_LIST_HEAD_INIT_VALUE, ast_log(), AST_STANDARD_APP_ARGS, ast_str_alloca, ast_str_append(), ast_str_reset(), ast_strdupa, ast_strlen_zero(), ast_verb, ivr_localuser::chan, ast_flags::flags, hostname, LOG_ERROR, and ivr_localuser::playlist.
00390 { 00391 struct ast_flags flags = { 0, }; 00392 char *opts[0]; 00393 struct playlist_entry *entry; 00394 int child_stdin[2] = { -1, -1 }; 00395 int child_stdout[2] = { -1, -1 }; 00396 int child_stderr[2] = { -1, -1 }; 00397 int res = -1; 00398 int pid; 00399 00400 char hostname[1024]; 00401 char *port_str = NULL; 00402 int port = 0; 00403 struct ast_tcptls_session_instance *ser = NULL; 00404 00405 struct ivr_localuser foo = { 00406 .playlist = AST_LIST_HEAD_INIT_VALUE, 00407 .finishlist = AST_LIST_HEAD_INIT_VALUE, 00408 .gen_active = 0, 00409 }; 00410 struct ivr_localuser *u = &foo; 00411 00412 char *buf; 00413 int j; 00414 char *s, **app_args, *e; 00415 struct ast_str *comma_delim_args = ast_str_alloca(100); 00416 00417 AST_DECLARE_APP_ARGS(eivr_args, 00418 AST_APP_ARG(application); 00419 AST_APP_ARG(options); 00420 ); 00421 AST_DECLARE_APP_ARGS(application_args, 00422 AST_APP_ARG(cmd)[32]; 00423 ); 00424 00425 u->abort_current_sound = 0; 00426 u->chan = chan; 00427 00428 if (ast_strlen_zero(data)) { 00429 ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n"); 00430 goto exit; 00431 } 00432 00433 buf = ast_strdupa(data); 00434 AST_STANDARD_APP_ARGS(eivr_args, buf); 00435 00436 ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application); 00437 ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options); 00438 00439 /* Parse out any application arguments */ 00440 if ((s = strchr(eivr_args.application, '('))) { 00441 s[0] = ','; 00442 if ((e = strrchr(s, ')'))) { 00443 *e = '\0'; 00444 } else { 00445 ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n"); 00446 goto exit; 00447 } 00448 } 00449 00450 AST_STANDARD_APP_ARGS(application_args, eivr_args.application); 00451 app_args = application_args.argv; 00452 00453 /* Put the application + the arguments in a , delimited list */ 00454 ast_str_reset(comma_delim_args); 00455 for (j = 0; application_args.cmd[j] != NULL; j++) { 00456 ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]); 00457 } 00458 00459 /* Get rid of any extraneous arguments */ 00460 if (eivr_args.options && (s = strchr(eivr_args.options, ','))) { 00461 *s = '\0'; 00462 } 00463 00464 /* Parse the ExternalIVR() arguments */ 00465 ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options); 00466 ast_app_parse_options(app_opts, &flags, opts, eivr_args.options); 00467 if (ast_test_flag(&flags, noanswer)) { 00468 ast_verb(4, "noanswer is set\n"); 00469 } 00470 if (ast_test_flag(&flags, ignore_hangup)) { 00471 ast_verb(4, "ignore_hangup is set\n"); 00472 } 00473 if (ast_test_flag(&flags, run_dead)) { 00474 ast_verb(4, "run_dead is set\n"); 00475 } 00476 00477 if (!(ast_test_flag(&flags, noanswer))) { 00478 ast_verb(3, "Answering channel and starting generator\n"); 00479 if (chan->_state != AST_STATE_UP) { 00480 if (ast_test_flag(&flags, run_dead)) { 00481 ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00482 goto exit; 00483 } 00484 ast_answer(chan); 00485 } 00486 if (ast_activate_generator(chan, &gen, u) < 0) { 00487 ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n"); 00488 goto exit; 00489 } else { 00490 u->gen_active = 1; 00491 } 00492 } 00493 00494 if (!strncmp(app_args[0], "ivr://", 6)) { 00495 struct ast_tcptls_session_args ivr_desc = { 00496 .accept_fd = -1, 00497 .name = "IVR", 00498 }; 00499 struct ast_hostent hp; 00500 struct sockaddr_in remote_address_tmp; 00501 00502 /*communicate through socket to server*/ 00503 ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]); 00504 ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname)); 00505 if ((port_str = strchr(hostname, ':')) != NULL) { 00506 port_str[0] = 0; 00507 port_str += 1; 00508 port = atoi(port_str); 00509 } 00510 if (!port) { 00511 port = 2949; /* default port, if one is not provided */ 00512 } 00513 00514 ast_gethostbyname(hostname, &hp); 00515 remote_address_tmp.sin_family = AF_INET; 00516 remote_address_tmp.sin_port = htons(port); 00517 memcpy(&remote_address_tmp.sin_addr.s_addr, hp.hp.h_addr, sizeof(hp.hp.h_addr)); 00518 ast_sockaddr_from_sin(&ivr_desc.remote_address, &remote_address_tmp); 00519 if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) { 00520 goto exit; 00521 } 00522 res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags); 00523 00524 } else { 00525 if (pipe(child_stdin)) { 00526 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno)); 00527 goto exit; 00528 } 00529 if (pipe(child_stdout)) { 00530 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno)); 00531 goto exit; 00532 } 00533 if (pipe(child_stderr)) { 00534 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno)); 00535 goto exit; 00536 } 00537 00538 pid = ast_safe_fork(0); 00539 if (pid < 0) { 00540 ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno)); 00541 goto exit; 00542 } 00543 00544 if (!pid) { 00545 /* child process */ 00546 if (ast_opt_high_priority) 00547 ast_set_priority(0); 00548 00549 dup2(child_stdin[0], STDIN_FILENO); 00550 dup2(child_stdout[1], STDOUT_FILENO); 00551 dup2(child_stderr[1], STDERR_FILENO); 00552 ast_close_fds_above_n(STDERR_FILENO); 00553 execv(app_args[0], app_args); 00554 fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno)); 00555 _exit(1); 00556 } else { 00557 /* parent process */ 00558 close(child_stdin[0]); 00559 child_stdin[0] = -1; 00560 close(child_stdout[1]); 00561 child_stdout[1] = -1; 00562 close(child_stderr[1]); 00563 child_stderr[1] = -1; 00564 res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags); 00565 } 00566 } 00567 00568 exit: 00569 if (u->gen_active) { 00570 ast_deactivate_generator(chan); 00571 } 00572 if (child_stdin[0] > -1) { 00573 close(child_stdin[0]); 00574 } 00575 if (child_stdin[1] > -1) { 00576 close(child_stdin[1]); 00577 } 00578 if (child_stdout[0] > -1) { 00579 close(child_stdout[0]); 00580 } 00581 if (child_stdout[1] > -1) { 00582 close(child_stdout[1]); 00583 } 00584 if (child_stderr[0] > -1) { 00585 close(child_stderr[0]); 00586 } 00587 if (child_stderr[1] > -1) { 00588 close(child_stderr[1]); 00589 } 00590 if (ser) { 00591 ao2_ref(ser, -1); 00592 } 00593 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00594 ast_free(entry); 00595 } 00596 return res; 00597 }
static void ast_eivr_getvariable | ( | struct ast_channel * | chan, | |
char * | data, | |||
char * | outbuf, | |||
int | outbuflen | |||
) | [static] |
Definition at line 301 of file app_externalivr.c.
References ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_str_alloca, ast_str_append(), ast_str_buffer(), inbuf(), pbx_builtin_getvar_helper(), strsep(), and value.
Referenced by eivr_comm().
00302 { 00303 /* original input data: "G,var1,var2," */ 00304 /* data passed as "data": "var1,var2" */ 00305 00306 char *inbuf, *variable; 00307 const char *value; 00308 int j; 00309 struct ast_str *newstring = ast_str_alloca(outbuflen); 00310 00311 outbuf[0] = '\0'; 00312 00313 for (j = 1, inbuf = data; ; j++) { 00314 variable = strsep(&inbuf, ","); 00315 if (variable == NULL) { 00316 int outstrlen = strlen(outbuf); 00317 if (outstrlen && outbuf[outstrlen - 1] == ',') { 00318 outbuf[outstrlen - 1] = 0; 00319 } 00320 break; 00321 } 00322 00323 ast_channel_lock(chan); 00324 if (!(value = pbx_builtin_getvar_helper(chan, variable))) { 00325 value = ""; 00326 } 00327 00328 ast_str_append(&newstring, 0, "%s=%s,", variable, value); 00329 ast_channel_unlock(chan); 00330 ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen); 00331 } 00332 }
static void ast_eivr_senddtmf | ( | struct ast_channel * | chan, | |
char * | vdata | |||
) | [static] |
Definition at line 353 of file app_externalivr.c.
References args, AST_APP_ARG, ast_app_parse_timelen(), AST_DECLARE_APP_ARGS, ast_dtmf_stream(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, and TIMELEN_MILLISECONDS.
Referenced by eivr_comm().
00354 { 00355 00356 char *data; 00357 int dinterval = 0, duration = 0; 00358 AST_DECLARE_APP_ARGS(args, 00359 AST_APP_ARG(digits); 00360 AST_APP_ARG(dinterval); 00361 AST_APP_ARG(duration); 00362 ); 00363 00364 data = ast_strdupa(vdata); 00365 AST_STANDARD_APP_ARGS(args, data); 00366 00367 if (!ast_strlen_zero(args.dinterval)) { 00368 ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS); 00369 } 00370 if (!ast_strlen_zero(args.duration)) { 00371 ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS); 00372 } 00373 ast_verb(4, "Sending DTMF: %s %d %d\n", args.digits, dinterval <= 0 ? 250 : dinterval, duration); 00374 ast_dtmf_stream(chan, NULL, args.digits, dinterval <= 0 ? 250 : dinterval, duration); 00375 }
static void ast_eivr_setvariable | ( | struct ast_channel * | chan, | |
char * | data | |||
) | [static] |
Definition at line 334 of file app_externalivr.c.
References ast_debug, ast_strdupa, inbuf(), pbx_builtin_setvar_helper(), strsep(), and value.
Referenced by eivr_comm().
00335 { 00336 char *value; 00337 00338 char *inbuf = ast_strdupa(data), *variable; 00339 00340 for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) { 00341 ast_debug(1, "Setting up a variable: %s\n", variable); 00342 /* variable contains "varname=value" */ 00343 value = strchr(variable, '='); 00344 if (!value) { 00345 value = ""; 00346 } else { 00347 *value++ = '\0'; 00348 } 00349 pbx_builtin_setvar_helper(chan, variable, value); 00350 } 00351 }
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 599 of file app_externalivr.c.
References ast_channel::_state, ivr_localuser::abort_current_sound, args, ast_activate_generator(), ast_answer(), ast_chan_log, ast_check_hangup(), AST_CONTROL_HANGUP, ast_eivr_getvariable(), ast_eivr_senddtmf(), 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_str_buffer(), ast_strip(), ast_test_flag, ast_verb, ast_waitfor_nandfds(), ivr_localuser::chan, EIVR_CMD_ANS, EIVR_CMD_APND, EIVR_CMD_DTMF, EIVR_CMD_EXIT, EIVR_CMD_GET, EIVR_CMD_HGUP, EIVR_CMD_LOG, EIVR_CMD_OPT, EIVR_CMD_PARM, EIVR_CMD_SQUE, EIVR_CMD_SVAR, EIVR_CMD_XIT, 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_ERROR, LOG_NOTICE, LOG_WARNING, make_entry(), ivr_localuser::option_autoclear, ivr_localuser::playing_silence, ivr_localuser::playlist, run_dead, and send_eivr_event().
00602 { 00603 struct playlist_entry *entry; 00604 struct ast_frame *f; 00605 int ms; 00606 int exception; 00607 int ready_fd; 00608 int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 }; 00609 struct ast_channel *rchan; 00610 int res = -1; 00611 int test_available_fd = -1; 00612 int hangup_info_sent = 0; 00613 00614 FILE *eivr_commands = NULL; 00615 FILE *eivr_errors = NULL; 00616 FILE *eivr_events = NULL; 00617 00618 if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) { 00619 ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n"); 00620 goto exit; 00621 } 00622 if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) { 00623 ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n"); 00624 goto exit; 00625 } 00626 if (eivr_errors_fd) { /* if opening a socket connection, error stream will not be used */ 00627 if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) { 00628 ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n"); 00629 goto exit; 00630 } 00631 } 00632 00633 test_available_fd = open("/dev/null", O_RDONLY); 00634 00635 setvbuf(eivr_events, NULL, _IONBF, 0); 00636 setvbuf(eivr_commands, NULL, _IONBF, 0); 00637 if (eivr_errors) { 00638 setvbuf(eivr_errors, NULL, _IONBF, 0); 00639 } 00640 00641 while (1) { 00642 if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { 00643 ast_chan_log(LOG_ERROR, chan, "Is a zombie\n"); 00644 break; 00645 } 00646 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) { 00647 if (ast_test_flag(&flags, ignore_hangup)) { 00648 ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n"); 00649 send_eivr_event(eivr_events, 'I', "HANGUP", chan); 00650 hangup_info_sent = 1; 00651 } else { 00652 ast_verb(3, "Got check_hangup\n"); 00653 send_eivr_event(eivr_events, 'H', NULL, chan); 00654 break; 00655 } 00656 } 00657 00658 ready_fd = 0; 00659 ms = 100; 00660 errno = 0; 00661 exception = 0; 00662 00663 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms); 00664 00665 if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) { 00666 AST_LIST_LOCK(&u->finishlist); 00667 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { 00668 send_eivr_event(eivr_events, 'F', entry->filename, chan); 00669 ast_free(entry); 00670 } 00671 AST_LIST_UNLOCK(&u->finishlist); 00672 } 00673 00674 if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) { 00675 /* the channel has something */ 00676 f = ast_read(chan); 00677 if (!f) { 00678 ast_verb(3, "Returned no frame\n"); 00679 send_eivr_event(eivr_events, 'H', NULL, chan); 00680 break; 00681 } 00682 if (f->frametype == AST_FRAME_DTMF) { 00683 send_eivr_event(eivr_events, f->subclass.integer, NULL, chan); 00684 if (u->option_autoclear) { 00685 if (!u->abort_current_sound && !u->playing_silence) { 00686 /* send interrupted file as T data */ 00687 entry = AST_LIST_REMOVE_HEAD(&u->playlist, list); 00688 send_eivr_event(eivr_events, 'T', entry->filename, chan); 00689 ast_free(entry); 00690 } 00691 AST_LIST_LOCK(&u->playlist); 00692 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00693 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00694 ast_free(entry); 00695 } 00696 if (!u->playing_silence) 00697 u->abort_current_sound = 1; 00698 AST_LIST_UNLOCK(&u->playlist); 00699 } 00700 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) { 00701 ast_verb(3, "Got AST_CONTROL_HANGUP\n"); 00702 send_eivr_event(eivr_events, 'H', NULL, chan); 00703 if (f->data.uint32) { 00704 chan->hangupcause = f->data.uint32; 00705 } 00706 ast_frfree(f); 00707 break; 00708 } 00709 ast_frfree(f); 00710 } else if (ready_fd == *eivr_commands_fd) { 00711 char input[1024]; 00712 00713 if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) { 00714 ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); 00715 break; 00716 } 00717 00718 if (!fgets(input, sizeof(input), eivr_commands)) { 00719 continue; 00720 } 00721 00722 ast_strip(input); 00723 ast_verb(4, "got command '%s'\n", input); 00724 00725 if (strlen(input) < 3) { 00726 continue; 00727 } 00728 00729 if (input[0] == EIVR_CMD_PARM) { 00730 struct ast_str *tmp = (struct ast_str *) args; 00731 send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan); 00732 } else if (input[0] == EIVR_CMD_DTMF) { 00733 ast_verb(4, "Sending DTMF: %s\n", &input[2]); 00734 ast_eivr_senddtmf(chan, &input[2]); 00735 } else if (input[0] == EIVR_CMD_ANS) { 00736 ast_verb(3, "Answering channel if needed and starting generator\n"); 00737 if (chan->_state != AST_STATE_UP) { 00738 if (ast_test_flag(&flags, run_dead)) { 00739 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00740 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan); 00741 continue; 00742 } 00743 if (ast_answer(chan)) { 00744 ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n"); 00745 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan); 00746 continue; 00747 } 00748 } 00749 if (!(u->gen_active)) { 00750 if (ast_activate_generator(chan, &gen, u) < 0) { 00751 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); 00752 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan); 00753 } else { 00754 u->gen_active = 1; 00755 } 00756 } 00757 } else if (input[0] == EIVR_CMD_SQUE) { 00758 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00759 ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n"); 00760 send_eivr_event(eivr_events, 'Z', NULL, chan); 00761 continue; 00762 } 00763 if (!ast_fileexists(&input[2], NULL, u->chan->language)) { 00764 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00765 send_eivr_event(eivr_events, 'Z', &input[2], chan); 00766 } else { 00767 AST_LIST_LOCK(&u->playlist); 00768 if (!u->abort_current_sound && !u->playing_silence) { 00769 /* send interrupted file as T data */ 00770 entry = AST_LIST_REMOVE_HEAD(&u->playlist, list); 00771 send_eivr_event(eivr_events, 'T', entry->filename, chan); 00772 ast_free(entry); 00773 } 00774 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00775 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00776 ast_free(entry); 00777 } 00778 if (!u->playing_silence) { 00779 u->abort_current_sound = 1; 00780 } 00781 entry = make_entry(&input[2]); 00782 if (entry) { 00783 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00784 } 00785 AST_LIST_UNLOCK(&u->playlist); 00786 } 00787 } else if (input[0] == EIVR_CMD_APND) { 00788 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00789 ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n"); 00790 send_eivr_event(eivr_events, 'Z', NULL, chan); 00791 continue; 00792 } 00793 if (!ast_fileexists(&input[2], NULL, u->chan->language)) { 00794 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00795 send_eivr_event(eivr_events, 'Z', &input[2], chan); 00796 } else { 00797 entry = make_entry(&input[2]); 00798 if (entry) { 00799 AST_LIST_LOCK(&u->playlist); 00800 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00801 AST_LIST_UNLOCK(&u->playlist); 00802 } 00803 } 00804 } else if (input[0] == EIVR_CMD_GET) { 00805 char response[2048]; 00806 ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]); 00807 ast_eivr_getvariable(chan, &input[2], response, sizeof(response)); 00808 send_eivr_event(eivr_events, 'G', response, chan); 00809 } else if (input[0] == EIVR_CMD_SVAR) { 00810 ast_verb(4, "Setting Variables in channel: %s\n", &input[2]); 00811 ast_eivr_setvariable(chan, &input[2]); 00812 } else if (input[0] == EIVR_CMD_LOG) { 00813 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]); 00814 } else if (input[0] == EIVR_CMD_XIT) { 00815 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); 00816 ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n"); 00817 res = 0; 00818 break; 00819 } else if (input[0] == EIVR_CMD_EXIT) { 00820 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); 00821 send_eivr_event(eivr_events, 'E', NULL, chan); 00822 res = 0; 00823 break; 00824 } else if (input[0] == EIVR_CMD_HGUP) { 00825 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); 00826 send_eivr_event(eivr_events, 'H', NULL, chan); 00827 break; 00828 } else if (input[0] == EIVR_CMD_OPT) { 00829 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00830 ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n"); 00831 send_eivr_event(eivr_events, 'Z', NULL, chan); 00832 continue; 00833 } 00834 if (!strcasecmp(&input[2], "autoclear")) 00835 u->option_autoclear = 1; 00836 else if (!strcasecmp(&input[2], "noautoclear")) 00837 u->option_autoclear = 0; 00838 else 00839 ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]); 00840 } 00841 } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) { 00842 char input[1024]; 00843 00844 if (exception || feof(eivr_errors)) { 00845 ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); 00846 break; 00847 } 00848 if (fgets(input, sizeof(input), eivr_errors)) { 00849 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input)); 00850 } 00851 } else if ((ready_fd < 0) && ms) { 00852 if (errno == 0 || errno == EINTR) 00853 continue; 00854 00855 ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno)); 00856 break; 00857 } 00858 } 00859 00860 exit: 00861 if (test_available_fd > -1) { 00862 close(test_available_fd); 00863 } 00864 if (eivr_events) { 00865 fclose(eivr_events); 00866 *eivr_events_fd = -1; 00867 } 00868 if (eivr_commands) { 00869 fclose(eivr_commands); 00870 *eivr_commands_fd = -1; 00871 } 00872 if (eivr_errors) { 00873 fclose(eivr_errors); 00874 *eivr_errors_fd = -1; 00875 } 00876 return res; 00877 }
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 172 of file app_externalivr.c.
References ast_calloc, state, and gen_state::u.
00173 { 00174 struct ivr_localuser *u = params; 00175 struct gen_state *state; 00176 00177 if (!(state = ast_calloc(1, sizeof(*state)))) 00178 return NULL; 00179 00180 state->u = u; 00181 00182 return state; 00183 }
static void gen_closestream | ( | struct gen_state * | state | ) | [static] |
Definition at line 185 of file app_externalivr.c.
References ast_closestream(), and state.
Referenced by gen_nextfile(), gen_readframe(), and gen_release().
00186 { 00187 if (!state->stream) 00188 return; 00189 00190 ast_closestream(state->stream); 00191 state->u->chan->stream = NULL; 00192 state->stream = NULL; 00193 }
static int gen_generate | ( | struct ast_channel * | chan, | |
void * | data, | |||
int | len, | |||
int | samples | |||
) | [static] |
Definition at line 270 of file app_externalivr.c.
References ast_chan_log, ast_frfree, ast_write(), errno, f, gen_readframe(), LOG_WARNING, and state.
00271 { 00272 struct gen_state *state = data; 00273 struct ast_frame *f = NULL; 00274 int res = 0; 00275 00276 state->sample_queue += samples; 00277 00278 while (state->sample_queue > 0) { 00279 if (!(f = gen_readframe(state))) 00280 return -1; 00281 00282 res = ast_write(chan, f); 00283 ast_frfree(f); 00284 if (res < 0) { 00285 ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno)); 00286 return -1; 00287 } 00288 state->sample_queue -= f->samples; 00289 } 00290 00291 return res; 00292 }
static int gen_nextfile | ( | struct gen_state * | state | ) | [static] |
Definition at line 204 of file app_externalivr.c.
References ivr_localuser::abort_current_sound, ast_chan_log, AST_LIST_FIRST, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_openstream_full(), ivr_localuser::chan, errno, gen_closestream(), ast_channel::language, playlist_entry::list, LOG_WARNING, ivr_localuser::playing_silence, ivr_localuser::playlist, and state.
Referenced by gen_readframe().
00205 { 00206 struct ivr_localuser *u = state->u; 00207 char *file_to_stream; 00208 00209 u->abort_current_sound = 0; 00210 u->playing_silence = 0; 00211 gen_closestream(state); 00212 00213 while (!state->stream) { 00214 state->current = AST_LIST_FIRST(&u->playlist); 00215 if (state->current) { 00216 file_to_stream = state->current->filename; 00217 } else { 00218 file_to_stream = "silence/10"; 00219 u->playing_silence = 1; 00220 } 00221 00222 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, u->chan->language, 1))) { 00223 ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno)); 00224 AST_LIST_LOCK(&u->playlist); 00225 AST_LIST_REMOVE_HEAD(&u->playlist, list); 00226 AST_LIST_UNLOCK(&u->playlist); 00227 if (!u->playing_silence) { 00228 continue; 00229 } else { 00230 break; 00231 } 00232 } 00233 } 00234 00235 return (!state->stream); 00236 }
Definition at line 238 of file app_externalivr.c.
References ivr_localuser::abort_current_sound, AST_LIST_FIRST, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_readframe(), f, ivr_localuser::finishlist, gen_closestream(), gen_nextfile(), playlist_entry::list, ivr_localuser::playing_silence, ivr_localuser::playlist, and state.
Referenced by gen_generate().
00239 { 00240 struct ast_frame *f = NULL; 00241 struct ivr_localuser *u = state->u; 00242 00243 if (u->abort_current_sound || 00244 (u->playing_silence && AST_LIST_FIRST(&u->playlist))) { 00245 gen_closestream(state); 00246 AST_LIST_LOCK(&u->playlist); 00247 gen_nextfile(state); 00248 AST_LIST_UNLOCK(&u->playlist); 00249 } 00250 00251 if (!(state->stream && (f = ast_readframe(state->stream)))) { 00252 if (state->current) { 00253 /* remove finished file from playlist */ 00254 AST_LIST_LOCK(&u->playlist); 00255 AST_LIST_REMOVE_HEAD(&u->playlist, list); 00256 AST_LIST_UNLOCK(&u->playlist); 00257 /* add finished file to finishlist */ 00258 AST_LIST_LOCK(&u->finishlist); 00259 AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list); 00260 AST_LIST_UNLOCK(&u->finishlist); 00261 state->current = NULL; 00262 } 00263 if (!gen_nextfile(state)) 00264 f = ast_readframe(state->stream); 00265 } 00266 00267 return f; 00268 }
static void gen_release | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 195 of file app_externalivr.c.
References ast_free, gen_closestream(), and state.
00196 { 00197 struct gen_state *state = data; 00198 00199 gen_closestream(state); 00200 ast_free(data); 00201 }
static int load_module | ( | void | ) | [static] |
Definition at line 884 of file app_externalivr.c.
References app_exec, and ast_register_application_xml.
00885 { 00886 return ast_register_application_xml(app, app_exec); 00887 }
static struct playlist_entry* make_entry | ( | const char * | filename | ) | [static] |
Definition at line 377 of file app_externalivr.c.
References ast_calloc.
Referenced by eivr_comm().
00378 { 00379 struct playlist_entry *entry; 00380 00381 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */ 00382 return NULL; 00383 00384 strcpy(entry->filename, filename); 00385 00386 return entry; 00387 }
static void send_eivr_event | ( | FILE * | handle, | |
const char | event, | |||
const char * | data, | |||
const struct ast_channel * | chan | |||
) | [static] |
Definition at line 157 of file app_externalivr.c.
References ast_debug, ast_free, ast_str_append(), ast_str_buffer(), and ast_str_create().
Referenced by eivr_comm().
00159 { 00160 struct ast_str *tmp = ast_str_create(12); 00161 00162 ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL)); 00163 if (data) { 00164 ast_str_append(&tmp, 0, ",%s", data); 00165 } 00166 00167 fprintf(handle, "%s\n", ast_str_buffer(tmp)); 00168 ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp)); 00169 ast_free(tmp); 00170 }
static int unload_module | ( | void | ) | [static] |
Definition at line 879 of file app_externalivr.c.
References ast_unregister_application().
00880 { 00881 return ast_unregister_application(app); 00882 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .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 = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static] |
Definition at line 889 of file app_externalivr.c.
const char app[] = "ExternalIVR" [static] |
Definition at line 97 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 126 of file app_externalivr.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 889 of file app_externalivr.c.
struct ast_generator gen [static] |
Definition at line 294 of file app_externalivr.c.
Referenced by ast_activate_generator(), eivr_comm(), reload_config(), and set_config().