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