Mon Aug 31 12:29:58 2015

Asterisk developer's documentation


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: 362586 $")
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, channel->name , ## __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_LOG  'L' /* log message */
00109 #define EIVR_CMD_OPT  'O' /* option */
00110 #define EIVR_CMD_PARM 'P' /* return supplied params */
00111 #define EIVR_CMD_SQUE 'S' /* (re)set prompt queue */
00112 #define EIVR_CMD_ANS  'T' /* answer channel */
00113 #define EIVR_CMD_SVAR 'V' /* set channel varable(s) */
00114 #define EIVR_CMD_XIT  'X' /* exit **depricated** */
00115 
00116 enum options_flags {
00117    noanswer = (1 << 0),
00118    ignore_hangup = (1 << 1),
00119    run_dead = (1 << 2),
00120 };
00121 
00122 AST_APP_OPTIONS(app_opts, {
00123    AST_APP_OPTION('n', noanswer),
00124    AST_APP_OPTION('i', ignore_hangup),
00125    AST_APP_OPTION('d', run_dead),
00126 });
00127 
00128 struct playlist_entry {
00129    AST_LIST_ENTRY(playlist_entry) list;
00130    char filename[1];
00131 };
00132 
00133 struct ivr_localuser {
00134    struct ast_channel *chan;
00135    AST_LIST_HEAD(playlist, playlist_entry) playlist;
00136    AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
00137    int abort_current_sound;
00138    int playing_silence;
00139    int option_autoclear;
00140    int gen_active;
00141 };
00142 
00143 
00144 struct gen_state {
00145    struct ivr_localuser *u;
00146    struct ast_filestream *stream;
00147    struct playlist_entry *current;
00148    int sample_queue;
00149 };
00150 
00151 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
00152    int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, 
00153    const struct ast_str *args, const struct ast_flags flags);
00154 
00155 int eivr_connect_socket(struct ast_channel *chan, const char *host, int port);
00156 
00157 static void send_eivr_event(FILE *handle, const char event, const char *data,
00158    const struct ast_channel *chan)
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 }
00171 
00172 static void *gen_alloc(struct ast_channel *chan, void *params)
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 }
00184 
00185 static void gen_closestream(struct gen_state *state)
00186 {
00187    if (!state->stream)
00188       return;
00189 
00190    ast_closestream(state->stream);
00191    state->u->chan->stream = NULL;
00192    state->stream = NULL;
00193 }
00194 
00195 static void gen_release(struct ast_channel *chan, void *data)
00196 {
00197    struct gen_state *state = data;
00198 
00199    gen_closestream(state);
00200    ast_free(data);
00201 }
00202 
00203 /* caller has the playlist locked */
00204 static int gen_nextfile(struct gen_state *state)
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 }
00237 
00238 static struct ast_frame *gen_readframe(struct gen_state *state)
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 }
00269 
00270 static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
00271 {
00272    struct gen_state *state = data;
00273    struct ast_frame *f = NULL;
00274    int res = 0;
00275 
00276    state->sample_queue += samples;
00277 
00278    while (state->sample_queue > 0) {
00279       if (!(f = gen_readframe(state)))
00280          return -1;
00281 
00282       res = ast_write(chan, f);
00283       ast_frfree(f);
00284       if (res < 0) {
00285          ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
00286          return -1;
00287       }
00288       state->sample_queue -= f->samples;
00289    }
00290 
00291    return res;
00292 }
00293 
00294 static struct ast_generator gen =
00295 {
00296    .alloc = gen_alloc,
00297    .release = gen_release,
00298    .generate = gen_generate,
00299 };
00300 
00301 static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
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 }
00333 
00334 static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
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 }
00352 
00353 static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
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 }
00376 
00377 static struct playlist_entry *make_entry(const char *filename)
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 }
00388 
00389 static int app_exec(struct ast_channel *chan, const char *data)
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 }
00598 
00599 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
00600             int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, 
00601             const struct ast_str *args, const struct ast_flags flags)
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 }
00880 
00881 static int unload_module(void)
00882 {
00883    return ast_unregister_application(app);
00884 }
00885 
00886 static int load_module(void)
00887 {
00888    return ast_register_application_xml(app, app_exec);
00889 }
00890 
00891 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");

Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1