app_externalivr.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Kevin P. Fleming <kpfleming@digium.com>
00007  *
00008  * Portions taken from the file-based music-on-hold work
00009  * created by Anthony Minessale II in res_musiconhold.c
00010  *
00011  * See http://www.asterisk.org for more information about
00012  * the Asterisk project. Please do not directly contact
00013  * any of the maintainers of this project for assistance;
00014  * the project provides a web site, mailing lists and IRC
00015  * channels for your use.
00016  *
00017  * This program is free software, distributed under the terms of
00018  * the GNU General Public License Version 2. See the LICENSE file
00019  * at the top of the source tree.
00020  */
00021 
00022 /*! \file
00023  *
00024  * \brief External IVR application interface
00025  *
00026  * \author Kevin P. Fleming <kpfleming@digium.com>
00027  *
00028  * \note Portions taken from the file-based music-on-hold work
00029  * created by Anthony Minessale II in res_musiconhold.c
00030  *
00031  * \ingroup applications
00032  */
00033 
00034 /*** MODULEINFO
00035    <support_level>extended</support_level>
00036  ***/
00037 
00038 #include "asterisk.h"
00039 
00040 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 433058 $")
00041 
00042 #include <signal.h>
00043 
00044 #include "asterisk/lock.h"
00045 #include "asterisk/file.h"
00046 #include "asterisk/channel.h"
00047 #include "asterisk/pbx.h"
00048 #include "asterisk/module.h"
00049 #include "asterisk/linkedlists.h"
00050 #include "asterisk/app.h"
00051 #include "asterisk/utils.h"
00052 #include "asterisk/tcptls.h"
00053 #include "asterisk/astobj2.h"
00054 
00055 /*** DOCUMENTATION
00056    <application name="ExternalIVR" language="en_US">
00057       <synopsis>
00058          Interfaces with an external IVR application.
00059       </synopsis>
00060       <syntax>
00061          <parameter name="command|ivr://host" required="true" hasparams="true">
00062             <argument name="arg1" />
00063             <argument name="arg2" multiple="yes" />
00064          </parameter>
00065          <parameter name="options">
00066             <optionlist>
00067                <option name="n">
00068                   <para>Tells ExternalIVR() not to answer the channel.</para>
00069                </option>
00070                <option name="i">
00071                   <para>Tells ExternalIVR() not to send a hangup and exit when the
00072                   channel receives a hangup, instead it sends an <literal>I</literal>
00073                   informative message meaning that the external application MUST hang
00074                   up the call with an <literal>H</literal> command.</para>
00075                </option>
00076                <option name="d">
00077                   <para>Tells ExternalIVR() to run on a channel that has been hung up
00078                   and will not look for hangups. The external application must exit with
00079                   an <literal>E</literal> command.</para>
00080                </option>
00081             </optionlist>
00082          </parameter>
00083       </syntax>
00084       <description>
00085          <para>Either forks a process to run given command or makes a socket to connect
00086          to given host and starts a generator on the channel. The generator's play list
00087          is controlled by the external application, which can add and clear entries via
00088          simple commands issued over its stdout. The external application will receive
00089          all DTMF events received on the channel, and notification if the channel is
00090          hung up. The received on the channel, and notification if the channel is hung
00091          up. The application will not be forcibly terminated when the channel is hung up.
00092          For more information see <filename>doc/AST.pdf</filename>.</para>
00093       </description>
00094    </application>
00095  ***/
00096 
00097 static const char app[] = "ExternalIVR";
00098 
00099 /* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
00100 #define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, ast_channel_name(channel) , ## __VA_ARGS__)
00101 
00102 /* Commands */
00103 #define EIVR_CMD_APND 'A' /* append to prompt queue */
00104 #define EIVR_CMD_DTMF 'D' /* send DTMF */
00105 #define EIVR_CMD_EXIT 'E' /* exit */
00106 #define EIVR_CMD_GET  'G' /* get channel varable(s) */
00107 #define EIVR_CMD_HGUP 'H' /* hangup */
00108 #define EIVR_CMD_IRPT 'I' /* interrupt */
00109 #define EIVR_CMD_LOG  'L' /* log message */
00110 #define EIVR_CMD_OPT  'O' /* option */
00111 #define EIVR_CMD_PARM 'P' /* return supplied params */
00112 #define EIVR_CMD_SQUE 'S' /* (re)set prompt queue */
00113 #define EIVR_CMD_ANS  'T' /* answer channel */
00114 #define EIVR_CMD_SVAR 'V' /* set channel varable(s) */
00115 #define EIVR_CMD_XIT  'X' /* exit **depricated** */
00116 
00117 #define EXTERNALIVR_PORT 2949
00118 
00119 enum options_flags {
00120    noanswer = (1 << 0),
00121    ignore_hangup = (1 << 1),
00122    run_dead = (1 << 2),
00123 };
00124 
00125 AST_APP_OPTIONS(app_opts, {
00126    AST_APP_OPTION('n', noanswer),
00127    AST_APP_OPTION('i', ignore_hangup),
00128    AST_APP_OPTION('d', run_dead),
00129 });
00130 
00131 struct playlist_entry {
00132    AST_LIST_ENTRY(playlist_entry) list;
00133    char filename[1];
00134 };
00135 
00136 struct ivr_localuser {
00137    struct ast_channel *chan;
00138    AST_LIST_HEAD(playlist, playlist_entry) playlist;
00139    AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
00140    int abort_current_sound;
00141    int playing_silence;
00142    int option_autoclear;
00143    int gen_active;
00144 };
00145 
00146 
00147 struct gen_state {
00148    struct ivr_localuser *u;
00149    struct ast_filestream *stream;
00150    struct playlist_entry *current;
00151    int sample_queue;
00152 };
00153 
00154 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
00155    int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, 
00156    const struct ast_str *args, const struct ast_flags flags);
00157 
00158 static void send_eivr_event(FILE *handle, const char event, const char *data,
00159    const struct ast_channel *chan)
00160 {
00161    struct ast_str *tmp = ast_str_create(12);
00162 
00163    ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
00164    if (data) {
00165       ast_str_append(&tmp, 0, ",%s", data);
00166    }
00167 
00168    fprintf(handle, "%s\n", ast_str_buffer(tmp));
00169    ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp));
00170    ast_free(tmp);
00171 }
00172 
00173 static void *gen_alloc(struct ast_channel *chan, void *params)
00174 {
00175    struct ivr_localuser *u = params;
00176    struct gen_state *state;
00177 
00178    if (!(state = ast_calloc(1, sizeof(*state))))
00179       return NULL;
00180 
00181    state->u = u;
00182 
00183    return state;
00184 }
00185 
00186 static void gen_closestream(struct gen_state *state)
00187 {
00188    if (!state->stream)
00189       return;
00190 
00191    ast_closestream(state->stream);
00192    ast_channel_stream_set(state->u->chan, NULL);
00193    state->stream = NULL;
00194 }
00195 
00196 static void gen_release(struct ast_channel *chan, void *data)
00197 {
00198    struct gen_state *state = data;
00199 
00200    gen_closestream(state);
00201    ast_free(data);
00202 }
00203 
00204 /* caller has the playlist locked */
00205 static int gen_nextfile(struct gen_state *state)
00206 {
00207    struct ivr_localuser *u = state->u;
00208    char *file_to_stream;
00209 
00210    u->abort_current_sound = 0;
00211    u->playing_silence = 0;
00212    gen_closestream(state);
00213 
00214    while (!state->stream) {
00215       state->current = AST_LIST_FIRST(&u->playlist);
00216       if (state->current) {
00217          file_to_stream = state->current->filename;
00218       } else {
00219          file_to_stream = "silence/10";
00220          u->playing_silence = 1;
00221       }
00222 
00223       if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, ast_channel_language(u->chan), 1))) {
00224          ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
00225          AST_LIST_LOCK(&u->playlist);
00226          AST_LIST_REMOVE_HEAD(&u->playlist, list);
00227          AST_LIST_UNLOCK(&u->playlist);
00228          if (!u->playing_silence) {
00229             continue;
00230          } else {
00231             break;
00232          }
00233       }
00234    }
00235 
00236    return (!state->stream);
00237 }
00238 
00239 static struct ast_frame *gen_readframe(struct gen_state *state)
00240 {
00241    struct ast_frame *f = NULL;
00242    struct ivr_localuser *u = state->u;
00243 
00244    if (u->abort_current_sound ||
00245       (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
00246       gen_closestream(state);
00247       AST_LIST_LOCK(&u->playlist);
00248       gen_nextfile(state);
00249       AST_LIST_UNLOCK(&u->playlist);
00250    }
00251 
00252    if (!(state->stream && (f = ast_readframe(state->stream)))) {
00253       if (state->current) {
00254          /* remove finished file from playlist */
00255                         AST_LIST_LOCK(&u->playlist);
00256                         AST_LIST_REMOVE_HEAD(&u->playlist, list);
00257                         AST_LIST_UNLOCK(&u->playlist);
00258          /* add finished file to finishlist */
00259          AST_LIST_LOCK(&u->finishlist);
00260          AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
00261          AST_LIST_UNLOCK(&u->finishlist);
00262          state->current = NULL;
00263       }
00264       if (!gen_nextfile(state))
00265          f = ast_readframe(state->stream);
00266    }
00267 
00268    return f;
00269 }
00270 
00271 static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
00272 {
00273    struct gen_state *state = data;
00274    struct ast_frame *f = NULL;
00275    int res = 0;
00276 
00277    state->sample_queue += samples;
00278 
00279    while (state->sample_queue > 0) {
00280       if (!(f = gen_readframe(state)))
00281          return -1;
00282 
00283       res = ast_write(chan, f);
00284       ast_frfree(f);
00285       if (res < 0) {
00286          ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
00287          return -1;
00288       }
00289       state->sample_queue -= f->samples;
00290    }
00291 
00292    return res;
00293 }
00294 
00295 static struct ast_generator gen =
00296 {
00297    .alloc = gen_alloc,
00298    .release = gen_release,
00299    .generate = gen_generate,
00300 };
00301 
00302 static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
00303 {
00304    /* original input data: "G,var1,var2," */
00305    /* data passed as "data":  "var1,var2" */
00306 
00307    char *inbuf, *variable;
00308    const char *value;
00309    int j;
00310    struct ast_str *newstring = ast_str_alloca(outbuflen); 
00311 
00312    outbuf[0] = '\0';
00313 
00314    for (j = 1, inbuf = data; ; j++) {
00315       variable = strsep(&inbuf, ",");
00316       if (variable == NULL) {
00317          int outstrlen = strlen(outbuf);
00318          if (outstrlen && outbuf[outstrlen - 1] == ',') {
00319             outbuf[outstrlen - 1] = 0;
00320          }
00321          break;
00322       }
00323       
00324       ast_channel_lock(chan);
00325       if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
00326          value = "";
00327       }
00328 
00329       ast_str_append(&newstring, 0, "%s=%s,", variable, value);
00330       ast_channel_unlock(chan);
00331       ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen);
00332    }
00333 }
00334 
00335 static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
00336 {
00337    char *value;
00338 
00339    char *inbuf = ast_strdupa(data), *variable;
00340 
00341    for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) {
00342       ast_debug(1, "Setting up a variable: %s\n", variable);
00343       /* variable contains "varname=value" */
00344       value = strchr(variable, '=');
00345       if (!value) {
00346          value = "";
00347       } else {
00348          *value++ = '\0';
00349       }
00350       pbx_builtin_setvar_helper(chan, variable, value);
00351    }
00352 }
00353 
00354 static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
00355 {
00356 
00357    char *data;
00358    int dinterval = 0, duration = 0;
00359    AST_DECLARE_APP_ARGS(args,
00360       AST_APP_ARG(digits);
00361       AST_APP_ARG(dinterval);
00362       AST_APP_ARG(duration);
00363    );
00364 
00365    data = ast_strdupa(vdata);
00366    AST_STANDARD_APP_ARGS(args, data);
00367 
00368    if (!ast_strlen_zero(args.dinterval)) {
00369       ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
00370    }
00371    if (!ast_strlen_zero(args.duration)) {
00372       ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
00373    }
00374    ast_verb(4, "Sending DTMF: %s %d %d\n", args.digits, dinterval <= 0 ? 250 : dinterval, duration);
00375    ast_dtmf_stream(chan, NULL, args.digits, dinterval <= 0 ? 250 : dinterval, duration);
00376 }
00377 
00378 static struct playlist_entry *make_entry(const char *filename)
00379 {
00380    struct playlist_entry *entry;
00381 
00382    if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
00383       return NULL;
00384 
00385    strcpy(entry->filename, filename);
00386 
00387    return entry;
00388 }
00389 
00390 static int app_exec(struct ast_channel *chan, const char *data)
00391 {
00392    struct ast_flags flags = { 0, };
00393    char *opts[0];
00394    struct playlist_entry *entry;
00395    int child_stdin[2] = { -1, -1 };
00396    int child_stdout[2] = { -1, -1 };
00397    int child_stderr[2] = { -1, -1 };
00398    int res = -1;
00399    int pid;
00400 
00401    struct ast_tcptls_session_instance *ser = NULL;
00402 
00403    struct ivr_localuser foo = {
00404       .playlist = AST_LIST_HEAD_INIT_VALUE,
00405       .finishlist = AST_LIST_HEAD_INIT_VALUE,
00406       .gen_active = 0,
00407       .playing_silence = 1,
00408    };
00409    struct ivr_localuser *u = &foo;
00410 
00411    char *buf;
00412    int j;
00413    char *s, **app_args, *e; 
00414    struct ast_str *comma_delim_args = ast_str_alloca(100);
00415 
00416    AST_DECLARE_APP_ARGS(eivr_args,
00417       AST_APP_ARG(application);
00418       AST_APP_ARG(options);
00419    );
00420    AST_DECLARE_APP_ARGS(application_args,
00421       AST_APP_ARG(cmd)[32];
00422    );
00423 
00424    u->abort_current_sound = 0;
00425    u->chan = chan;
00426 
00427    if (ast_strlen_zero(data)) {
00428       ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n");
00429       goto exit;
00430    }
00431 
00432    buf = ast_strdupa(data);
00433    AST_STANDARD_APP_ARGS(eivr_args, buf);
00434 
00435    ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application);
00436    ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options);
00437 
00438    /* Parse out any application arguments */
00439    if ((s = strchr(eivr_args.application, '('))) {
00440       s[0] = ',';
00441       if ((e = strrchr(s, ')'))) {
00442          *e = '\0';
00443       } else {
00444          ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n");
00445          goto exit;
00446       }
00447    }
00448 
00449    AST_STANDARD_APP_ARGS(application_args, eivr_args.application);
00450    app_args = application_args.argv;
00451 
00452    /* Put the application + the arguments in a , delimited list */
00453    ast_str_reset(comma_delim_args);
00454    for (j = 0; application_args.cmd[j] != NULL; j++) {
00455       ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
00456    }
00457 
00458    /* Get rid of any extraneous arguments */
00459    if (eivr_args.options && (s = strchr(eivr_args.options, ','))) {
00460       *s = '\0';
00461    }
00462 
00463    /* Parse the ExternalIVR() arguments */
00464    ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options);
00465    ast_app_parse_options(app_opts, &flags, opts, eivr_args.options);
00466    if (ast_test_flag(&flags, noanswer)) {
00467       ast_verb(4, "noanswer is set\n");
00468    }
00469    if (ast_test_flag(&flags, ignore_hangup)) {
00470       ast_verb(4, "ignore_hangup is set\n");
00471    }
00472    if (ast_test_flag(&flags, run_dead)) {
00473       ast_verb(4, "run_dead is set\n");
00474    }
00475    
00476    if (!(ast_test_flag(&flags, noanswer))) {
00477       ast_verb(3, "Answering channel and starting generator\n");
00478       if (ast_channel_state(chan) != AST_STATE_UP) {
00479          if (ast_test_flag(&flags, run_dead)) {
00480             ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
00481             goto exit;
00482          }
00483          ast_answer(chan);
00484       }
00485       if (ast_activate_generator(chan, &gen, u) < 0) {
00486          ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n");
00487          goto exit;
00488       } else {
00489          u->gen_active = 1;
00490       }
00491    }
00492 
00493    if (!strncmp(app_args[0], "ivr://", sizeof("ivr://") - 1)) {
00494       struct ast_tcptls_session_args ivr_desc = {
00495          .accept_fd = -1,
00496          .name = "IVR",
00497       };
00498       struct ast_sockaddr *addrs;
00499       int num_addrs = 0, i = 0;
00500       char *host = app_args[0] + sizeof("ivr://") - 1;
00501 
00502       /* Communicate through socket to server */
00503       ast_debug(1, "Parsing hostname/port for socket connect from \"%s\"\n", host);
00504 
00505       if (!(num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC))) {
00506          ast_chan_log(LOG_ERROR, chan, "Unable to locate host '%s'\n", host);
00507          goto exit;
00508       }
00509 
00510       for (i = 0; i < num_addrs; i++) {
00511          if (!ast_sockaddr_port(&addrs[i])) {
00512             /* Default port if not specified */
00513             ast_sockaddr_set_port(&addrs[i], EXTERNALIVR_PORT);
00514          }
00515          ast_sockaddr_copy(&ivr_desc.remote_address, &addrs[i]);
00516          if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) {
00517             continue;
00518          }
00519          break;
00520       }
00521 
00522       ast_free(addrs);
00523 
00524       if (i == num_addrs) {
00525          ast_chan_log(LOG_ERROR, chan, "Could not connect to any host.  ExternalIVR failed.\n");
00526          goto exit;
00527       }
00528 
00529       res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags);
00530 
00531    } else {
00532       if (pipe(child_stdin)) {
00533          ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno));
00534          goto exit;
00535       }
00536       if (pipe(child_stdout)) {
00537          ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno));
00538          goto exit;
00539       }
00540       if (pipe(child_stderr)) {
00541          ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
00542          goto exit;
00543       }
00544    
00545       pid = ast_safe_fork(0);
00546       if (pid < 0) {
00547          ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno));
00548          goto exit;
00549       }
00550    
00551       if (!pid) {
00552          /* child process */
00553          if (ast_opt_high_priority)
00554             ast_set_priority(0);
00555    
00556          dup2(child_stdin[0], STDIN_FILENO);
00557          dup2(child_stdout[1], STDOUT_FILENO);
00558          dup2(child_stderr[1], STDERR_FILENO);
00559          ast_close_fds_above_n(STDERR_FILENO);
00560          execv(app_args[0], app_args);
00561          fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
00562          _exit(1);
00563       } else {
00564          /* parent process */
00565          close(child_stdin[0]);
00566          child_stdin[0] = -1;
00567          close(child_stdout[1]);
00568          child_stdout[1] = -1;
00569          close(child_stderr[1]);
00570          child_stderr[1] = -1;
00571          res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags);
00572       }
00573    }
00574 
00575    exit:
00576    if (u->gen_active) {
00577       ast_deactivate_generator(chan);
00578    }
00579    if (child_stdin[0] > -1) {
00580       close(child_stdin[0]);
00581    }
00582    if (child_stdin[1] > -1) {
00583       close(child_stdin[1]);
00584    }
00585    if (child_stdout[0] > -1) {
00586       close(child_stdout[0]);
00587    }
00588    if (child_stdout[1] > -1) {
00589       close(child_stdout[1]);
00590    }
00591    if (child_stderr[0] > -1) {
00592       close(child_stderr[0]);
00593    }
00594    if (child_stderr[1] > -1) {
00595       close(child_stderr[1]);
00596    }
00597    if (ser) {
00598       ao2_ref(ser, -1);
00599    }
00600    while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00601       ast_free(entry);
00602    }
00603    return res;
00604 }
00605 
00606 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
00607             int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, 
00608             const struct ast_str *args, const struct ast_flags flags)
00609 {
00610    struct playlist_entry *entry;
00611    struct ast_frame *f;
00612    int ms;
00613    int exception;
00614    int ready_fd;
00615    int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 };
00616    struct ast_channel *rchan;
00617    int res = -1;
00618    int test_available_fd = -1;
00619    int hangup_info_sent = 0;
00620   
00621    FILE *eivr_commands = NULL;
00622    FILE *eivr_errors = NULL;
00623    FILE *eivr_events = NULL;
00624 
00625    if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) {
00626       ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n");
00627       goto exit;
00628    }
00629    if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) {
00630       ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n");
00631       goto exit;
00632    }
00633    if (eivr_errors_fd) {  /* if opening a socket connection, error stream will not be used */
00634       if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) {
00635          ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n");
00636          goto exit;
00637       }
00638    }
00639 
00640    test_available_fd = open("/dev/null", O_RDONLY);
00641  
00642    setvbuf(eivr_events, NULL, _IONBF, 0);
00643    setvbuf(eivr_commands, NULL, _IONBF, 0);
00644    if (eivr_errors) {
00645       setvbuf(eivr_errors, NULL, _IONBF, 0);
00646    }
00647 
00648    while (1) {
00649       if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) {
00650          ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
00651          break;
00652       }
00653       if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
00654          if (ast_test_flag(&flags, ignore_hangup)) {
00655             ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
00656             send_eivr_event(eivr_events, 'I', "HANGUP", chan);
00657             hangup_info_sent = 1;
00658          } else {
00659             ast_verb(3, "Got check_hangup\n");
00660             send_eivr_event(eivr_events, 'H', NULL, chan);
00661             break;
00662          }
00663       }
00664  
00665       ready_fd = 0;
00666       ms = 100;
00667       errno = 0;
00668       exception = 0;
00669  
00670       rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms);
00671  
00672       if (ast_channel_state(chan) == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
00673          AST_LIST_LOCK(&u->finishlist);
00674          while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
00675             send_eivr_event(eivr_events, 'F', entry->filename, chan);
00676             ast_free(entry);
00677          }
00678          AST_LIST_UNLOCK(&u->finishlist);
00679       }
00680  
00681       if (ast_channel_state(chan) == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
00682          /* the channel has something */
00683          f = ast_read(chan);
00684          if (!f) {
00685             ast_verb(3, "Returned no frame\n");
00686             send_eivr_event(eivr_events, 'H', NULL, chan);
00687             break;
00688          }
00689          if (f->frametype == AST_FRAME_DTMF) {
00690             send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
00691             if (u->option_autoclear) {
00692                AST_LIST_LOCK(&u->playlist);
00693                if (!u->abort_current_sound && !u->playing_silence) {
00694                   /* send interrupted file as T data */
00695                   if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00696                      send_eivr_event(eivr_events, 'T', entry->filename, chan);
00697                      ast_free(entry);
00698                   }
00699                }
00700                while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00701                   send_eivr_event(eivr_events, 'D', entry->filename, chan);
00702                   ast_free(entry);
00703                }
00704                if (!u->playing_silence)
00705                   u->abort_current_sound = 1;
00706                AST_LIST_UNLOCK(&u->playlist);
00707             }
00708          } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
00709             ast_verb(3, "Got AST_CONTROL_HANGUP\n");
00710             send_eivr_event(eivr_events, 'H', NULL, chan);
00711             if (f->data.uint32) {
00712                ast_channel_hangupcause_set(chan, f->data.uint32);
00713             }
00714             ast_frfree(f);
00715             break;
00716          }
00717          ast_frfree(f);
00718       } else if (ready_fd == *eivr_commands_fd) {
00719          char input[1024];
00720  
00721          if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
00722             ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
00723             break;
00724          }
00725   
00726          if (!fgets(input, sizeof(input), eivr_commands)) {
00727             continue;
00728          }
00729 
00730          ast_strip(input);
00731          ast_verb(4, "got command '%s'\n", input);
00732 
00733          if (strlen(input) < 3) {
00734             continue;
00735          }
00736 
00737          if (input[0] == EIVR_CMD_PARM) {
00738             struct ast_str *tmp = (struct ast_str *) args;
00739             send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
00740          } else if (input[0] == EIVR_CMD_DTMF) {
00741             ast_verb(4, "Sending DTMF: %s\n", &input[2]);
00742             ast_eivr_senddtmf(chan, &input[2]);
00743          } else if (input[0] == EIVR_CMD_ANS) {
00744             ast_verb(3, "Answering channel if needed and starting generator\n");
00745             if (ast_channel_state(chan) != AST_STATE_UP) {
00746                if (ast_test_flag(&flags, run_dead)) {
00747                   ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
00748                   send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
00749                   continue;
00750                }
00751                if (ast_answer(chan)) {
00752                   ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n");
00753                   send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
00754                   continue;
00755                }
00756             }
00757             if (!(u->gen_active)) {
00758                if (ast_activate_generator(chan, &gen, u) < 0) {
00759                   ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
00760                   send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
00761                } else {
00762                   u->gen_active = 1;
00763                }
00764             }
00765          } else if (input[0] == EIVR_CMD_IRPT) {
00766             if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
00767                ast_chan_log(LOG_WARNING, chan, "Queue 'I'nterrupt called on unanswered channel\n");
00768                send_eivr_event(eivr_events, 'Z', NULL, chan);
00769                continue;
00770             }
00771             AST_LIST_LOCK(&u->playlist);
00772             if (!u->abort_current_sound && !u->playing_silence) {
00773                /* send interrupted file as T data */
00774                if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00775                   send_eivr_event(eivr_events, 'T', entry->filename, chan);
00776                   ast_free(entry);
00777                }
00778             }
00779             while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00780                send_eivr_event(eivr_events, 'D', entry->filename, chan);
00781                ast_free(entry);
00782             }
00783             if (!u->playing_silence) {
00784                u->abort_current_sound = 1;
00785             }
00786             AST_LIST_UNLOCK(&u->playlist);
00787          } else if (input[0] == EIVR_CMD_SQUE) {
00788             if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
00789                ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n");
00790                send_eivr_event(eivr_events, 'Z', NULL, chan);
00791                continue;
00792             }
00793             if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
00794                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00795                send_eivr_event(eivr_events, 'Z', &input[2], chan);
00796             } else {
00797                AST_LIST_LOCK(&u->playlist);
00798                if (!u->abort_current_sound && !u->playing_silence) {
00799                   /* send interrupted file as T data */
00800                   if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00801                      send_eivr_event(eivr_events, 'T', entry->filename, chan);
00802                      ast_free(entry);
00803                   }
00804                }
00805                while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
00806                   send_eivr_event(eivr_events, 'D', entry->filename, chan);
00807                   ast_free(entry);
00808                }
00809                if (!u->playing_silence) {
00810                   u->abort_current_sound = 1;
00811                }
00812                entry = make_entry(&input[2]);
00813                if (entry) {
00814                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00815                }
00816                AST_LIST_UNLOCK(&u->playlist);
00817             }
00818          } else if (input[0] == EIVR_CMD_APND) {
00819             if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
00820                ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
00821                send_eivr_event(eivr_events, 'Z', NULL, chan);
00822                continue;
00823             }
00824             if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
00825                ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
00826                send_eivr_event(eivr_events, 'Z', &input[2], chan);
00827             } else {
00828                entry = make_entry(&input[2]);
00829                if (entry) {
00830                   AST_LIST_LOCK(&u->playlist);
00831                   AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
00832                   AST_LIST_UNLOCK(&u->playlist);
00833                }
00834             }
00835          } else if (input[0] == EIVR_CMD_GET) {
00836             char response[2048];
00837             ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
00838             ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
00839             send_eivr_event(eivr_events, 'G', response, chan);
00840          } else if (input[0] == EIVR_CMD_SVAR) {
00841             ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
00842             ast_eivr_setvariable(chan, &input[2]);
00843          } else if (input[0] == EIVR_CMD_LOG) {
00844             ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
00845          } else if (input[0] == EIVR_CMD_XIT) {
00846             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00847             ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
00848             res = 0;
00849             break;
00850          } else if (input[0] == EIVR_CMD_EXIT) {
00851             ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
00852             send_eivr_event(eivr_events, 'E', NULL, chan);
00853             res = 0;
00854             break;
00855          } else if (input[0] == EIVR_CMD_HGUP) {
00856             ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
00857             send_eivr_event(eivr_events, 'H', NULL, chan);
00858             break;
00859          } else if (input[0] == EIVR_CMD_OPT) {
00860             if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
00861                ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
00862                send_eivr_event(eivr_events, 'Z', NULL, chan);
00863                continue;
00864             }
00865             if (!strcasecmp(&input[2], "autoclear"))
00866                u->option_autoclear = 1;
00867             else if (!strcasecmp(&input[2], "noautoclear"))
00868                u->option_autoclear = 0;
00869             else
00870                ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
00871          }
00872       } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
00873          char input[1024];
00874   
00875          if (exception || feof(eivr_errors)) {
00876             ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
00877             break;
00878          }
00879          if (fgets(input, sizeof(input), eivr_errors)) {
00880             ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
00881          }
00882       } else if ((ready_fd < 0) && ms) { 
00883          if (errno == 0 || errno == EINTR)
00884             continue;
00885  
00886          ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
00887          break;
00888       }
00889    }
00890  
00891    exit:
00892    if (test_available_fd > -1) {
00893       close(test_available_fd);
00894    }
00895    if (eivr_events) {
00896       fclose(eivr_events);
00897       *eivr_events_fd = -1;
00898    }
00899    if (eivr_commands) {
00900       fclose(eivr_commands);
00901       *eivr_commands_fd = -1;
00902    }
00903    if (eivr_errors) {
00904       fclose(eivr_errors);
00905       *eivr_errors_fd = -1;
00906    }
00907    return res;
00908 }
00909 
00910 static int unload_module(void)
00911 {
00912    return ast_unregister_application(app);
00913 }
00914 
00915 static int load_module(void)
00916 {
00917    return ast_register_application_xml(app, app_exec);
00918 }
00919 
00920 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "External IVR Interface Application");
00921 

Generated on Thu Apr 16 06:27:09 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6