#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, ast_channel_name(channel) , ## __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 = AST_BUILDOPT_SUM, .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, ast_channel_name(channel) , ## __VA_ARGS__) |
Definition at line 100 of file app_externalivr.c.
Referenced by app_exec(), 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 890 of file app_externalivr.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 890 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 ast_channel::_state, ivr_localuser::abort_current_sound, ast_tcptls_session_args::accept_fd, ao2_ref, app_opts, ast_activate_generator(), ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_chan_log, ast_close_fds_above_n(), ast_copy_string(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, ast_free, ast_gethostbyname(), AST_LIST_HEAD_INIT_VALUE, AST_LIST_REMOVE_HEAD, ast_log(), ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_sockaddr_from_sin, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_str_alloca, ast_str_append(), ast_str_reset(), ast_strdupa, ast_strlen_zero(), ast_tcptls_client_create(), ast_tcptls_client_start(), ast_test_flag, ast_verb, ivr_localuser::chan, eivr_comm(), errno, ast_tcptls_session_instance::fd, ivr_localuser::gen_active, hostname, ast_hostent::hp, ignore_hangup, LOG_ERROR, noanswer, ivr_localuser::playlist, ast_tcptls_session_args::remote_address, and run_dead.
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 .playing_silence = 1, 00410 }; 00411 struct ivr_localuser *u = &foo; 00412 00413 char *buf; 00414 int j; 00415 char *s, **app_args, *e; 00416 struct ast_str *comma_delim_args = ast_str_alloca(100); 00417 00418 AST_DECLARE_APP_ARGS(eivr_args, 00419 AST_APP_ARG(application); 00420 AST_APP_ARG(options); 00421 ); 00422 AST_DECLARE_APP_ARGS(application_args, 00423 AST_APP_ARG(cmd)[32]; 00424 ); 00425 00426 u->abort_current_sound = 0; 00427 u->chan = chan; 00428 00429 if (ast_strlen_zero(data)) { 00430 ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n"); 00431 goto exit; 00432 } 00433 00434 buf = ast_strdupa(data); 00435 AST_STANDARD_APP_ARGS(eivr_args, buf); 00436 00437 ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application); 00438 ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options); 00439 00440 /* Parse out any application arguments */ 00441 if ((s = strchr(eivr_args.application, '('))) { 00442 s[0] = ','; 00443 if ((e = strrchr(s, ')'))) { 00444 *e = '\0'; 00445 } else { 00446 ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n"); 00447 goto exit; 00448 } 00449 } 00450 00451 AST_STANDARD_APP_ARGS(application_args, eivr_args.application); 00452 app_args = application_args.argv; 00453 00454 /* Put the application + the arguments in a , delimited list */ 00455 ast_str_reset(comma_delim_args); 00456 for (j = 0; application_args.cmd[j] != NULL; j++) { 00457 ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]); 00458 } 00459 00460 /* Get rid of any extraneous arguments */ 00461 if (eivr_args.options && (s = strchr(eivr_args.options, ','))) { 00462 *s = '\0'; 00463 } 00464 00465 /* Parse the ExternalIVR() arguments */ 00466 ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options); 00467 ast_app_parse_options(app_opts, &flags, opts, eivr_args.options); 00468 if (ast_test_flag(&flags, noanswer)) { 00469 ast_verb(4, "noanswer is set\n"); 00470 } 00471 if (ast_test_flag(&flags, ignore_hangup)) { 00472 ast_verb(4, "ignore_hangup is set\n"); 00473 } 00474 if (ast_test_flag(&flags, run_dead)) { 00475 ast_verb(4, "run_dead is set\n"); 00476 } 00477 00478 if (!(ast_test_flag(&flags, noanswer))) { 00479 ast_verb(3, "Answering channel and starting generator\n"); 00480 if (chan->_state != AST_STATE_UP) { 00481 if (ast_test_flag(&flags, run_dead)) { 00482 ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00483 goto exit; 00484 } 00485 ast_answer(chan); 00486 } 00487 if (ast_activate_generator(chan, &gen, u) < 0) { 00488 ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n"); 00489 goto exit; 00490 } else { 00491 u->gen_active = 1; 00492 } 00493 } 00494 00495 if (!strncmp(app_args[0], "ivr://", 6)) { 00496 struct ast_tcptls_session_args ivr_desc = { 00497 .accept_fd = -1, 00498 .name = "IVR", 00499 }; 00500 struct ast_hostent hp; 00501 struct sockaddr_in remote_address_tmp; 00502 00503 /*communicate through socket to server*/ 00504 ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]); 00505 ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname)); 00506 if ((port_str = strchr(hostname, ':')) != NULL) { 00507 port_str[0] = 0; 00508 port_str += 1; 00509 port = atoi(port_str); 00510 } 00511 if (!port) { 00512 port = 2949; /* default port, if one is not provided */ 00513 } 00514 00515 ast_gethostbyname(hostname, &hp); 00516 remote_address_tmp.sin_family = AF_INET; 00517 remote_address_tmp.sin_port = htons(port); 00518 memcpy(&remote_address_tmp.sin_addr.s_addr, hp.hp.h_addr, sizeof(hp.hp.h_addr)); 00519 ast_sockaddr_from_sin(&ivr_desc.remote_address, &remote_address_tmp); 00520 if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) { 00521 goto exit; 00522 } 00523 res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags); 00524 00525 } else { 00526 if (pipe(child_stdin)) { 00527 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno)); 00528 goto exit; 00529 } 00530 if (pipe(child_stdout)) { 00531 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno)); 00532 goto exit; 00533 } 00534 if (pipe(child_stderr)) { 00535 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno)); 00536 goto exit; 00537 } 00538 00539 pid = ast_safe_fork(0); 00540 if (pid < 0) { 00541 ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno)); 00542 goto exit; 00543 } 00544 00545 if (!pid) { 00546 /* child process */ 00547 if (ast_opt_high_priority) 00548 ast_set_priority(0); 00549 00550 dup2(child_stdin[0], STDIN_FILENO); 00551 dup2(child_stdout[1], STDOUT_FILENO); 00552 dup2(child_stderr[1], STDERR_FILENO); 00553 ast_close_fds_above_n(STDERR_FILENO); 00554 execv(app_args[0], app_args); 00555 fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno)); 00556 _exit(1); 00557 } else { 00558 /* parent process */ 00559 close(child_stdin[0]); 00560 child_stdin[0] = -1; 00561 close(child_stdout[1]); 00562 child_stdout[1] = -1; 00563 close(child_stderr[1]); 00564 child_stderr[1] = -1; 00565 res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags); 00566 } 00567 } 00568 00569 exit: 00570 if (u->gen_active) { 00571 ast_deactivate_generator(chan); 00572 } 00573 if (child_stdin[0] > -1) { 00574 close(child_stdin[0]); 00575 } 00576 if (child_stdin[1] > -1) { 00577 close(child_stdin[1]); 00578 } 00579 if (child_stdout[0] > -1) { 00580 close(child_stdout[0]); 00581 } 00582 if (child_stdout[1] > -1) { 00583 close(child_stdout[1]); 00584 } 00585 if (child_stderr[0] > -1) { 00586 close(child_stderr[0]); 00587 } 00588 if (child_stderr[1] > -1) { 00589 close(child_stderr[1]); 00590 } 00591 if (ser) { 00592 ao2_ref(ser, -1); 00593 } 00594 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00595 ast_free(entry); 00596 } 00597 return res; 00598 }
| 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 600 of file app_externalivr.c.
References ast_channel::_state, ivr_localuser::abort_current_sound, ast_activate_generator(), ast_answer(), ast_chan_log, ast_channel_language(), 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, ast_frame::data, 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_frame::frametype, ivr_localuser::gen_active, ast_channel::hangupcause, ignore_hangup, input(), ast_frame_subclass::integer, LOG_ERROR, LOG_NOTICE, LOG_WARNING, make_entry(), ivr_localuser::option_autoclear, ivr_localuser::playing_silence, ivr_localuser::playlist, run_dead, send_eivr_event(), ast_frame::subclass, and ast_frame::uint32.
Referenced by app_exec().
00603 { 00604 struct playlist_entry *entry; 00605 struct ast_frame *f; 00606 int ms; 00607 int exception; 00608 int ready_fd; 00609 int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 }; 00610 struct ast_channel *rchan; 00611 int res = -1; 00612 int test_available_fd = -1; 00613 int hangup_info_sent = 0; 00614 00615 FILE *eivr_commands = NULL; 00616 FILE *eivr_errors = NULL; 00617 FILE *eivr_events = NULL; 00618 00619 if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) { 00620 ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n"); 00621 goto exit; 00622 } 00623 if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) { 00624 ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n"); 00625 goto exit; 00626 } 00627 if (eivr_errors_fd) { /* if opening a socket connection, error stream will not be used */ 00628 if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) { 00629 ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n"); 00630 goto exit; 00631 } 00632 } 00633 00634 test_available_fd = open("/dev/null", O_RDONLY); 00635 00636 setvbuf(eivr_events, NULL, _IONBF, 0); 00637 setvbuf(eivr_commands, NULL, _IONBF, 0); 00638 if (eivr_errors) { 00639 setvbuf(eivr_errors, NULL, _IONBF, 0); 00640 } 00641 00642 while (1) { 00643 if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) { 00644 ast_chan_log(LOG_ERROR, chan, "Is a zombie\n"); 00645 break; 00646 } 00647 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) { 00648 if (ast_test_flag(&flags, ignore_hangup)) { 00649 ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n"); 00650 send_eivr_event(eivr_events, 'I', "HANGUP", chan); 00651 hangup_info_sent = 1; 00652 } else { 00653 ast_verb(3, "Got check_hangup\n"); 00654 send_eivr_event(eivr_events, 'H', NULL, chan); 00655 break; 00656 } 00657 } 00658 00659 ready_fd = 0; 00660 ms = 100; 00661 errno = 0; 00662 exception = 0; 00663 00664 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms); 00665 00666 if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) { 00667 AST_LIST_LOCK(&u->finishlist); 00668 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) { 00669 send_eivr_event(eivr_events, 'F', entry->filename, chan); 00670 ast_free(entry); 00671 } 00672 AST_LIST_UNLOCK(&u->finishlist); 00673 } 00674 00675 if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) { 00676 /* the channel has something */ 00677 f = ast_read(chan); 00678 if (!f) { 00679 ast_verb(3, "Returned no frame\n"); 00680 send_eivr_event(eivr_events, 'H', NULL, chan); 00681 break; 00682 } 00683 if (f->frametype == AST_FRAME_DTMF) { 00684 send_eivr_event(eivr_events, f->subclass.integer, NULL, chan); 00685 if (u->option_autoclear) { 00686 if (!u->abort_current_sound && !u->playing_silence) { 00687 /* send interrupted file as T data */ 00688 entry = AST_LIST_REMOVE_HEAD(&u->playlist, list); 00689 send_eivr_event(eivr_events, 'T', entry->filename, chan); 00690 ast_free(entry); 00691 } 00692 AST_LIST_LOCK(&u->playlist); 00693 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00694 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00695 ast_free(entry); 00696 } 00697 if (!u->playing_silence) 00698 u->abort_current_sound = 1; 00699 AST_LIST_UNLOCK(&u->playlist); 00700 } 00701 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) { 00702 ast_verb(3, "Got AST_CONTROL_HANGUP\n"); 00703 send_eivr_event(eivr_events, 'H', NULL, chan); 00704 if (f->data.uint32) { 00705 chan->hangupcause = f->data.uint32; 00706 } 00707 ast_frfree(f); 00708 break; 00709 } 00710 ast_frfree(f); 00711 } else if (ready_fd == *eivr_commands_fd) { 00712 char input[1024]; 00713 00714 if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) { 00715 ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); 00716 break; 00717 } 00718 00719 if (!fgets(input, sizeof(input), eivr_commands)) { 00720 continue; 00721 } 00722 00723 ast_strip(input); 00724 ast_verb(4, "got command '%s'\n", input); 00725 00726 if (strlen(input) < 3) { 00727 continue; 00728 } 00729 00730 if (input[0] == EIVR_CMD_PARM) { 00731 struct ast_str *tmp = (struct ast_str *) args; 00732 send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan); 00733 } else if (input[0] == EIVR_CMD_DTMF) { 00734 ast_verb(4, "Sending DTMF: %s\n", &input[2]); 00735 ast_eivr_senddtmf(chan, &input[2]); 00736 } else if (input[0] == EIVR_CMD_ANS) { 00737 ast_verb(3, "Answering channel if needed and starting generator\n"); 00738 if (chan->_state != AST_STATE_UP) { 00739 if (ast_test_flag(&flags, run_dead)) { 00740 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n"); 00741 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan); 00742 continue; 00743 } 00744 if (ast_answer(chan)) { 00745 ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n"); 00746 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan); 00747 continue; 00748 } 00749 } 00750 if (!(u->gen_active)) { 00751 if (ast_activate_generator(chan, &gen, u) < 0) { 00752 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n"); 00753 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan); 00754 } else { 00755 u->gen_active = 1; 00756 } 00757 } 00758 } else if (input[0] == EIVR_CMD_SQUE) { 00759 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00760 ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n"); 00761 send_eivr_event(eivr_events, 'Z', NULL, chan); 00762 continue; 00763 } 00764 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) { 00765 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00766 send_eivr_event(eivr_events, 'Z', &input[2], chan); 00767 } else { 00768 AST_LIST_LOCK(&u->playlist); 00769 if (!u->abort_current_sound && !u->playing_silence) { 00770 /* send interrupted file as T data */ 00771 entry = AST_LIST_REMOVE_HEAD(&u->playlist, list); 00772 send_eivr_event(eivr_events, 'T', entry->filename, chan); 00773 ast_free(entry); 00774 } 00775 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) { 00776 send_eivr_event(eivr_events, 'D', entry->filename, chan); 00777 ast_free(entry); 00778 } 00779 if (!u->playing_silence) { 00780 u->abort_current_sound = 1; 00781 } 00782 entry = make_entry(&input[2]); 00783 if (entry) { 00784 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00785 } 00786 AST_LIST_UNLOCK(&u->playlist); 00787 } 00788 } else if (input[0] == EIVR_CMD_APND) { 00789 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00790 ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n"); 00791 send_eivr_event(eivr_events, 'Z', NULL, chan); 00792 continue; 00793 } 00794 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) { 00795 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]); 00796 send_eivr_event(eivr_events, 'Z', &input[2], chan); 00797 } else { 00798 entry = make_entry(&input[2]); 00799 if (entry) { 00800 AST_LIST_LOCK(&u->playlist); 00801 AST_LIST_INSERT_TAIL(&u->playlist, entry, list); 00802 AST_LIST_UNLOCK(&u->playlist); 00803 } 00804 } 00805 } else if (input[0] == EIVR_CMD_GET) { 00806 char response[2048]; 00807 ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]); 00808 ast_eivr_getvariable(chan, &input[2], response, sizeof(response)); 00809 send_eivr_event(eivr_events, 'G', response, chan); 00810 } else if (input[0] == EIVR_CMD_SVAR) { 00811 ast_verb(4, "Setting Variables in channel: %s\n", &input[2]); 00812 ast_eivr_setvariable(chan, &input[2]); 00813 } else if (input[0] == EIVR_CMD_LOG) { 00814 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]); 00815 } else if (input[0] == EIVR_CMD_XIT) { 00816 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); 00817 ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n"); 00818 res = 0; 00819 break; 00820 } else if (input[0] == EIVR_CMD_EXIT) { 00821 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]); 00822 send_eivr_event(eivr_events, 'E', NULL, chan); 00823 res = 0; 00824 break; 00825 } else if (input[0] == EIVR_CMD_HGUP) { 00826 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]); 00827 send_eivr_event(eivr_events, 'H', NULL, chan); 00828 break; 00829 } else if (input[0] == EIVR_CMD_OPT) { 00830 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) { 00831 ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n"); 00832 send_eivr_event(eivr_events, 'Z', NULL, chan); 00833 continue; 00834 } 00835 if (!strcasecmp(&input[2], "autoclear")) 00836 u->option_autoclear = 1; 00837 else if (!strcasecmp(&input[2], "noautoclear")) 00838 u->option_autoclear = 0; 00839 else 00840 ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]); 00841 } 00842 } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) { 00843 char input[1024]; 00844 00845 if (exception || feof(eivr_errors)) { 00846 ast_chan_log(LOG_ERROR, chan, "Child process went away\n"); 00847 break; 00848 } 00849 if (fgets(input, sizeof(input), eivr_errors)) { 00850 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input)); 00851 } 00852 } else if ((ready_fd < 0) && ms) { 00853 if (errno == 0 || errno == EINTR) 00854 continue; 00855 00856 ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno)); 00857 break; 00858 } 00859 } 00860 00861 exit: 00862 if (test_available_fd > -1) { 00863 close(test_available_fd); 00864 } 00865 if (eivr_events) { 00866 fclose(eivr_events); 00867 *eivr_events_fd = -1; 00868 } 00869 if (eivr_commands) { 00870 fclose(eivr_commands); 00871 *eivr_commands_fd = -1; 00872 } 00873 if (eivr_errors) { 00874 fclose(eivr_errors); 00875 *eivr_errors_fd = -1; 00876 } 00877 return res; 00878 }
| 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, 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(), ivr_localuser::chan, ast_channel::stream, gen_state::stream, and gen_state::u.
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, gen_state::sample_queue, and ast_frame::samples.
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_channel_language(), AST_LIST_FIRST, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_openstream_full(), ivr_localuser::chan, gen_state::current, errno, playlist_entry::filename, gen_closestream(), LOG_WARNING, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.
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, ast_channel_language(u->chan), 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(), gen_state::current, f, ivr_localuser::finishlist, gen_closestream(), gen_nextfile(), ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.
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, and gen_closestream().
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 885 of file app_externalivr.c.
References app_exec, and ast_register_application_xml.
00886 { 00887 return ast_register_application_xml(app, app_exec); 00888 }
| static struct playlist_entry* make_entry | ( | const char * | filename | ) | [static, read] |
Definition at line 377 of file app_externalivr.c.
References ast_calloc, and playlist_entry::filename.
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 880 of file app_externalivr.c.
References ast_unregister_application().
00881 { 00882 return ast_unregister_application(app); 00883 }
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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static] |
Definition at line 890 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 890 of file app_externalivr.c.
struct ast_generator gen [static] |
1.5.6