Last batch of 'static' qualifiers for module-level global variables.
[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         buf = ast_strdupa(data);
377         AST_STANDARD_APP_ARGS(eivr_args, buf);
378
379         if ((s = strchr(eivr_args.cmd[0], '('))) {
380                 s[0] = ',';
381                 if (( e = strrchr(s, ')')) ) {
382                         *e = '\0';
383                 } else {
384                         ast_log(LOG_ERROR, "Parse error, no closing paren?\n");
385                 }
386                 AST_STANDARD_APP_ARGS(application_args, eivr_args.cmd[0]);
387                 app_args = application_args.argv;
388
389                 /* Put the application + the arguments in a | delimited list */
390                 ast_str_reset(pipe_delim_args);
391                 for (j = 0; application_args.cmd[j] != NULL; j++) {
392                         ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
393                 }
394
395                 /* Parse the ExternalIVR() arguments */
396                 if (option_debug)
397                         ast_debug(1, "Parsing options from: [%s]\n", eivr_args.cmd[1]);
398                 ast_app_parse_options(app_opts, &flags, opts, eivr_args.cmd[1]);
399                 if (option_debug) {
400                         if (ast_test_flag(&flags, noanswer))
401                                 ast_debug(1, "noanswer is set\n");
402                         if (ast_test_flag(&flags, ignore_hangup))
403                                 ast_debug(1, "ignore_hangup is set\n");
404                         if (ast_test_flag(&flags, run_dead))
405                                 ast_debug(1, "run_dead is set\n");
406                 }
407
408         } else {
409                 app_args = eivr_args.argv;
410                 for (j = 0; eivr_args.cmd[j] != NULL; j++) {
411                         ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", eivr_args.cmd[j]);
412                 }
413         }
414         
415         if (ast_strlen_zero(data)) {
416                 ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
417                 return -1;
418         }
419
420         if (!(ast_test_flag(&flags, noanswer))) {
421                 ast_chan_log(LOG_WARNING, chan, "Answering channel and starting generator\n");
422                 if (chan->_state != AST_STATE_UP) {
423                         if (ast_test_flag(&flags, run_dead)) {
424                                 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
425                                 goto exit;
426                         }
427                         ast_answer(chan);
428                 }
429                 if (ast_activate_generator(chan, &gen, u) < 0) {
430                         ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
431                         goto exit;
432                 } else {
433                         u->gen_active = 1;
434                 }
435         }
436
437         if (!strncmp(app_args[0], "ivr://", 6)) {
438                 struct ast_tcptls_session_args ivr_desc = {
439                         .accept_fd = -1,
440                         .name = "IVR",
441                 };
442                 struct ast_hostent hp;
443
444                 /*communicate through socket to server*/
445                 ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]);
446                 ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname));
447                 if ((port_str = strchr(hostname, ':')) != NULL) {
448                         port_str[0] = 0;
449                         port_str += 1;
450                         port = atoi(port_str);
451                 }
452                 if (!port) {
453                         port = 2949;  /* default port, if one is not provided */
454                 }
455
456                 ast_gethostbyname(hostname, &hp);
457                 ivr_desc.local_address.sin_family = AF_INET;
458                 ivr_desc.local_address.sin_port = htons(port);
459                 memcpy(&ivr_desc.local_address.sin_addr.s_addr, hp.hp.h_addr, hp.hp.h_length);
460                 ser = ast_tcptls_client_start(&ivr_desc);
461
462                 if (!ser) {
463                         goto exit;
464                 }
465                 res = eivr_comm(chan, u, ser->fd, ser->fd, -1, pipe_delim_args, flags);
466
467         } else {
468         
469                 if (pipe(child_stdin)) {
470                         ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
471                         goto exit;
472                 }
473                 if (pipe(child_stdout)) {
474                         ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
475                         goto exit;
476                 }
477                 if (pipe(child_stderr)) {
478                         ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
479                         goto exit;
480                 }
481         
482                 pid = ast_safe_fork(0);
483                 if (pid < 0) {
484                         ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
485                         goto exit;
486                 }
487         
488                 if (!pid) {
489                         /* child process */
490                         if (ast_opt_high_priority)
491                                 ast_set_priority(0);
492         
493                         dup2(child_stdin[0], STDIN_FILENO);
494                         dup2(child_stdout[1], STDOUT_FILENO);
495                         dup2(child_stderr[1], STDERR_FILENO);
496                         ast_close_fds_above_n(STDERR_FILENO);
497                         execv(app_args[0], app_args);
498                         fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
499                         _exit(1);
500                 } else {
501                         /* parent process */
502                         close(child_stdin[0]);
503                         child_stdin[0] = 0;
504                         close(child_stdout[1]);
505                         child_stdout[1] = 0;
506                         close(child_stderr[1]);
507                         child_stderr[1] = 0;
508                         res = eivr_comm(chan, u, child_stdin[1], child_stdout[0], child_stderr[0], pipe_delim_args, flags);
509                 }
510         }
511
512         exit:
513         if (u->gen_active)
514                 ast_deactivate_generator(chan);
515
516         if (child_stdin[0])
517                 close(child_stdin[0]);
518
519         if (child_stdin[1])
520                 close(child_stdin[1]);
521
522         if (child_stdout[0])
523                 close(child_stdout[0]);
524
525         if (child_stdout[1])
526                 close(child_stdout[1]);
527
528         if (child_stderr[0])
529                 close(child_stderr[0]);
530
531         if (child_stderr[1])
532                 close(child_stderr[1]);
533         if (ser) {
534                 ao2_ref(ser, -1);
535         }
536         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
537                 ast_free(entry);
538
539         return res;
540 }
541
542 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
543                                 int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd, 
544                                 const struct ast_str *args, const struct ast_flags flags)
545 {
546         struct playlist_entry *entry;
547         struct ast_frame *f;
548         int ms;
549         int exception;
550         int ready_fd;
551         int waitfds[2] = { eivr_commands_fd, eivr_errors_fd };
552         struct ast_channel *rchan;
553         char *command;
554         int res = -1;
555         int test_available_fd = -1;
556         int hangup_info_sent = 0;
557   
558         FILE *eivr_commands = NULL;
559         FILE *eivr_errors = NULL;
560         FILE *eivr_events = NULL;
561
562         if (!(eivr_events = fdopen(eivr_events_fd, "w"))) {
563                 ast_chan_log(LOG_WARNING, chan, "Could not open stream to send events\n");
564                 goto exit;
565         }
566         if (!(eivr_commands = fdopen(eivr_commands_fd, "r"))) {
567                 ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive commands\n");
568                 goto exit;
569         }
570         if (eivr_errors_fd > -1) {  /* if opening a socket connection, error stream will not be used */
571                 if (!(eivr_errors = fdopen(eivr_errors_fd, "r"))) {
572                         ast_chan_log(LOG_WARNING, chan, "Could not open stream to receive errors\n");
573                         goto exit;
574                 }
575         }
576
577         test_available_fd = open("/dev/null", O_RDONLY);
578  
579         setvbuf(eivr_events, NULL, _IONBF, 0);
580         setvbuf(eivr_commands, NULL, _IONBF, 0);
581         if (eivr_errors) {
582                 setvbuf(eivr_errors, NULL, _IONBF, 0);
583         }
584
585         res = 0;
586  
587         while (1) {
588                 if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
589                         ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
590                         res = -1;
591                         break;
592                 }
593                 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
594                         if (ast_test_flag(&flags, ignore_hangup)) {
595                                 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
596                                 send_eivr_event(eivr_events, 'I', "HANGUP", chan);
597                                 hangup_info_sent = 1;
598                         } else {
599                                 ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
600                                 send_eivr_event(eivr_events, 'H', NULL, chan);
601                                 res = -1;
602                                 break;
603                         }
604                 }
605  
606                 ready_fd = 0;
607                 ms = 100;
608                 errno = 0;
609                 exception = 0;
610  
611                 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd < 0) ? 1 : 2, &exception, &ready_fd, &ms);
612  
613                 if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
614                         AST_LIST_LOCK(&u->finishlist);
615                         while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
616                                 send_eivr_event(eivr_events, 'F', entry->filename, chan);
617                                 ast_free(entry);
618                         }
619                         AST_LIST_UNLOCK(&u->finishlist);
620                 }
621  
622                 if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
623                         /* the channel has something */
624                         f = ast_read(chan);
625                         if (!f) {
626                                 ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
627                                 send_eivr_event(eivr_events, 'H', NULL, chan);
628                                 res = -1;
629                                 break;
630                         }
631                         if (f->frametype == AST_FRAME_DTMF) {
632                                 send_eivr_event(eivr_events, f->subclass, NULL, chan);
633                                 if (u->option_autoclear) {
634                                         if (!u->abort_current_sound && !u->playing_silence)
635                                                 send_eivr_event(eivr_events, 'T', NULL, chan);
636                                         AST_LIST_LOCK(&u->playlist);
637                                         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
638                                                 send_eivr_event(eivr_events, 'D', entry->filename, chan);
639                                                 ast_free(entry);
640                                         }
641                                         if (!u->playing_silence)
642                                                 u->abort_current_sound = 1;
643                                         AST_LIST_UNLOCK(&u->playlist);
644                                 }
645                         } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
646                                 ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
647                                 send_eivr_event(eivr_events, 'H', NULL, chan);
648                                 if (f->data.uint32) {
649                                         chan->hangupcause = f->data.uint32;
650                                 }
651                                 ast_frfree(f);
652                                 res = -1;
653                                 break;
654                         }
655                         ast_frfree(f);
656                 } else if (ready_fd == eivr_commands_fd) {
657                         char input[1024];
658  
659                         if (exception || (dup2(eivr_commands_fd, test_available_fd) == -1) || feof(eivr_commands)) {
660                                 ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
661                                 res = -1;
662                                 break;
663                         }
664   
665                         if (!fgets(input, sizeof(input), eivr_commands))
666                                 continue;
667  
668                         command = ast_strip(input);
669   
670                         if (option_debug)
671                                 ast_debug(1, "got command '%s'\n", input);
672   
673                         if (strlen(input) < 4)
674                                 continue;
675   
676                         if (input[0] == 'P') {
677                                 struct ast_str *tmp = (struct ast_str *) args;
678                                 send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
679                         } else if ( input[0] == 'T' ) {
680                                 ast_chan_log(LOG_WARNING, chan, "Answering channel if needed and starting generator\n");
681                                 if (chan->_state != AST_STATE_UP) {
682                                         if (ast_test_flag(&flags, run_dead)) {
683                                                 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
684                                                 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
685                                                 continue;
686                                         }
687                                         ast_answer(chan);
688                                 }
689                                 if (!(u->gen_active)) {
690                                         if (ast_activate_generator(chan, &gen, u) < 0) {
691                                                 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
692                                                 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
693                                         } else {
694                                                 u->gen_active = 1;
695                                         }
696                                 }
697                         } else if (input[0] == 'S') {
698                                 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
699                                         ast_chan_log(LOG_WARNING, chan, "Queue 'S'et called on unanswered channel\n");
700                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
701                                         continue;
702                                 }
703                                 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
704                                         ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
705                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
706                                         strcpy(&input[2], "exception");
707                                 }
708                                 if (!u->abort_current_sound && !u->playing_silence)
709                                         send_eivr_event(eivr_events, 'T', NULL, chan);
710                                 AST_LIST_LOCK(&u->playlist);
711                                 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
712                                         send_eivr_event(eivr_events, 'D', entry->filename, chan);
713                                         ast_free(entry);
714                                 }
715                                 if (!u->playing_silence)
716                                         u->abort_current_sound = 1;
717                                 entry = make_entry(&input[2]);
718                                 if (entry)
719                                         AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
720                                 AST_LIST_UNLOCK(&u->playlist);
721                         } else if (input[0] == 'A') {
722                                 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
723                                         ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
724                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
725                                         continue;
726                                 }
727                                 if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
728                                         ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
729                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
730                                         strcpy(&input[2], "exception");
731                                 }
732                                 entry = make_entry(&input[2]);
733                                 if (entry) {
734                                         AST_LIST_LOCK(&u->playlist);
735                                         AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
736                                         AST_LIST_UNLOCK(&u->playlist);
737                                 }
738                         } else if (input[0] == 'G') {
739                                 /* A get variable message:  "G,variable1,variable2,..." */
740                                 char response[2048];
741
742                                 ast_chan_log(LOG_NOTICE, chan, "Getting a Variable out of the channel: %s\n", &input[2]);
743                                 ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
744                                 send_eivr_event(eivr_events, 'G', response, chan);
745                         } else if (input[0] == 'V') {
746                                 /* A set variable message:  "V,variablename=foo" */
747                                 ast_chan_log(LOG_NOTICE, chan, "Setting a Variable up: %s\n", &input[2]);
748                                 ast_eivr_setvariable(chan, &input[2]);
749                         } else if (input[0] == 'L') {
750                                 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
751                         } else if (input[0] == 'X') {
752                                 ast_chan_log(LOG_NOTICE, chan, "Exiting ExternalIVR: %s\n", &input[2]);
753                                 /*! \todo add deprecation debug message for X command here */
754                                 res = 0;
755                                 break;
756                         } else if (input[0] == 'E') {
757                                 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
758                                 send_eivr_event(eivr_events, 'E', NULL, chan);
759                                 res = 0;
760                                 break;
761                         } else if (input[0] == 'H') {
762                                 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
763                                 send_eivr_event(eivr_events, 'H', NULL, chan);
764                                 res = -1;
765                                 break;
766                         } else if (input[0] == 'O') {
767                                 if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
768                                         ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
769                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
770                                         continue;
771                                 }
772                                 if (!strcasecmp(&input[2], "autoclear"))
773                                         u->option_autoclear = 1;
774                                 else if (!strcasecmp(&input[2], "noautoclear"))
775                                         u->option_autoclear = 0;
776                                 else
777                                         ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
778                         }
779                 } else if (eivr_errors_fd && ready_fd == eivr_errors_fd) {
780                         char input[1024];
781   
782                         if (exception || feof(eivr_errors)) {
783                                 ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
784                                 res = -1;
785                                 break;
786                         }
787                         if (fgets(input, sizeof(input), eivr_errors)) {
788                                 command = ast_strip(input);
789                                 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
790                         }
791                 } else if ((ready_fd < 0) && ms) { 
792                         if (errno == 0 || errno == EINTR)
793                                 continue;
794  
795                         ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
796                         break;
797                 }
798         }
799   
800  
801 exit:
802  
803         if (test_available_fd > -1) {
804                 close(test_available_fd);
805         }
806
807         if (eivr_events)
808                 fclose(eivr_events);
809  
810         if (eivr_commands)
811                 fclose(eivr_commands);
812
813         if (eivr_errors)
814                 fclose(eivr_errors);
815   
816         return res;
817  
818 }
819
820 static int unload_module(void)
821 {
822         return ast_unregister_application(app);
823 }
824
825 static int load_module(void)
826 {
827         return ast_register_application_xml(app, app_exec);
828 }
829
830 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "External IVR Interface Application");