Add IPv6 support 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_LOG  'L' /* log message */
109 #define EIVR_CMD_OPT  'O' /* option */
110 #define EIVR_CMD_PARM 'P' /* return supplied params */
111 #define EIVR_CMD_SQUE 'S' /* (re)set prompt queue */
112 #define EIVR_CMD_ANS  'T' /* answer channel */
113 #define EIVR_CMD_SVAR 'V' /* set channel varable(s) */
114 #define EIVR_CMD_XIT  'X' /* exit **depricated** */
115
116 #define EXTERNALIVR_PORT 2949
117
118 enum options_flags {
119         noanswer = (1 << 0),
120         ignore_hangup = (1 << 1),
121         run_dead = (1 << 2),
122 };
123
124 AST_APP_OPTIONS(app_opts, {
125         AST_APP_OPTION('n', noanswer),
126         AST_APP_OPTION('i', ignore_hangup),
127         AST_APP_OPTION('d', run_dead),
128 });
129
130 struct playlist_entry {
131         AST_LIST_ENTRY(playlist_entry) list;
132         char filename[1];
133 };
134
135 struct ivr_localuser {
136         struct ast_channel *chan;
137         AST_LIST_HEAD(playlist, playlist_entry) playlist;
138         AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
139         int abort_current_sound;
140         int playing_silence;
141         int option_autoclear;
142         int gen_active;
143 };
144
145
146 struct gen_state {
147         struct ivr_localuser *u;
148         struct ast_filestream *stream;
149         struct playlist_entry *current;
150         int sample_queue;
151 };
152
153 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
154         int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, 
155         const struct ast_str *args, const struct ast_flags flags);
156
157 int eivr_connect_socket(struct ast_channel *chan, const char *host, int port);
158
159 static void send_eivr_event(FILE *handle, const char event, const char *data,
160         const struct ast_channel *chan)
161 {
162         struct ast_str *tmp = ast_str_create(12);
163
164         ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
165         if (data) {
166                 ast_str_append(&tmp, 0, ",%s", data);
167         }
168
169         fprintf(handle, "%s\n", ast_str_buffer(tmp));
170         ast_debug(1, "sent '%s'\n", ast_str_buffer(tmp));
171         ast_free(tmp);
172 }
173
174 static void *gen_alloc(struct ast_channel *chan, void *params)
175 {
176         struct ivr_localuser *u = params;
177         struct gen_state *state;
178
179         if (!(state = ast_calloc(1, sizeof(*state))))
180                 return NULL;
181
182         state->u = u;
183
184         return state;
185 }
186
187 static void gen_closestream(struct gen_state *state)
188 {
189         if (!state->stream)
190                 return;
191
192         ast_closestream(state->stream);
193         ast_channel_stream_set(state->u->chan, NULL);
194         state->stream = NULL;
195 }
196
197 static void gen_release(struct ast_channel *chan, void *data)
198 {
199         struct gen_state *state = data;
200
201         gen_closestream(state);
202         ast_free(data);
203 }
204
205 /* caller has the playlist locked */
206 static int gen_nextfile(struct gen_state *state)
207 {
208         struct ivr_localuser *u = state->u;
209         char *file_to_stream;
210
211         u->abort_current_sound = 0;
212         u->playing_silence = 0;
213         gen_closestream(state);
214
215         while (!state->stream) {
216                 state->current = AST_LIST_FIRST(&u->playlist);
217                 if (state->current) {
218                         file_to_stream = state->current->filename;
219                 } else {
220                         file_to_stream = "silence/10";
221                         u->playing_silence = 1;
222                 }
223
224                 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, ast_channel_language(u->chan), 1))) {
225                         ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
226                         AST_LIST_LOCK(&u->playlist);
227                         AST_LIST_REMOVE_HEAD(&u->playlist, list);
228                         AST_LIST_UNLOCK(&u->playlist);
229                         if (!u->playing_silence) {
230                                 continue;
231                         } else {
232                                 break;
233                         }
234                 }
235         }
236
237         return (!state->stream);
238 }
239
240 static struct ast_frame *gen_readframe(struct gen_state *state)
241 {
242         struct ast_frame *f = NULL;
243         struct ivr_localuser *u = state->u;
244
245         if (u->abort_current_sound ||
246                 (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
247                 gen_closestream(state);
248                 AST_LIST_LOCK(&u->playlist);
249                 gen_nextfile(state);
250                 AST_LIST_UNLOCK(&u->playlist);
251         }
252
253         if (!(state->stream && (f = ast_readframe(state->stream)))) {
254                 if (state->current) {
255                         /* remove finished file from playlist */
256                         AST_LIST_LOCK(&u->playlist);
257                         AST_LIST_REMOVE_HEAD(&u->playlist, list);
258                         AST_LIST_UNLOCK(&u->playlist);
259                         /* add finished file to finishlist */
260                         AST_LIST_LOCK(&u->finishlist);
261                         AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
262                         AST_LIST_UNLOCK(&u->finishlist);
263                         state->current = NULL;
264                 }
265                 if (!gen_nextfile(state))
266                         f = ast_readframe(state->stream);
267         }
268
269         return f;
270 }
271
272 static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
273 {
274         struct gen_state *state = data;
275         struct ast_frame *f = NULL;
276         int res = 0;
277
278         state->sample_queue += samples;
279
280         while (state->sample_queue > 0) {
281                 if (!(f = gen_readframe(state)))
282                         return -1;
283
284                 res = ast_write(chan, f);
285                 ast_frfree(f);
286                 if (res < 0) {
287                         ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
288                         return -1;
289                 }
290                 state->sample_queue -= f->samples;
291         }
292
293         return res;
294 }
295
296 static struct ast_generator gen =
297 {
298         .alloc = gen_alloc,
299         .release = gen_release,
300         .generate = gen_generate,
301 };
302
303 static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
304 {
305         /* original input data: "G,var1,var2," */
306         /* data passed as "data":  "var1,var2" */
307
308         char *inbuf, *variable;
309         const char *value;
310         int j;
311         struct ast_str *newstring = ast_str_alloca(outbuflen); 
312
313         outbuf[0] = '\0';
314
315         for (j = 1, inbuf = data; ; j++) {
316                 variable = strsep(&inbuf, ",");
317                 if (variable == NULL) {
318                         int outstrlen = strlen(outbuf);
319                         if (outstrlen && outbuf[outstrlen - 1] == ',') {
320                                 outbuf[outstrlen - 1] = 0;
321                         }
322                         break;
323                 }
324                 
325                 ast_channel_lock(chan);
326                 if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
327                         value = "";
328                 }
329
330                 ast_str_append(&newstring, 0, "%s=%s,", variable, value);
331                 ast_channel_unlock(chan);
332                 ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen);
333         }
334 }
335
336 static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
337 {
338         char *value;
339
340         char *inbuf = ast_strdupa(data), *variable;
341
342         for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) {
343                 ast_debug(1, "Setting up a variable: %s\n", variable);
344                 /* variable contains "varname=value" */
345                 value = strchr(variable, '=');
346                 if (!value) {
347                         value = "";
348                 } else {
349                         *value++ = '\0';
350                 }
351                 pbx_builtin_setvar_helper(chan, variable, value);
352         }
353 }
354
355 static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
356 {
357
358         char *data;
359         int dinterval = 0, duration = 0;
360         AST_DECLARE_APP_ARGS(args,
361                 AST_APP_ARG(digits);
362                 AST_APP_ARG(dinterval);
363                 AST_APP_ARG(duration);
364         );
365
366         data = ast_strdupa(vdata);
367         AST_STANDARD_APP_ARGS(args, data);
368
369         if (!ast_strlen_zero(args.dinterval)) {
370                 ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
371         }
372         if (!ast_strlen_zero(args.duration)) {
373                 ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
374         }
375         ast_verb(4, "Sending DTMF: %s %d %d\n", args.digits, dinterval <= 0 ? 250 : dinterval, duration);
376         ast_dtmf_stream(chan, NULL, args.digits, dinterval <= 0 ? 250 : dinterval, duration);
377 }
378
379 static struct playlist_entry *make_entry(const char *filename)
380 {
381         struct playlist_entry *entry;
382
383         if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
384                 return NULL;
385
386         strcpy(entry->filename, filename);
387
388         return entry;
389 }
390
391 static int app_exec(struct ast_channel *chan, const char *data)
392 {
393         struct ast_flags flags = { 0, };
394         char *opts[0];
395         struct playlist_entry *entry;
396         int child_stdin[2] = { -1, -1 };
397         int child_stdout[2] = { -1, -1 };
398         int child_stderr[2] = { -1, -1 };
399         int res = -1;
400         int pid;
401
402         struct ast_tcptls_session_instance *ser = NULL;
403
404         struct ivr_localuser foo = {
405                 .playlist = AST_LIST_HEAD_INIT_VALUE,
406                 .finishlist = AST_LIST_HEAD_INIT_VALUE,
407                 .gen_active = 0,
408                 .playing_silence = 1,
409         };
410         struct ivr_localuser *u = &foo;
411
412         char *buf;
413         int j;
414         char *s, **app_args, *e; 
415         struct ast_str *comma_delim_args = ast_str_alloca(100);
416
417         AST_DECLARE_APP_ARGS(eivr_args,
418                 AST_APP_ARG(application);
419                 AST_APP_ARG(options);
420         );
421         AST_DECLARE_APP_ARGS(application_args,
422                 AST_APP_ARG(cmd)[32];
423         );
424
425         u->abort_current_sound = 0;
426         u->chan = chan;
427
428         if (ast_strlen_zero(data)) {
429                 ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n");
430                 goto exit;
431         }
432
433         buf = ast_strdupa(data);
434         AST_STANDARD_APP_ARGS(eivr_args, buf);
435
436         ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application);
437         ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options);
438
439         /* Parse out any application arguments */
440         if ((s = strchr(eivr_args.application, '('))) {
441                 s[0] = ',';
442                 if ((e = strrchr(s, ')'))) {
443                         *e = '\0';
444                 } else {
445                         ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n");
446                         goto exit;
447                 }
448         }
449
450         AST_STANDARD_APP_ARGS(application_args, eivr_args.application);
451         app_args = application_args.argv;
452
453         /* Put the application + the arguments in a , delimited list */
454         ast_str_reset(comma_delim_args);
455         for (j = 0; application_args.cmd[j] != NULL; j++) {
456                 ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
457         }
458
459         /* Get rid of any extraneous arguments */
460         if (eivr_args.options && (s = strchr(eivr_args.options, ','))) {
461                 *s = '\0';
462         }
463
464         /* Parse the ExternalIVR() arguments */
465         ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options);
466         ast_app_parse_options(app_opts, &flags, opts, eivr_args.options);
467         if (ast_test_flag(&flags, noanswer)) {
468                 ast_verb(4, "noanswer is set\n");
469         }
470         if (ast_test_flag(&flags, ignore_hangup)) {
471                 ast_verb(4, "ignore_hangup is set\n");
472         }
473         if (ast_test_flag(&flags, run_dead)) {
474                 ast_verb(4, "run_dead is set\n");
475         }
476         
477         if (!(ast_test_flag(&flags, noanswer))) {
478                 ast_verb(3, "Answering channel and starting generator\n");
479                 if (ast_channel_state(chan) != AST_STATE_UP) {
480                         if (ast_test_flag(&flags, run_dead)) {
481                                 ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
482                                 goto exit;
483                         }
484                         ast_answer(chan);
485                 }
486                 if (ast_activate_generator(chan, &gen, u) < 0) {
487                         ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n");
488                         goto exit;
489                 } else {
490                         u->gen_active = 1;
491                 }
492         }
493
494         if (!strncmp(app_args[0], "ivr://", sizeof("ivr://") - 1)) {
495                 struct ast_tcptls_session_args ivr_desc = {
496                         .accept_fd = -1,
497                         .name = "IVR",
498                 };
499                 struct ast_sockaddr *addrs;
500                 int num_addrs = 0, i = 0;
501                 char *host = app_args[0] + sizeof("ivr://") - 1;
502
503                 /* Communicate through socket to server */
504                 ast_debug(1, "Parsing hostname/port for socket connect from \"%s\"\n", host);
505
506                 if (!(num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC))) {
507                         ast_chan_log(LOG_ERROR, chan, "Unable to locate host '%s'\n", host);
508                         goto exit;
509                 }
510
511                 for (i = 0; i < num_addrs; i++) {
512                         if (!ast_sockaddr_port(&addrs[i])) {
513                                 /* Default port if not specified */
514                                 ast_sockaddr_set_port(&addrs[i], EXTERNALIVR_PORT);
515                         }
516                         ast_sockaddr_copy(&ivr_desc.remote_address, &addrs[i]);
517                         if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) {
518                                 continue;
519                         }
520                         break;
521                 }
522
523                 if (i == num_addrs) {
524                         ast_chan_log(LOG_ERROR, chan, "Could not connect to any host.  ExternalIVR failed.\n");
525                         goto exit;
526                 }
527
528                 res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags);
529
530         } else {
531                 if (pipe(child_stdin)) {
532                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno));
533                         goto exit;
534                 }
535                 if (pipe(child_stdout)) {
536                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno));
537                         goto exit;
538                 }
539                 if (pipe(child_stderr)) {
540                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
541                         goto exit;
542                 }
543         
544                 pid = ast_safe_fork(0);
545                 if (pid < 0) {
546                         ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno));
547                         goto exit;
548                 }
549         
550                 if (!pid) {
551                         /* child process */
552                         if (ast_opt_high_priority)
553                                 ast_set_priority(0);
554         
555                         dup2(child_stdin[0], STDIN_FILENO);
556                         dup2(child_stdout[1], STDOUT_FILENO);
557                         dup2(child_stderr[1], STDERR_FILENO);
558                         ast_close_fds_above_n(STDERR_FILENO);
559                         execv(app_args[0], app_args);
560                         fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
561                         _exit(1);
562                 } else {
563                         /* parent process */
564                         close(child_stdin[0]);
565                         child_stdin[0] = -1;
566                         close(child_stdout[1]);
567                         child_stdout[1] = -1;
568                         close(child_stderr[1]);
569                         child_stderr[1] = -1;
570                         res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags);
571                 }
572         }
573
574         exit:
575         if (u->gen_active) {
576                 ast_deactivate_generator(chan);
577         }
578         if (child_stdin[0] > -1) {
579                 close(child_stdin[0]);
580         }
581         if (child_stdin[1] > -1) {
582                 close(child_stdin[1]);
583         }
584         if (child_stdout[0] > -1) {
585                 close(child_stdout[0]);
586         }
587         if (child_stdout[1] > -1) {
588                 close(child_stdout[1]);
589         }
590         if (child_stderr[0] > -1) {
591                 close(child_stderr[0]);
592         }
593         if (child_stderr[1] > -1) {
594                 close(child_stderr[1]);
595         }
596         if (ser) {
597                 ao2_ref(ser, -1);
598         }
599         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
600                 ast_free(entry);
601         }
602         return res;
603 }
604
605 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
606                                 int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, 
607                                 const struct ast_str *args, const struct ast_flags flags)
608 {
609         struct playlist_entry *entry;
610         struct ast_frame *f;
611         int ms;
612         int exception;
613         int ready_fd;
614         int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 };
615         struct ast_channel *rchan;
616         int res = -1;
617         int test_available_fd = -1;
618         int hangup_info_sent = 0;
619   
620         FILE *eivr_commands = NULL;
621         FILE *eivr_errors = NULL;
622         FILE *eivr_events = NULL;
623
624         if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) {
625                 ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n");
626                 goto exit;
627         }
628         if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) {
629                 ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n");
630                 goto exit;
631         }
632         if (eivr_errors_fd) {  /* if opening a socket connection, error stream will not be used */
633                 if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) {
634                         ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n");
635                         goto exit;
636                 }
637         }
638
639         test_available_fd = open("/dev/null", O_RDONLY);
640  
641         setvbuf(eivr_events, NULL, _IONBF, 0);
642         setvbuf(eivr_commands, NULL, _IONBF, 0);
643         if (eivr_errors) {
644                 setvbuf(eivr_errors, NULL, _IONBF, 0);
645         }
646
647         while (1) {
648                 if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) {
649                         ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
650                         break;
651                 }
652                 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
653                         if (ast_test_flag(&flags, ignore_hangup)) {
654                                 ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
655                                 send_eivr_event(eivr_events, 'I', "HANGUP", chan);
656                                 hangup_info_sent = 1;
657                         } else {
658                                 ast_verb(3, "Got check_hangup\n");
659                                 send_eivr_event(eivr_events, 'H', NULL, chan);
660                                 break;
661                         }
662                 }
663  
664                 ready_fd = 0;
665                 ms = 100;
666                 errno = 0;
667                 exception = 0;
668  
669                 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms);
670  
671                 if (ast_channel_state(chan) == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
672                         AST_LIST_LOCK(&u->finishlist);
673                         while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
674                                 send_eivr_event(eivr_events, 'F', entry->filename, chan);
675                                 ast_free(entry);
676                         }
677                         AST_LIST_UNLOCK(&u->finishlist);
678                 }
679  
680                 if (ast_channel_state(chan) == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
681                         /* the channel has something */
682                         f = ast_read(chan);
683                         if (!f) {
684                                 ast_verb(3, "Returned no frame\n");
685                                 send_eivr_event(eivr_events, 'H', NULL, chan);
686                                 break;
687                         }
688                         if (f->frametype == AST_FRAME_DTMF) {
689                                 send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
690                                 if (u->option_autoclear) {
691                                         AST_LIST_LOCK(&u->playlist);
692                                         if (!u->abort_current_sound && !u->playing_silence) {
693                                                 /* send interrupted file as T data */
694                                                 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
695                                                         send_eivr_event(eivr_events, 'T', entry->filename, chan);
696                                                         ast_free(entry);
697                                                 }
698                                         }
699                                         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
700                                                 send_eivr_event(eivr_events, 'D', entry->filename, chan);
701                                                 ast_free(entry);
702                                         }
703                                         if (!u->playing_silence)
704                                                 u->abort_current_sound = 1;
705                                         AST_LIST_UNLOCK(&u->playlist);
706                                 }
707                         } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
708                                 ast_verb(3, "Got AST_CONTROL_HANGUP\n");
709                                 send_eivr_event(eivr_events, 'H', NULL, chan);
710                                 if (f->data.uint32) {
711                                         ast_channel_hangupcause_set(chan, f->data.uint32);
712                                 }
713                                 ast_frfree(f);
714                                 break;
715                         }
716                         ast_frfree(f);
717                 } else if (ready_fd == *eivr_commands_fd) {
718                         char input[1024];
719  
720                         if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
721                                 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
722                                 break;
723                         }
724   
725                         if (!fgets(input, sizeof(input), eivr_commands)) {
726                                 continue;
727                         }
728
729                         ast_strip(input);
730                         ast_verb(4, "got command '%s'\n", input);
731
732                         if (strlen(input) < 3) {
733                                 continue;
734                         }
735
736                         if (input[0] == EIVR_CMD_PARM) {
737                                 struct ast_str *tmp = (struct ast_str *) args;
738                                 send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
739                         } else if (input[0] == EIVR_CMD_DTMF) {
740                                 ast_verb(4, "Sending DTMF: %s\n", &input[2]);
741                                 ast_eivr_senddtmf(chan, &input[2]);
742                         } else if (input[0] == EIVR_CMD_ANS) {
743                                 ast_verb(3, "Answering channel if needed and starting generator\n");
744                                 if (ast_channel_state(chan) != AST_STATE_UP) {
745                                         if (ast_test_flag(&flags, run_dead)) {
746                                                 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
747                                                 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
748                                                 continue;
749                                         }
750                                         if (ast_answer(chan)) {
751                                                 ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n");
752                                                 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
753                                                 continue;
754                                         }
755                                 }
756                                 if (!(u->gen_active)) {
757                                         if (ast_activate_generator(chan, &gen, u) < 0) {
758                                                 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
759                                                 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
760                                         } else {
761                                                 u->gen_active = 1;
762                                         }
763                                 }
764                         } else if (input[0] == EIVR_CMD_SQUE) {
765                                 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
766                                         ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n");
767                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
768                                         continue;
769                                 }
770                                 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
771                                         ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
772                                         send_eivr_event(eivr_events, 'Z', &input[2], chan);
773                                 } else {
774                                         AST_LIST_LOCK(&u->playlist);
775                                         if (!u->abort_current_sound && !u->playing_silence) {
776                                                 /* send interrupted file as T data */
777                                                 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
778                                                         send_eivr_event(eivr_events, 'T', entry->filename, chan);
779                                                         ast_free(entry);
780                                                 }
781                                         }
782                                         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
783                                                 send_eivr_event(eivr_events, 'D', entry->filename, chan);
784                                                 ast_free(entry);
785                                         }
786                                         if (!u->playing_silence) {
787                                                 u->abort_current_sound = 1;
788                                         }
789                                         entry = make_entry(&input[2]);
790                                         if (entry) {
791                                                 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
792                                         }
793                                         AST_LIST_UNLOCK(&u->playlist);
794                                 }
795                         } else if (input[0] == EIVR_CMD_APND) {
796                                 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
797                                         ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
798                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
799                                         continue;
800                                 }
801                                 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
802                                         ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
803                                         send_eivr_event(eivr_events, 'Z', &input[2], chan);
804                                 } else {
805                                         entry = make_entry(&input[2]);
806                                         if (entry) {
807                                                 AST_LIST_LOCK(&u->playlist);
808                                                 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
809                                                 AST_LIST_UNLOCK(&u->playlist);
810                                         }
811                                 }
812                         } else if (input[0] == EIVR_CMD_GET) {
813                                 char response[2048];
814                                 ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
815                                 ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
816                                 send_eivr_event(eivr_events, 'G', response, chan);
817                         } else if (input[0] == EIVR_CMD_SVAR) {
818                                 ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
819                                 ast_eivr_setvariable(chan, &input[2]);
820                         } else if (input[0] == EIVR_CMD_LOG) {
821                                 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
822                         } else if (input[0] == EIVR_CMD_XIT) {
823                                 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
824                                 ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
825                                 res = 0;
826                                 break;
827                         } else if (input[0] == EIVR_CMD_EXIT) {
828                                 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
829                                 send_eivr_event(eivr_events, 'E', NULL, chan);
830                                 res = 0;
831                                 break;
832                         } else if (input[0] == EIVR_CMD_HGUP) {
833                                 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
834                                 send_eivr_event(eivr_events, 'H', NULL, chan);
835                                 break;
836                         } else if (input[0] == EIVR_CMD_OPT) {
837                                 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
838                                         ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
839                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
840                                         continue;
841                                 }
842                                 if (!strcasecmp(&input[2], "autoclear"))
843                                         u->option_autoclear = 1;
844                                 else if (!strcasecmp(&input[2], "noautoclear"))
845                                         u->option_autoclear = 0;
846                                 else
847                                         ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
848                         }
849                 } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
850                         char input[1024];
851   
852                         if (exception || feof(eivr_errors)) {
853                                 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
854                                 break;
855                         }
856                         if (fgets(input, sizeof(input), eivr_errors)) {
857                                 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
858                         }
859                 } else if ((ready_fd < 0) && ms) { 
860                         if (errno == 0 || errno == EINTR)
861                                 continue;
862  
863                         ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
864                         break;
865                 }
866         }
867  
868         exit:
869         if (test_available_fd > -1) {
870                 close(test_available_fd);
871         }
872         if (eivr_events) {
873                 fclose(eivr_events);
874                 *eivr_events_fd = -1;
875         }
876         if (eivr_commands) {
877                 fclose(eivr_commands);
878                 *eivr_commands_fd = -1;
879         }
880         if (eivr_errors) {
881                 fclose(eivr_errors);
882                 *eivr_errors_fd = -1;
883         }
884         return res;
885 }
886
887 static int unload_module(void)
888 {
889         return ast_unregister_application(app);
890 }
891
892 static int load_module(void)
893 {
894         return ast_register_application_xml(app, app_exec);
895 }
896
897 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");