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