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