2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * Kevin P. Fleming <kpfleming@digium.com>
8 * Portions taken from the file-based music-on-hold work
9 * created by Anthony Minessale II in res_musiconhold.c
11 * See http://www.asterisk.org for more information about
12 * the Asterisk project. Please do not directly contact
13 * any of the maintainers of this project for assistance;
14 * the project provides a web site, mailing lists and IRC
15 * channels for your use.
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2. See the LICENSE file
19 * at the top of the source tree.
24 * \brief External IVR application interface
26 * \author Kevin P. Fleming <kpfleming@digium.com>
28 * \note Portions taken from the file-based music-on-hold work
29 * created by Anthony Minessale II in res_musiconhold.c
31 * \ingroup applications
35 <support_level>extended</support_level>
40 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
44 #include "asterisk/lock.h"
45 #include "asterisk/file.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/module.h"
49 #include "asterisk/linkedlists.h"
50 #include "asterisk/app.h"
51 #include "asterisk/utils.h"
52 #include "asterisk/tcptls.h"
53 #include "asterisk/astobj2.h"
56 <application name="ExternalIVR" language="en_US">
58 Interfaces with an external IVR application.
61 <parameter name="command|ivr://host" required="true" hasparams="true">
62 <argument name="arg1" />
63 <argument name="arg2" multiple="yes" />
65 <parameter name="options">
68 <para>Tells ExternalIVR() not to answer the channel.</para>
71 <para>Tells ExternalIVR() not to send a hangup and exit when the
72 channel receives a hangup, instead it sends an <literal>I</literal>
73 informative message meaning that the external application MUST hang
74 up the call with an <literal>H</literal> command.</para>
77 <para>Tells ExternalIVR() to run on a channel that has been hung up
78 and will not look for hangups. The external application must exit with
79 an <literal>E</literal> command.</para>
85 <para>Either forks a process to run given command or makes a socket to connect
86 to given host and starts a generator on the channel. The generator's play list
87 is controlled by the external application, which can add and clear entries via
88 simple commands issued over its stdout. The external application will receive
89 all DTMF events received on the channel, and notification if the channel is
90 hung up. The received on the channel, and notification if the channel is hung
91 up. The application will not be forcibly terminated when the channel is hung up.
92 For more information see <filename>doc/AST.pdf</filename>.</para>
97 static const char app[] = "ExternalIVR";
99 /* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
100 #define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, ast_channel_name(channel) , ## __VA_ARGS__)
103 #define EIVR_CMD_APND 'A' /* append to prompt queue */
104 #define EIVR_CMD_DTMF 'D' /* send DTMF */
105 #define EIVR_CMD_EXIT 'E' /* exit */
106 #define EIVR_CMD_GET 'G' /* get channel varable(s) */
107 #define EIVR_CMD_HGUP 'H' /* hangup */
108 #define EIVR_CMD_IRPT 'I' /* interrupt */
109 #define EIVR_CMD_LOG 'L' /* log message */
110 #define EIVR_CMD_OPT 'O' /* option */
111 #define EIVR_CMD_PARM 'P' /* return supplied params */
112 #define EIVR_CMD_SQUE 'S' /* (re)set prompt queue */
113 #define EIVR_CMD_ANS 'T' /* answer channel */
114 #define EIVR_CMD_SVAR 'V' /* set channel varable(s) */
115 #define EIVR_CMD_XIT 'X' /* exit **depricated** */
117 #define EXTERNALIVR_PORT 2949
121 ignore_hangup = (1 << 1),
125 AST_APP_OPTIONS(app_opts, {
126 AST_APP_OPTION('n', noanswer),
127 AST_APP_OPTION('i', ignore_hangup),
128 AST_APP_OPTION('d', run_dead),
131 struct playlist_entry {
132 AST_LIST_ENTRY(playlist_entry) list;
136 struct ivr_localuser {
137 struct ast_channel *chan;
138 AST_LIST_HEAD(playlist, playlist_entry) playlist;
139 AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
140 int abort_current_sound;
142 int option_autoclear;
148 struct ivr_localuser *u;
149 struct ast_filestream *stream;
150 struct playlist_entry *current;
154 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
155 int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd,
156 const struct ast_str *args, const struct ast_flags flags);
158 int eivr_connect_socket(struct ast_channel *chan, const char *host, int port);
160 static void send_eivr_event(FILE *handle, const char event, const char *data,
161 const struct ast_channel *chan)
163 struct ast_str *tmp = ast_str_create(12);
165 ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
167 ast_str_append(&tmp, 0, ",%s", data);
170 fprintf(handle, "%s\n", ast_str_buffer(tmp));
171 ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp));
175 static void *gen_alloc(struct ast_channel *chan, void *params)
177 struct ivr_localuser *u = params;
178 struct gen_state *state;
180 if (!(state = ast_calloc(1, sizeof(*state))))
188 static void gen_closestream(struct gen_state *state)
193 ast_closestream(state->stream);
194 ast_channel_stream_set(state->u->chan, NULL);
195 state->stream = NULL;
198 static void gen_release(struct ast_channel *chan, void *data)
200 struct gen_state *state = data;
202 gen_closestream(state);
206 /* caller has the playlist locked */
207 static int gen_nextfile(struct gen_state *state)
209 struct ivr_localuser *u = state->u;
210 char *file_to_stream;
212 u->abort_current_sound = 0;
213 u->playing_silence = 0;
214 gen_closestream(state);
216 while (!state->stream) {
217 state->current = AST_LIST_FIRST(&u->playlist);
218 if (state->current) {
219 file_to_stream = state->current->filename;
221 file_to_stream = "silence/10";
222 u->playing_silence = 1;
225 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, ast_channel_language(u->chan), 1))) {
226 ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
227 AST_LIST_LOCK(&u->playlist);
228 AST_LIST_REMOVE_HEAD(&u->playlist, list);
229 AST_LIST_UNLOCK(&u->playlist);
230 if (!u->playing_silence) {
238 return (!state->stream);
241 static struct ast_frame *gen_readframe(struct gen_state *state)
243 struct ast_frame *f = NULL;
244 struct ivr_localuser *u = state->u;
246 if (u->abort_current_sound ||
247 (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
248 gen_closestream(state);
249 AST_LIST_LOCK(&u->playlist);
251 AST_LIST_UNLOCK(&u->playlist);
254 if (!(state->stream && (f = ast_readframe(state->stream)))) {
255 if (state->current) {
256 /* remove finished file from playlist */
257 AST_LIST_LOCK(&u->playlist);
258 AST_LIST_REMOVE_HEAD(&u->playlist, list);
259 AST_LIST_UNLOCK(&u->playlist);
260 /* add finished file to finishlist */
261 AST_LIST_LOCK(&u->finishlist);
262 AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
263 AST_LIST_UNLOCK(&u->finishlist);
264 state->current = NULL;
266 if (!gen_nextfile(state))
267 f = ast_readframe(state->stream);
273 static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
275 struct gen_state *state = data;
276 struct ast_frame *f = NULL;
279 state->sample_queue += samples;
281 while (state->sample_queue > 0) {
282 if (!(f = gen_readframe(state)))
285 res = ast_write(chan, f);
288 ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
291 state->sample_queue -= f->samples;
297 static struct ast_generator gen =
300 .release = gen_release,
301 .generate = gen_generate,
304 static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
306 /* original input data: "G,var1,var2," */
307 /* data passed as "data": "var1,var2" */
309 char *inbuf, *variable;
312 struct ast_str *newstring = ast_str_alloca(outbuflen);
316 for (j = 1, inbuf = data; ; j++) {
317 variable = strsep(&inbuf, ",");
318 if (variable == NULL) {
319 int outstrlen = strlen(outbuf);
320 if (outstrlen && outbuf[outstrlen - 1] == ',') {
321 outbuf[outstrlen - 1] = 0;
326 ast_channel_lock(chan);
327 if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
331 ast_str_append(&newstring, 0, "%s=%s,", variable, value);
332 ast_channel_unlock(chan);
333 ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen);
337 static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
341 char *inbuf = ast_strdupa(data), *variable;
343 for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) {
344 ast_debug(1, "Setting up a variable: %s\n", variable);
345 /* variable contains "varname=value" */
346 value = strchr(variable, '=');
352 pbx_builtin_setvar_helper(chan, variable, value);
356 static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
360 int dinterval = 0, duration = 0;
361 AST_DECLARE_APP_ARGS(args,
363 AST_APP_ARG(dinterval);
364 AST_APP_ARG(duration);
367 data = ast_strdupa(vdata);
368 AST_STANDARD_APP_ARGS(args, data);
370 if (!ast_strlen_zero(args.dinterval)) {
371 ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
373 if (!ast_strlen_zero(args.duration)) {
374 ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
376 ast_verb(4, "Sending DTMF: %s %d %d\n", args.digits, dinterval <= 0 ? 250 : dinterval, duration);
377 ast_dtmf_stream(chan, NULL, args.digits, dinterval <= 0 ? 250 : dinterval, duration);
380 static struct playlist_entry *make_entry(const char *filename)
382 struct playlist_entry *entry;
384 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
387 strcpy(entry->filename, filename);
392 static int app_exec(struct ast_channel *chan, const char *data)
394 struct ast_flags flags = { 0, };
396 struct playlist_entry *entry;
397 int child_stdin[2] = { -1, -1 };
398 int child_stdout[2] = { -1, -1 };
399 int child_stderr[2] = { -1, -1 };
403 struct ast_tcptls_session_instance *ser = NULL;
405 struct ivr_localuser foo = {
406 .playlist = AST_LIST_HEAD_INIT_VALUE,
407 .finishlist = AST_LIST_HEAD_INIT_VALUE,
409 .playing_silence = 1,
411 struct ivr_localuser *u = &foo;
415 char *s, **app_args, *e;
416 struct ast_str *comma_delim_args = ast_str_alloca(100);
418 AST_DECLARE_APP_ARGS(eivr_args,
419 AST_APP_ARG(application);
420 AST_APP_ARG(options);
422 AST_DECLARE_APP_ARGS(application_args,
423 AST_APP_ARG(cmd)[32];
426 u->abort_current_sound = 0;
429 if (ast_strlen_zero(data)) {
430 ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n");
434 buf = ast_strdupa(data);
435 AST_STANDARD_APP_ARGS(eivr_args, buf);
437 ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application);
438 ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options);
440 /* Parse out any application arguments */
441 if ((s = strchr(eivr_args.application, '('))) {
443 if ((e = strrchr(s, ')'))) {
446 ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n");
451 AST_STANDARD_APP_ARGS(application_args, eivr_args.application);
452 app_args = application_args.argv;
454 /* Put the application + the arguments in a , delimited list */
455 ast_str_reset(comma_delim_args);
456 for (j = 0; application_args.cmd[j] != NULL; j++) {
457 ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
460 /* Get rid of any extraneous arguments */
461 if (eivr_args.options && (s = strchr(eivr_args.options, ','))) {
465 /* Parse the ExternalIVR() arguments */
466 ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options);
467 ast_app_parse_options(app_opts, &flags, opts, eivr_args.options);
468 if (ast_test_flag(&flags, noanswer)) {
469 ast_verb(4, "noanswer is set\n");
471 if (ast_test_flag(&flags, ignore_hangup)) {
472 ast_verb(4, "ignore_hangup is set\n");
474 if (ast_test_flag(&flags, run_dead)) {
475 ast_verb(4, "run_dead is set\n");
478 if (!(ast_test_flag(&flags, noanswer))) {
479 ast_verb(3, "Answering channel and starting generator\n");
480 if (ast_channel_state(chan) != AST_STATE_UP) {
481 if (ast_test_flag(&flags, run_dead)) {
482 ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
487 if (ast_activate_generator(chan, &gen, u) < 0) {
488 ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n");
495 if (!strncmp(app_args[0], "ivr://", sizeof("ivr://") - 1)) {
496 struct ast_tcptls_session_args ivr_desc = {
500 struct ast_sockaddr *addrs;
501 int num_addrs = 0, i = 0;
502 char *host = app_args[0] + sizeof("ivr://") - 1;
504 /* Communicate through socket to server */
505 ast_debug(1, "Parsing hostname/port for socket connect from \"%s\"\n", host);
507 if (!(num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC))) {
508 ast_chan_log(LOG_ERROR, chan, "Unable to locate host '%s'\n", host);
512 for (i = 0; i < num_addrs; i++) {
513 if (!ast_sockaddr_port(&addrs[i])) {
514 /* Default port if not specified */
515 ast_sockaddr_set_port(&addrs[i], EXTERNALIVR_PORT);
517 ast_sockaddr_copy(&ivr_desc.remote_address, &addrs[i]);
518 if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) {
524 if (i == num_addrs) {
525 ast_chan_log(LOG_ERROR, chan, "Could not connect to any host. ExternalIVR failed.\n");
529 res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags);
532 if (pipe(child_stdin)) {
533 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno));
536 if (pipe(child_stdout)) {
537 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno));
540 if (pipe(child_stderr)) {
541 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
545 pid = ast_safe_fork(0);
547 ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno));
553 if (ast_opt_high_priority)
556 dup2(child_stdin[0], STDIN_FILENO);
557 dup2(child_stdout[1], STDOUT_FILENO);
558 dup2(child_stderr[1], STDERR_FILENO);
559 ast_close_fds_above_n(STDERR_FILENO);
560 execv(app_args[0], app_args);
561 fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
565 close(child_stdin[0]);
567 close(child_stdout[1]);
568 child_stdout[1] = -1;
569 close(child_stderr[1]);
570 child_stderr[1] = -1;
571 res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags);
577 ast_deactivate_generator(chan);
579 if (child_stdin[0] > -1) {
580 close(child_stdin[0]);
582 if (child_stdin[1] > -1) {
583 close(child_stdin[1]);
585 if (child_stdout[0] > -1) {
586 close(child_stdout[0]);
588 if (child_stdout[1] > -1) {
589 close(child_stdout[1]);
591 if (child_stderr[0] > -1) {
592 close(child_stderr[0]);
594 if (child_stderr[1] > -1) {
595 close(child_stderr[1]);
600 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
606 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
607 int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd,
608 const struct ast_str *args, const struct ast_flags flags)
610 struct playlist_entry *entry;
615 int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 };
616 struct ast_channel *rchan;
618 int test_available_fd = -1;
619 int hangup_info_sent = 0;
621 FILE *eivr_commands = NULL;
622 FILE *eivr_errors = NULL;
623 FILE *eivr_events = NULL;
625 if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) {
626 ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n");
629 if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) {
630 ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n");
633 if (eivr_errors_fd) { /* if opening a socket connection, error stream will not be used */
634 if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) {
635 ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n");
640 test_available_fd = open("/dev/null", O_RDONLY);
642 setvbuf(eivr_events, NULL, _IONBF, 0);
643 setvbuf(eivr_commands, NULL, _IONBF, 0);
645 setvbuf(eivr_errors, NULL, _IONBF, 0);
649 if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) {
650 ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
653 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
654 if (ast_test_flag(&flags, ignore_hangup)) {
655 ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
656 send_eivr_event(eivr_events, 'I', "HANGUP", chan);
657 hangup_info_sent = 1;
659 ast_verb(3, "Got check_hangup\n");
660 send_eivr_event(eivr_events, 'H', NULL, chan);
670 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms);
672 if (ast_channel_state(chan) == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
673 AST_LIST_LOCK(&u->finishlist);
674 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
675 send_eivr_event(eivr_events, 'F', entry->filename, chan);
678 AST_LIST_UNLOCK(&u->finishlist);
681 if (ast_channel_state(chan) == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
682 /* the channel has something */
685 ast_verb(3, "Returned no frame\n");
686 send_eivr_event(eivr_events, 'H', NULL, chan);
689 if (f->frametype == AST_FRAME_DTMF) {
690 send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
691 if (u->option_autoclear) {
692 AST_LIST_LOCK(&u->playlist);
693 if (!u->abort_current_sound && !u->playing_silence) {
694 /* send interrupted file as T data */
695 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
696 send_eivr_event(eivr_events, 'T', entry->filename, chan);
700 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
701 send_eivr_event(eivr_events, 'D', entry->filename, chan);
704 if (!u->playing_silence)
705 u->abort_current_sound = 1;
706 AST_LIST_UNLOCK(&u->playlist);
708 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
709 ast_verb(3, "Got AST_CONTROL_HANGUP\n");
710 send_eivr_event(eivr_events, 'H', NULL, chan);
711 if (f->data.uint32) {
712 ast_channel_hangupcause_set(chan, f->data.uint32);
718 } else if (ready_fd == *eivr_commands_fd) {
721 if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
722 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
726 if (!fgets(input, sizeof(input), eivr_commands)) {
731 ast_verb(4, "got command '%s'\n", input);
733 if (strlen(input) < 3) {
737 if (input[0] == EIVR_CMD_PARM) {
738 struct ast_str *tmp = (struct ast_str *) args;
739 send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
740 } else if (input[0] == EIVR_CMD_DTMF) {
741 ast_verb(4, "Sending DTMF: %s\n", &input[2]);
742 ast_eivr_senddtmf(chan, &input[2]);
743 } else if (input[0] == EIVR_CMD_ANS) {
744 ast_verb(3, "Answering channel if needed and starting generator\n");
745 if (ast_channel_state(chan) != AST_STATE_UP) {
746 if (ast_test_flag(&flags, run_dead)) {
747 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
748 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
751 if (ast_answer(chan)) {
752 ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n");
753 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
757 if (!(u->gen_active)) {
758 if (ast_activate_generator(chan, &gen, u) < 0) {
759 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
760 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
765 } else if (input[0] == EIVR_CMD_IRPT) {
766 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
767 ast_chan_log(LOG_WARNING, chan, "Queue 'I'nterrupt called on unanswered channel\n");
768 send_eivr_event(eivr_events, 'Z', NULL, chan);
771 AST_LIST_LOCK(&u->playlist);
772 if (!u->abort_current_sound && !u->playing_silence) {
773 /* send interrupted file as T data */
774 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
775 send_eivr_event(eivr_events, 'T', entry->filename, chan);
779 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
780 send_eivr_event(eivr_events, 'D', entry->filename, chan);
783 if (!u->playing_silence) {
784 u->abort_current_sound = 1;
786 AST_LIST_UNLOCK(&u->playlist);
787 } else if (input[0] == EIVR_CMD_SQUE) {
788 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
789 ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n");
790 send_eivr_event(eivr_events, 'Z', NULL, chan);
793 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
794 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
795 send_eivr_event(eivr_events, 'Z', &input[2], chan);
797 AST_LIST_LOCK(&u->playlist);
798 if (!u->abort_current_sound && !u->playing_silence) {
799 /* send interrupted file as T data */
800 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
801 send_eivr_event(eivr_events, 'T', entry->filename, chan);
805 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
806 send_eivr_event(eivr_events, 'D', entry->filename, chan);
809 if (!u->playing_silence) {
810 u->abort_current_sound = 1;
812 entry = make_entry(&input[2]);
814 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
816 AST_LIST_UNLOCK(&u->playlist);
818 } else if (input[0] == EIVR_CMD_APND) {
819 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
820 ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
821 send_eivr_event(eivr_events, 'Z', NULL, chan);
824 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
825 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
826 send_eivr_event(eivr_events, 'Z', &input[2], chan);
828 entry = make_entry(&input[2]);
830 AST_LIST_LOCK(&u->playlist);
831 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
832 AST_LIST_UNLOCK(&u->playlist);
835 } else if (input[0] == EIVR_CMD_GET) {
837 ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
838 ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
839 send_eivr_event(eivr_events, 'G', response, chan);
840 } else if (input[0] == EIVR_CMD_SVAR) {
841 ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
842 ast_eivr_setvariable(chan, &input[2]);
843 } else if (input[0] == EIVR_CMD_LOG) {
844 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
845 } else if (input[0] == EIVR_CMD_XIT) {
846 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
847 ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
850 } else if (input[0] == EIVR_CMD_EXIT) {
851 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
852 send_eivr_event(eivr_events, 'E', NULL, chan);
855 } else if (input[0] == EIVR_CMD_HGUP) {
856 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
857 send_eivr_event(eivr_events, 'H', NULL, chan);
859 } else if (input[0] == EIVR_CMD_OPT) {
860 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
861 ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
862 send_eivr_event(eivr_events, 'Z', NULL, chan);
865 if (!strcasecmp(&input[2], "autoclear"))
866 u->option_autoclear = 1;
867 else if (!strcasecmp(&input[2], "noautoclear"))
868 u->option_autoclear = 0;
870 ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
872 } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
875 if (exception || feof(eivr_errors)) {
876 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
879 if (fgets(input, sizeof(input), eivr_errors)) {
880 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
882 } else if ((ready_fd < 0) && ms) {
883 if (errno == 0 || errno == EINTR)
886 ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
892 if (test_available_fd > -1) {
893 close(test_available_fd);
897 *eivr_events_fd = -1;
900 fclose(eivr_commands);
901 *eivr_commands_fd = -1;
905 *eivr_errors_fd = -1;
910 static int unload_module(void)
912 return ast_unregister_application(app);
915 static int load_module(void)
917 return ast_register_application_xml(app, app_exec);
920 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");