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