git migration: Refactor the ASTERISK_FILE_VERSION macro
[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_REGISTER_FILE()
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                 ast_free(addrs);
523
524                 if (i == num_addrs) {
525                         ast_chan_log(LOG_ERROR, chan, "Could not connect to any host.  ExternalIVR failed.\n");
526                         goto exit;
527                 }
528
529                 res = eivr_comm(chan, u, &ser->fd, &ser->fd, NULL, comma_delim_args, flags);
530
531         } else {
532                 if (pipe(child_stdin)) {
533                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno));
534                         goto exit;
535                 }
536                 if (pipe(child_stdout)) {
537                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno));
538                         goto exit;
539                 }
540                 if (pipe(child_stderr)) {
541                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
542                         goto exit;
543                 }
544         
545                 pid = ast_safe_fork(0);
546                 if (pid < 0) {
547                         ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno));
548                         goto exit;
549                 }
550         
551                 if (!pid) {
552                         /* child process */
553                         if (ast_opt_high_priority)
554                                 ast_set_priority(0);
555         
556                         dup2(child_stdin[0], STDIN_FILENO);
557                         dup2(child_stdout[1], STDOUT_FILENO);
558                         dup2(child_stderr[1], STDERR_FILENO);
559                         ast_close_fds_above_n(STDERR_FILENO);
560                         execv(app_args[0], app_args);
561                         fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
562                         _exit(1);
563                 } else {
564                         /* parent process */
565                         close(child_stdin[0]);
566                         child_stdin[0] = -1;
567                         close(child_stdout[1]);
568                         child_stdout[1] = -1;
569                         close(child_stderr[1]);
570                         child_stderr[1] = -1;
571                         res = eivr_comm(chan, u, &child_stdin[1], &child_stdout[0], &child_stderr[0], comma_delim_args, flags);
572                 }
573         }
574
575         exit:
576         if (u->gen_active) {
577                 ast_deactivate_generator(chan);
578         }
579         if (child_stdin[0] > -1) {
580                 close(child_stdin[0]);
581         }
582         if (child_stdin[1] > -1) {
583                 close(child_stdin[1]);
584         }
585         if (child_stdout[0] > -1) {
586                 close(child_stdout[0]);
587         }
588         if (child_stdout[1] > -1) {
589                 close(child_stdout[1]);
590         }
591         if (child_stderr[0] > -1) {
592                 close(child_stderr[0]);
593         }
594         if (child_stderr[1] > -1) {
595                 close(child_stderr[1]);
596         }
597         if (ser) {
598                 ao2_ref(ser, -1);
599         }
600         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
601                 ast_free(entry);
602         }
603         return res;
604 }
605
606 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
607                                 int *eivr_events_fd, int *eivr_commands_fd, int *eivr_errors_fd, 
608                                 const struct ast_str *args, const struct ast_flags flags)
609 {
610         struct playlist_entry *entry;
611         struct ast_frame *f;
612         int ms;
613         int exception;
614         int ready_fd;
615         int waitfds[2] = { *eivr_commands_fd, (eivr_errors_fd) ? *eivr_errors_fd : -1 };
616         struct ast_channel *rchan;
617         int res = -1;
618         int test_available_fd = -1;
619         int hangup_info_sent = 0;
620   
621         FILE *eivr_commands = NULL;
622         FILE *eivr_errors = NULL;
623         FILE *eivr_events = NULL;
624
625         if (!(eivr_events = fdopen(*eivr_events_fd, "w"))) {
626                 ast_chan_log(LOG_ERROR, chan, "Could not open stream to send events\n");
627                 goto exit;
628         }
629         if (!(eivr_commands = fdopen(*eivr_commands_fd, "r"))) {
630                 ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive commands\n");
631                 goto exit;
632         }
633         if (eivr_errors_fd) {  /* if opening a socket connection, error stream will not be used */
634                 if (!(eivr_errors = fdopen(*eivr_errors_fd, "r"))) {
635                         ast_chan_log(LOG_ERROR, chan, "Could not open stream to receive errors\n");
636                         goto exit;
637                 }
638         }
639
640         test_available_fd = open("/dev/null", O_RDONLY);
641  
642         setvbuf(eivr_events, NULL, _IONBF, 0);
643         setvbuf(eivr_commands, NULL, _IONBF, 0);
644         if (eivr_errors) {
645                 setvbuf(eivr_errors, NULL, _IONBF, 0);
646         }
647
648         while (1) {
649                 if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) {
650                         ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
651                         break;
652                 }
653                 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
654                         if (ast_test_flag(&flags, ignore_hangup)) {
655                                 ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
656                                 send_eivr_event(eivr_events, 'I', "HANGUP", chan);
657                                 hangup_info_sent = 1;
658                         } else {
659                                 ast_verb(3, "Got check_hangup\n");
660                                 send_eivr_event(eivr_events, 'H', NULL, chan);
661                                 break;
662                         }
663                 }
664  
665                 ready_fd = 0;
666                 ms = 100;
667                 errno = 0;
668                 exception = 0;
669  
670                 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd) ? 2 : 1, &exception, &ready_fd, &ms);
671  
672                 if (ast_channel_state(chan) == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
673                         AST_LIST_LOCK(&u->finishlist);
674                         while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
675                                 send_eivr_event(eivr_events, 'F', entry->filename, chan);
676                                 ast_free(entry);
677                         }
678                         AST_LIST_UNLOCK(&u->finishlist);
679                 }
680  
681                 if (ast_channel_state(chan) == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
682                         /* the channel has something */
683                         f = ast_read(chan);
684                         if (!f) {
685                                 ast_verb(3, "Returned no frame\n");
686                                 send_eivr_event(eivr_events, 'H', NULL, chan);
687                                 break;
688                         }
689                         if (f->frametype == AST_FRAME_DTMF) {
690                                 send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
691                                 if (u->option_autoclear) {
692                                         AST_LIST_LOCK(&u->playlist);
693                                         if (!u->abort_current_sound && !u->playing_silence) {
694                                                 /* send interrupted file as T data */
695                                                 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
696                                                         send_eivr_event(eivr_events, 'T', entry->filename, chan);
697                                                         ast_free(entry);
698                                                 }
699                                         }
700                                         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
701                                                 send_eivr_event(eivr_events, 'D', entry->filename, chan);
702                                                 ast_free(entry);
703                                         }
704                                         if (!u->playing_silence)
705                                                 u->abort_current_sound = 1;
706                                         AST_LIST_UNLOCK(&u->playlist);
707                                 }
708                         } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
709                                 ast_verb(3, "Got AST_CONTROL_HANGUP\n");
710                                 send_eivr_event(eivr_events, 'H', NULL, chan);
711                                 if (f->data.uint32) {
712                                         ast_channel_hangupcause_set(chan, f->data.uint32);
713                                 }
714                                 ast_frfree(f);
715                                 break;
716                         }
717                         ast_frfree(f);
718                 } else if (ready_fd == *eivr_commands_fd) {
719                         char input[1024];
720  
721                         if (exception || (dup2(*eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
722                                 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
723                                 break;
724                         }
725   
726                         if (!fgets(input, sizeof(input), eivr_commands)) {
727                                 continue;
728                         }
729
730                         ast_strip(input);
731                         ast_verb(4, "got command '%s'\n", input);
732
733                         if (strlen(input) < 3) {
734                                 continue;
735                         }
736
737                         if (input[0] == EIVR_CMD_PARM) {
738                                 struct ast_str *tmp = (struct ast_str *) args;
739                                 send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
740                         } else if (input[0] == EIVR_CMD_DTMF) {
741                                 ast_verb(4, "Sending DTMF: %s\n", &input[2]);
742                                 ast_eivr_senddtmf(chan, &input[2]);
743                         } else if (input[0] == EIVR_CMD_ANS) {
744                                 ast_verb(3, "Answering channel if needed and starting generator\n");
745                                 if (ast_channel_state(chan) != AST_STATE_UP) {
746                                         if (ast_test_flag(&flags, run_dead)) {
747                                                 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
748                                                 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
749                                                 continue;
750                                         }
751                                         if (ast_answer(chan)) {
752                                                 ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n");
753                                                 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
754                                                 continue;
755                                         }
756                                 }
757                                 if (!(u->gen_active)) {
758                                         if (ast_activate_generator(chan, &gen, u) < 0) {
759                                                 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
760                                                 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
761                                         } else {
762                                                 u->gen_active = 1;
763                                         }
764                                 }
765                         } else if (input[0] == EIVR_CMD_IRPT) {
766                                 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
767                                         ast_chan_log(LOG_WARNING, chan, "Queue 'I'nterrupt called on unanswered channel\n");
768                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
769                                         continue;
770                                 }
771                                 AST_LIST_LOCK(&u->playlist);
772                                 if (!u->abort_current_sound && !u->playing_silence) {
773                                         /* send interrupted file as T data */
774                                         if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
775                                                 send_eivr_event(eivr_events, 'T', entry->filename, chan);
776                                                 ast_free(entry);
777                                         }
778                                 }
779                                 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
780                                         send_eivr_event(eivr_events, 'D', entry->filename, chan);
781                                         ast_free(entry);
782                                 }
783                                 if (!u->playing_silence) {
784                                         u->abort_current_sound = 1;
785                                 }
786                                 AST_LIST_UNLOCK(&u->playlist);
787                         } else if (input[0] == EIVR_CMD_SQUE) {
788                                 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
789                                         ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n");
790                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
791                                         continue;
792                                 }
793                                 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
794                                         ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
795                                         send_eivr_event(eivr_events, 'Z', &input[2], chan);
796                                 } else {
797                                         AST_LIST_LOCK(&u->playlist);
798                                         if (!u->abort_current_sound && !u->playing_silence) {
799                                                 /* send interrupted file as T data */
800                                                 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
801                                                         send_eivr_event(eivr_events, 'T', entry->filename, chan);
802                                                         ast_free(entry);
803                                                 }
804                                         }
805                                         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
806                                                 send_eivr_event(eivr_events, 'D', entry->filename, chan);
807                                                 ast_free(entry);
808                                         }
809                                         if (!u->playing_silence) {
810                                                 u->abort_current_sound = 1;
811                                         }
812                                         entry = make_entry(&input[2]);
813                                         if (entry) {
814                                                 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
815                                         }
816                                         AST_LIST_UNLOCK(&u->playlist);
817                                 }
818                         } else if (input[0] == EIVR_CMD_APND) {
819                                 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
820                                         ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
821                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
822                                         continue;
823                                 }
824                                 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
825                                         ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
826                                         send_eivr_event(eivr_events, 'Z', &input[2], chan);
827                                 } else {
828                                         entry = make_entry(&input[2]);
829                                         if (entry) {
830                                                 AST_LIST_LOCK(&u->playlist);
831                                                 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
832                                                 AST_LIST_UNLOCK(&u->playlist);
833                                         }
834                                 }
835                         } else if (input[0] == EIVR_CMD_GET) {
836                                 char response[2048];
837                                 ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
838                                 ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
839                                 send_eivr_event(eivr_events, 'G', response, chan);
840                         } else if (input[0] == EIVR_CMD_SVAR) {
841                                 ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
842                                 ast_eivr_setvariable(chan, &input[2]);
843                         } else if (input[0] == EIVR_CMD_LOG) {
844                                 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
845                         } else if (input[0] == EIVR_CMD_XIT) {
846                                 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
847                                 ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
848                                 res = 0;
849                                 break;
850                         } else if (input[0] == EIVR_CMD_EXIT) {
851                                 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
852                                 send_eivr_event(eivr_events, 'E', NULL, chan);
853                                 res = 0;
854                                 break;
855                         } else if (input[0] == EIVR_CMD_HGUP) {
856                                 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
857                                 send_eivr_event(eivr_events, 'H', NULL, chan);
858                                 break;
859                         } else if (input[0] == EIVR_CMD_OPT) {
860                                 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
861                                         ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
862                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
863                                         continue;
864                                 }
865                                 if (!strcasecmp(&input[2], "autoclear"))
866                                         u->option_autoclear = 1;
867                                 else if (!strcasecmp(&input[2], "noautoclear"))
868                                         u->option_autoclear = 0;
869                                 else
870                                         ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
871                         }
872                 } else if (eivr_errors_fd && (ready_fd == *eivr_errors_fd)) {
873                         char input[1024];
874   
875                         if (exception || feof(eivr_errors)) {
876                                 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
877                                 break;
878                         }
879                         if (fgets(input, sizeof(input), eivr_errors)) {
880                                 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
881                         }
882                 } else if ((ready_fd < 0) && ms) { 
883                         if (errno == 0 || errno == EINTR)
884                                 continue;
885  
886                         ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
887                         break;
888                 }
889         }
890  
891         exit:
892         if (test_available_fd > -1) {
893                 close(test_available_fd);
894         }
895         if (eivr_events) {
896                 fclose(eivr_events);
897                 *eivr_events_fd = -1;
898         }
899         if (eivr_commands) {
900                 fclose(eivr_commands);
901                 *eivr_commands_fd = -1;
902         }
903         if (eivr_errors) {
904                 fclose(eivr_errors);
905                 *eivr_errors_fd = -1;
906         }
907         return res;
908 }
909
910 static int unload_module(void)
911 {
912         return ast_unregister_application(app);
913 }
914
915 static int load_module(void)
916 {
917         return ast_register_application_xml(app, app_exec);
918 }
919
920 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "External IVR Interface Application");
921