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