Add interrupt ('I') command to ExternalIVR.
[asterisk/asterisk.git] / apps / app_externalivr.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  *
6  * Kevin P. Fleming <kpfleming@digium.com>
7  *
8  * Portions taken from the file-based music-on-hold work
9  * created by Anthony Minessale II in res_musiconhold.c
10  *
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.
16  *
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.
20  */
21
22 /*! \file
23  *
24  * \brief External IVR application interface
25  *
26  * \author Kevin P. Fleming <kpfleming@digium.com>
27  *
28  * \note Portions taken from the file-based music-on-hold work
29  * created by Anthony Minessale II in res_musiconhold.c
30  *
31  * \ingroup applications
32  */
33
34 /*** MODULEINFO
35         <support_level>extended</support_level>
36  ***/
37
38 #include "asterisk.h"
39
40 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
41
42 #include <signal.h>
43
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"
54
55 /*** DOCUMENTATION
56         <application name="ExternalIVR" language="en_US">
57                 <synopsis>
58                         Interfaces with an external IVR application.
59                 </synopsis>
60                 <syntax>
61                         <parameter name="command|ivr://host" required="true" hasparams="true">
62                                 <argument name="arg1" />
63                                 <argument name="arg2" multiple="yes" />
64                         </parameter>
65                         <parameter name="options">
66                                 <optionlist>
67                                         <option name="n">
68                                                 <para>Tells ExternalIVR() not to answer the channel.</para>
69                                         </option>
70                                         <option name="i">
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>
75                                         </option>
76                                         <option name="d">
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>
80                                         </option>
81                                 </optionlist>
82                         </parameter>
83                 </syntax>
84                 <description>
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>
93                 </description>
94         </application>
95  ***/
96
97 static const char app[] = "ExternalIVR";
98
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__)
101
102 /* Commands */
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** */
116
117 #define EXTERNALIVR_PORT 2949
118
119 enum options_flags {
120         noanswer = (1 << 0),
121         ignore_hangup = (1 << 1),
122         run_dead = (1 << 2),
123 };
124
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),
129 });
130
131 struct playlist_entry {
132         AST_LIST_ENTRY(playlist_entry) list;
133         char filename[1];
134 };
135
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;
141         int playing_silence;
142         int option_autoclear;
143         int gen_active;
144 };
145
146
147 struct gen_state {
148         struct ivr_localuser *u;
149         struct ast_filestream *stream;
150         struct playlist_entry *current;
151         int sample_queue;
152 };
153
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);
157
158 int eivr_connect_socket(struct ast_channel *chan, const char *host, int port);
159
160 static void send_eivr_event(FILE *handle, const char event, const char *data,
161         const struct ast_channel *chan)
162 {
163         struct ast_str *tmp = ast_str_create(12);
164
165         ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
166         if (data) {
167                 ast_str_append(&tmp, 0, ",%s", data);
168         }
169
170         fprintf(handle, "%s\n", ast_str_buffer(tmp));
171         ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp));
172         ast_free(tmp);
173 }
174
175 static void *gen_alloc(struct ast_channel *chan, void *params)
176 {
177         struct ivr_localuser *u = params;
178         struct gen_state *state;
179
180         if (!(state = ast_calloc(1, sizeof(*state))))
181                 return NULL;
182
183         state->u = u;
184
185         return state;
186 }
187
188 static void gen_closestream(struct gen_state *state)
189 {
190         if (!state->stream)
191                 return;
192
193         ast_closestream(state->stream);
194         ast_channel_stream_set(state->u->chan, NULL);
195         state->stream = NULL;
196 }
197
198 static void gen_release(struct ast_channel *chan, void *data)
199 {
200         struct gen_state *state = data;
201
202         gen_closestream(state);
203         ast_free(data);
204 }
205
206 /* caller has the playlist locked */
207 static int gen_nextfile(struct gen_state *state)
208 {
209         struct ivr_localuser *u = state->u;
210         char *file_to_stream;
211
212         u->abort_current_sound = 0;
213         u->playing_silence = 0;
214         gen_closestream(state);
215
216         while (!state->stream) {
217                 state->current = AST_LIST_FIRST(&u->playlist);
218                 if (state->current) {
219                         file_to_stream = state->current->filename;
220                 } else {
221                         file_to_stream = "silence/10";
222                         u->playing_silence = 1;
223                 }
224
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) {
231                                 continue;
232                         } else {
233                                 break;
234                         }
235                 }
236         }
237
238         return (!state->stream);
239 }
240
241 static struct ast_frame *gen_readframe(struct gen_state *state)
242 {
243         struct ast_frame *f = NULL;
244         struct ivr_localuser *u = state->u;
245
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);
250                 gen_nextfile(state);
251                 AST_LIST_UNLOCK(&u->playlist);
252         }
253
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;
265                 }
266                 if (!gen_nextfile(state))
267                         f = ast_readframe(state->stream);
268         }
269
270         return f;
271 }
272
273 static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
274 {
275         struct gen_state *state = data;
276         struct ast_frame *f = NULL;
277         int res = 0;
278
279         state->sample_queue += samples;
280
281         while (state->sample_queue > 0) {
282                 if (!(f = gen_readframe(state)))
283                         return -1;
284
285                 res = ast_write(chan, f);
286                 ast_frfree(f);
287                 if (res < 0) {
288                         ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
289                         return -1;
290                 }
291                 state->sample_queue -= f->samples;
292         }
293
294         return res;
295 }
296
297 static struct ast_generator gen =
298 {
299         .alloc = gen_alloc,
300         .release = gen_release,
301         .generate = gen_generate,
302 };
303
304 static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
305 {
306         /* original input data: "G,var1,var2," */
307         /* data passed as "data":  "var1,var2" */
308
309         char *inbuf, *variable;
310         const char *value;
311         int j;
312         struct ast_str *newstring = ast_str_alloca(outbuflen); 
313
314         outbuf[0] = '\0';
315
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;
322                         }
323                         break;
324                 }
325                 
326                 ast_channel_lock(chan);
327                 if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
328                         value = "";
329                 }
330
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);
334         }
335 }
336
337 static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
338 {
339         char *value;
340
341         char *inbuf = ast_strdupa(data), *variable;
342
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, '=');
347                 if (!value) {
348                         value = "";
349                 } else {
350                         *value++ = '\0';
351                 }
352                 pbx_builtin_setvar_helper(chan, variable, value);
353         }
354 }
355
356 static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
357 {
358
359         char *data;
360         int dinterval = 0, duration = 0;
361         AST_DECLARE_APP_ARGS(args,
362                 AST_APP_ARG(digits);
363                 AST_APP_ARG(dinterval);
364                 AST_APP_ARG(duration);
365         );
366
367         data = ast_strdupa(vdata);
368         AST_STANDARD_APP_ARGS(args, data);
369
370         if (!ast_strlen_zero(args.dinterval)) {
371                 ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
372         }
373         if (!ast_strlen_zero(args.duration)) {
374                 ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
375         }
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);
378 }
379
380 static struct playlist_entry *make_entry(const char *filename)
381 {
382         struct playlist_entry *entry;
383
384         if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
385                 return NULL;
386
387         strcpy(entry->filename, filename);
388
389         return entry;
390 }
391
392 static int app_exec(struct ast_channel *chan, const char *data)
393 {
394         struct ast_flags flags = { 0, };
395         char *opts[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 };
400         int res = -1;
401         int pid;
402
403         struct ast_tcptls_session_instance *ser = NULL;
404
405         struct ivr_localuser foo = {
406                 .playlist = AST_LIST_HEAD_INIT_VALUE,
407                 .finishlist = AST_LIST_HEAD_INIT_VALUE,
408                 .gen_active = 0,
409                 .playing_silence = 1,
410         };
411         struct ivr_localuser *u = &foo;
412
413         char *buf;
414         int j;
415         char *s, **app_args, *e; 
416         struct ast_str *comma_delim_args = ast_str_alloca(100);
417
418         AST_DECLARE_APP_ARGS(eivr_args,
419                 AST_APP_ARG(application);
420                 AST_APP_ARG(options);
421         );
422         AST_DECLARE_APP_ARGS(application_args,
423                 AST_APP_ARG(cmd)[32];
424         );
425
426         u->abort_current_sound = 0;
427         u->chan = chan;
428
429         if (ast_strlen_zero(data)) {
430                 ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n");
431                 goto exit;
432         }
433
434         buf = ast_strdupa(data);
435         AST_STANDARD_APP_ARGS(eivr_args, buf);
436
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);
439
440         /* Parse out any application arguments */
441         if ((s = strchr(eivr_args.application, '('))) {
442                 s[0] = ',';
443                 if ((e = strrchr(s, ')'))) {
444                         *e = '\0';
445                 } else {
446                         ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n");
447                         goto exit;
448                 }
449         }
450
451         AST_STANDARD_APP_ARGS(application_args, eivr_args.application);
452         app_args = application_args.argv;
453
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]);
458         }
459
460         /* Get rid of any extraneous arguments */
461         if (eivr_args.options && (s = strchr(eivr_args.options, ','))) {
462                 *s = '\0';
463         }
464
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");
470         }
471         if (ast_test_flag(&flags, ignore_hangup)) {
472                 ast_verb(4, "ignore_hangup is set\n");
473         }
474         if (ast_test_flag(&flags, run_dead)) {
475                 ast_verb(4, "run_dead is set\n");
476         }
477         
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");
483                                 goto exit;
484                         }
485                         ast_answer(chan);
486                 }
487                 if (ast_activate_generator(chan, &gen, u) < 0) {
488                         ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n");
489                         goto exit;
490                 } else {
491                         u->gen_active = 1;
492                 }
493         }
494
495         if (!strncmp(app_args[0], "ivr://", sizeof("ivr://") - 1)) {
496                 struct ast_tcptls_session_args ivr_desc = {
497                         .accept_fd = -1,
498                         .name = "IVR",
499                 };
500                 struct ast_sockaddr *addrs;
501                 int num_addrs = 0, i = 0;
502                 char *host = app_args[0] + sizeof("ivr://") - 1;
503
504                 /* Communicate through socket to server */
505                 ast_debug(1, "Parsing hostname/port for socket connect from \"%s\"\n", host);
506
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);
509                         goto exit;
510                 }
511
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);
516                         }
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))) {
519                                 continue;
520                         }
521                         break;
522                 }
523
524                 if (i == num_addrs) {
525                         ast_chan_log(LOG_ERROR, chan, "Could not connect to any host.  ExternalIVR failed.\n");
526                         goto exit;
527                 }
528
529                 res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags);
530
531         } else {
532                 if (pipe(child_stdin)) {
533                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno));
534                         goto exit;
535                 }
536                 if (pipe(child_stdout)) {
537                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno));
538                         goto exit;
539                 }
540                 if (pipe(child_stderr)) {
541                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
542                         goto exit;
543                 }
544         
545                 pid = ast_safe_fork(0);
546                 if (pid < 0) {
547                         ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno));
548                         goto exit;
549                 }
550         
551                 if (!pid) {
552                         /* child process */
553                         if (ast_opt_high_priority)
554                                 ast_set_priority(0);
555         
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));
562                         _exit(1);
563                 } else {
564                         /* parent process */
565                         close(child_stdin[0]);
566                         child_stdin[0] = -1;
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);
572                 }
573         }
574
575         exit:
576         if (u->gen_active) {
577                 ast_deactivate_generator(chan);
578         }
579         if (child_stdin[0] > -1) {
580                 close(child_stdin[0]);
581         }
582         if (child_stdin[1] > -1) {
583                 close(child_stdin[1]);
584         }
585         if (child_stdout[0] > -1) {
586                 close(child_stdout[0]);
587         }
588         if (child_stdout[1] > -1) {
589                 close(child_stdout[1]);
590         }
591         if (child_stderr[0] > -1) {
592                 close(child_stderr[0]);
593         }
594         if (child_stderr[1] > -1) {
595                 close(child_stderr[1]);
596         }
597         if (ser) {
598                 ao2_ref(ser, -1);
599         }
600         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
601                 ast_free(entry);
602         }
603         return res;
604 }
605
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)
609 {
610         struct playlist_entry *entry;
611         struct ast_frame *f;
612         int ms;
613         int exception;
614         int ready_fd;
615         int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 };
616         struct ast_channel *rchan;
617         int res = -1;
618         int test_available_fd = -1;
619         int hangup_info_sent = 0;
620   
621         FILE *eivr_commands = NULL;
622         FILE *eivr_errors = NULL;
623         FILE *eivr_events = NULL;
624
625         if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) {
626                 ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n");
627                 goto exit;
628         }
629         if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) {
630                 ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n");
631                 goto exit;
632         }
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");
636                         goto exit;
637                 }
638         }
639
640         test_available_fd = open("/dev/null", O_RDONLY);
641  
642         setvbuf(eivr_events, NULL, _IONBF, 0);
643         setvbuf(eivr_commands, NULL, _IONBF, 0);
644         if (eivr_errors) {
645                 setvbuf(eivr_errors, NULL, _IONBF, 0);
646         }
647
648         while (1) {
649                 if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) {
650                         ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
651                         break;
652                 }
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;
658                         } else {
659                                 ast_verb(3, "Got check_hangup\n");
660                                 send_eivr_event(eivr_events, 'H', NULL, chan);
661                                 break;
662                         }
663                 }
664  
665                 ready_fd = 0;
666                 ms = 100;
667                 errno = 0;
668                 exception = 0;
669  
670                 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms);
671  
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);
676                                 ast_free(entry);
677                         }
678                         AST_LIST_UNLOCK(&u->finishlist);
679                 }
680  
681                 if (ast_channel_state(chan) == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
682                         /* the channel has something */
683                         f = ast_read(chan);
684                         if (!f) {
685                                 ast_verb(3, "Returned no frame\n");
686                                 send_eivr_event(eivr_events, 'H', NULL, chan);
687                                 break;
688                         }
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);
697                                                         ast_free(entry);
698                                                 }
699                                         }
700                                         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
701                                                 send_eivr_event(eivr_events, 'D', entry->filename, chan);
702                                                 ast_free(entry);
703                                         }
704                                         if (!u->playing_silence)
705                                                 u->abort_current_sound = 1;
706                                         AST_LIST_UNLOCK(&u->playlist);
707                                 }
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);
713                                 }
714                                 ast_frfree(f);
715                                 break;
716                         }
717                         ast_frfree(f);
718                 } else if (ready_fd == *eivr_commands_fd) {
719                         char input[1024];
720  
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");
723                                 break;
724                         }
725   
726                         if (!fgets(input, sizeof(input), eivr_commands)) {
727                                 continue;
728                         }
729
730                         ast_strip(input);
731                         ast_verb(4, "got command '%s'\n", input);
732
733                         if (strlen(input) < 3) {
734                                 continue;
735                         }
736
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);
749                                                 continue;
750                                         }
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);
754                                                 continue;
755                                         }
756                                 }
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);
761                                         } else {
762                                                 u->gen_active = 1;
763                                         }
764                                 }
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);
769                                         continue;
770                                 }
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);
776                                                 ast_free(entry);
777                                         }
778                                 }
779                                 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
780                                         send_eivr_event(eivr_events, 'D', entry->filename, chan);
781                                         ast_free(entry);
782                                 }
783                                 if (!u->playing_silence) {
784                                         u->abort_current_sound = 1;
785                                 }
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);
791                                         continue;
792                                 }
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);
796                                 } else {
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);
802                                                         ast_free(entry);
803                                                 }
804                                         }
805                                         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
806                                                 send_eivr_event(eivr_events, 'D', entry->filename, chan);
807                                                 ast_free(entry);
808                                         }
809                                         if (!u->playing_silence) {
810                                                 u->abort_current_sound = 1;
811                                         }
812                                         entry = make_entry(&input[2]);
813                                         if (entry) {
814                                                 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
815                                         }
816                                         AST_LIST_UNLOCK(&u->playlist);
817                                 }
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);
822                                         continue;
823                                 }
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);
827                                 } else {
828                                         entry = make_entry(&input[2]);
829                                         if (entry) {
830                                                 AST_LIST_LOCK(&u->playlist);
831                                                 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
832                                                 AST_LIST_UNLOCK(&u->playlist);
833                                         }
834                                 }
835                         } else if (input[0] == EIVR_CMD_GET) {
836                                 char response[2048];
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");
848                                 res = 0;
849                                 break;
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);
853                                 res = 0;
854                                 break;
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);
858                                 break;
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);
863                                         continue;
864                                 }
865                                 if (!strcasecmp(&input[2], "autoclear"))
866                                         u->option_autoclear = 1;
867                                 else if (!strcasecmp(&input[2], "noautoclear"))
868                                         u->option_autoclear = 0;
869                                 else
870                                         ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
871                         }
872                 } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
873                         char input[1024];
874   
875                         if (exception || feof(eivr_errors)) {
876                                 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
877                                 break;
878                         }
879                         if (fgets(input, sizeof(input), eivr_errors)) {
880                                 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
881                         }
882                 } else if ((ready_fd < 0) && ms) { 
883                         if (errno == 0 || errno == EINTR)
884                                 continue;
885  
886                         ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
887                         break;
888                 }
889         }
890  
891         exit:
892         if (test_available_fd > -1) {
893                 close(test_available_fd);
894         }
895         if (eivr_events) {
896                 fclose(eivr_events);
897                 *eivr_events_fd = -1;
898         }
899         if (eivr_commands) {
900                 fclose(eivr_commands);
901                 *eivr_commands_fd = -1;
902         }
903         if (eivr_errors) {
904                 fclose(eivr_errors);
905                 *eivr_errors_fd = -1;
906         }
907         return res;
908 }
909
910 static int unload_module(void)
911 {
912         return ast_unregister_application(app);
913 }
914
915 static int load_module(void)
916 {
917         return ast_register_application_xml(app, app_exec);
918 }
919
920 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");