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