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