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