Wed Apr 6 11:29:49 2011

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 = "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_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 96 of file app_externalivr.c.

Referenced by eivr_comm(), gen_generate(), and gen_nextfile().

#define EIVR_CMD_ANS   'T'

Definition at line 108 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_APND   'A'

Definition at line 99 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_DTMF   'D'

Definition at line 100 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_EXIT   'E'

Definition at line 101 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_GET   'G'

Definition at line 102 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_HGUP   'H'

Definition at line 103 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_LOG   'L'

Definition at line 104 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_OPT   'O'

Definition at line 105 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_PARM   'P'

Definition at line 106 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_SQUE   'S'

Definition at line 107 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_SVAR   'V'

Definition at line 109 of file app_externalivr.c.

Referenced by eivr_comm().

#define EIVR_CMD_XIT   'X'

Definition at line 110 of file app_externalivr.c.

Referenced by eivr_comm().


Enumeration Type Documentation

enum options_flags

Enumerator:
noanswer 
ignore_hangup 
run_dead 

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


Function Documentation

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 }

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

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 }


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 = "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().


Generated on Wed Apr 6 11:29:49 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7