Sat Feb 11 06:33:01 2012

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

Generated on Sat Feb 11 06:33:01 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6