Mon Oct 8 12:39:07 2012

Asterisk developer's documentation


app_externalivr.c File Reference

External IVR application interface. More...

#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_framegen_readframe (struct gen_state *state)
static void gen_release (struct ast_channel *chan, void *data)
static int load_module (void)
static struct playlist_entrymake_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 = "ac1f6a56484a8820659555499174e588" , .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_infoast_module_info = &__mod_info
static struct ast_generator gen


Detailed Description

External IVR application interface.

Author:
Kevin P. Fleming <kpfleming@digium.com>
Note:
Portions taken from the file-based music-on-hold work created by Anthony Minessale II in res_musiconhold.c

Definition in file app_externalivr.c.


Define Documentation

#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'

Definition at line 112 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_APND   'A'

Definition at line 103 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_DTMF   'D'

Definition at line 104 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_EXIT   'E'

Definition at line 105 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_GET   'G'

Definition at line 106 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_HGUP   'H'

Definition at line 107 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_LOG   'L'

Definition at line 108 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_OPT   'O'

Definition at line 109 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_PARM   'P'

Definition at line 110 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_SQUE   'S'

Definition at line 111 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_SVAR   'V'

Definition at line 113 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_XIT   'X'

Definition at line 114 of file app_externalivr.c.

Referenced by eivr_comm().


Enumeration Type Documentation

enum options_flags

Enumerator:
noanswer 
ignore_hangup 
run_dead 

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 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 891 of file app_externalivr.c.

static void __unreg_module ( void   )  [static]

Definition at line 891 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, hp.hp.h_length);
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                AST_LIST_LOCK(&u->playlist);
00686                if (!u->abort_current_sound && !u->playing_silence) {
00687                   /* send interrupted file as T data */
00688                   if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00689                      send_eivr_event(eivr_events, 'T', entry->filename, chan);
00690                      ast_free(entry);
00691                   }
00692                }
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, u->chan->language)) {
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                   if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00772                      send_eivr_event(eivr_events, 'T', entry->filename, chan);
00773                      ast_free(entry);
00774                   }
00775                }
00776                while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00777                   send_eivr_event(eivr_events, 'D', entry->filename, chan);
00778                   ast_free(entry);
00779                }
00780                if (!u->playing_silence) {
00781                   u->abort_current_sound = 1;
00782                }
00783                entry = make_entry(&input[2]);
00784                if (entry) {
00785                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00786                }
00787                AST_LIST_UNLOCK(&u->playlist);
00788             }
00789          } else if (input[0] == EIVR_CMD_APND) {
00790             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00791                ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
00792                send_eivr_event(eivr_events, 'Z', NULL, chan);
00793                continue;
00794             }
00795             if (!ast_fileexists(&input[2], NULL, u->chan->language)) {
00796                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00797                send_eivr_event(eivr_events, 'Z', &input[2], chan);
00798             } else {
00799                entry = make_entry(&input[2]);
00800                if (entry) {
00801                   AST_LIST_LOCK(&u->playlist);
00802                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00803                   AST_LIST_UNLOCK(&u->playlist);
00804                }
00805             }
00806          } else if (input[0] == EIVR_CMD_GET) {
00807             char response[2048];
00808             ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
00809             ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
00810             send_eivr_event(eivr_events, 'G', response, chan);
00811          } else if (input[0] == EIVR_CMD_SVAR) {
00812             ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
00813             ast_eivr_setvariable(chan, &input[2]);
00814          } else if (input[0] == EIVR_CMD_LOG) {
00815             ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
00816          } else if (input[0] == EIVR_CMD_XIT) {
00817             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00818             ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
00819             res = 0;
00820             break;
00821          } else if (input[0] == EIVR_CMD_EXIT) {
00822             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00823             send_eivr_event(eivr_events, 'E', NULL, chan);
00824             res = 0;
00825             break;
00826          } else if (input[0] == EIVR_CMD_HGUP) {
00827             ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
00828             send_eivr_event(eivr_events, 'H', NULL, chan);
00829             break;
00830          } else if (input[0] == EIVR_CMD_OPT) {
00831             if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
00832                ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
00833                send_eivr_event(eivr_events, 'Z', NULL, chan);
00834                continue;
00835             }
00836             if (!strcasecmp(&input[2], "autoclear"))
00837                u->option_autoclear = 1;
00838             else if (!strcasecmp(&input[2], "noautoclear"))
00839                u->option_autoclear = 0;
00840             else
00841                ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
00842          }
00843       } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
00844          char input[1024];
00845   
00846          if (exception || feof(eivr_errors)) {
00847             ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
00848             break;
00849          }
00850          if (fgets(input, sizeof(input), eivr_errors)) {
00851             ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
00852          }
00853       } else if ((ready_fd < 0) && ms) { 
00854          if (errno == 0 || errno == EINTR)
00855             continue;
00856  
00857          ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
00858          break;
00859       }
00860    }
00861  
00862    exit:
00863    if (test_available_fd > -1) {
00864       close(test_available_fd);
00865    }
00866    if (eivr_events) {
00867       fclose(eivr_events);
00868       *eivr_events_fd = -1;
00869    }
00870    if (eivr_commands) {
00871       fclose(eivr_commands);
00872       *eivr_commands_fd = -1;
00873    }
00874    if (eivr_errors) {
00875       fclose(eivr_errors);
00876       *eivr_errors_fd = -1;
00877    }
00878    return res;
00879 }

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 }

static struct ast_frame* gen_readframe ( struct gen_state state  )  [static]

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 886 of file app_externalivr.c.

References app_exec, and ast_register_application_xml.

00887 {
00888    return ast_register_application_xml(app, app_exec);
00889 }

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 881 of file app_externalivr.c.

References ast_unregister_application().

00882 {
00883    return ast_unregister_application(app);
00884 }


Variable Documentation

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 = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static]

Definition at line 891 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 891 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().


Generated on Mon Oct 8 12:39:07 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7