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