res_rtp_asterisk: Avoid close the rtp/rtcp fd twice.
[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 /*** MODULEINFO
35         <support_level>extended</support_level>
36  ***/
37
38 #include "asterisk.h"
39
40 #include <signal.h>
41
42 #include "asterisk/lock.h"
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/linkedlists.h"
48 #include "asterisk/app.h"
49 #include "asterisk/utils.h"
50 #include "asterisk/tcptls.h"
51 #include "asterisk/astobj2.h"
52
53 /*** DOCUMENTATION
54         <application name="ExternalIVR" language="en_US">
55                 <synopsis>
56                         Interfaces with an external IVR application.
57                 </synopsis>
58                 <syntax>
59                         <parameter name="command|ivr://host" required="true" hasparams="true">
60                                 <argument name="arg1" />
61                                 <argument name="arg2" multiple="yes" />
62                         </parameter>
63                         <parameter name="options">
64                                 <optionlist>
65                                         <option name="n">
66                                                 <para>Tells ExternalIVR() not to answer the channel.</para>
67                                         </option>
68                                         <option name="i">
69                                                 <para>Tells ExternalIVR() not to send a hangup and exit when the
70                                                 channel receives a hangup, instead it sends an <literal>I</literal>
71                                                 informative message meaning that the external application MUST hang
72                                                 up the call with an <literal>H</literal> command.</para>
73                                         </option>
74                                         <option name="d">
75                                                 <para>Tells ExternalIVR() to run on a channel that has been hung up
76                                                 and will not look for hangups. The external application must exit with
77                                                 an <literal>E</literal> command.</para>
78                                         </option>
79                                 </optionlist>
80                         </parameter>
81                 </syntax>
82                 <description>
83                         <para>Either forks a process to run given command or makes a socket to connect
84                         to given host and starts a generator on the channel. The generator's play list
85                         is controlled by the external application, which can add and clear entries via
86                         simple commands issued over its stdout. The external application will receive
87                         all DTMF events received on the channel, and notification if the channel is
88                         hung up. The received on the channel, and notification if the channel is hung
89                         up. The application will not be forcibly terminated when the channel is hung up.
90                         For more information see <filename>doc/AST.pdf</filename>.</para>
91                 </description>
92         </application>
93  ***/
94
95 static const char app[] = "ExternalIVR";
96
97 /* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
98 #define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, ast_channel_name(channel) , ## __VA_ARGS__)
99
100 /* Commands */
101 #define EIVR_CMD_APND 'A' /* append to prompt queue */
102 #define EIVR_CMD_DTMF 'D' /* send DTMF */
103 #define EIVR_CMD_EXIT 'E' /* exit */
104 #define EIVR_CMD_GET  'G' /* get channel varable(s) */
105 #define EIVR_CMD_HGUP 'H' /* hangup */
106 #define EIVR_CMD_IRPT 'I' /* interrupt */
107 #define EIVR_CMD_LOG  'L' /* log message */
108 #define EIVR_CMD_OPT  'O' /* option */
109 #define EIVR_CMD_PARM 'P' /* return supplied params */
110 #define EIVR_CMD_SQUE 'S' /* (re)set prompt queue */
111 #define EIVR_CMD_ANS  'T' /* answer channel */
112 #define EIVR_CMD_SVAR 'V' /* set channel varable(s) */
113 #define EIVR_CMD_XIT  'X' /* exit **depricated** */
114
115 #define EXTERNALIVR_PORT 2949
116
117 enum options_flags {
118         noanswer = (1 << 0),
119         ignore_hangup = (1 << 1),
120         run_dead = (1 << 2),
121 };
122
123 AST_APP_OPTIONS(app_opts, {
124         AST_APP_OPTION('n', noanswer),
125         AST_APP_OPTION('i', ignore_hangup),
126         AST_APP_OPTION('d', run_dead),
127 });
128
129 struct playlist_entry {
130         AST_LIST_ENTRY(playlist_entry) list;
131         char filename[1];
132 };
133
134 struct ivr_localuser {
135         struct ast_channel *chan;
136         AST_LIST_HEAD(playlist, playlist_entry) playlist;
137         AST_LIST_HEAD(finishlist, playlist_entry) finishlist;
138         int abort_current_sound;
139         int playing_silence;
140         int option_autoclear;
141         int gen_active;
142 };
143
144
145 struct gen_state {
146         struct ivr_localuser *u;
147         struct ast_filestream *stream;
148         struct playlist_entry *current;
149         int sample_queue;
150 };
151
152 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
153         struct ast_iostream *eivr_events,
154         struct ast_iostream *eivr_commands,
155         struct ast_iostream *eivr_errors,
156         const struct ast_str *args, const struct ast_flags flags);
157
158 static void send_eivr_event(struct ast_iostream *stream, const char event, const char *data,
159         const struct ast_channel *chan)
160 {
161         struct ast_str *tmp = ast_str_create(12);
162
163         ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
164         if (data) {
165                 ast_str_append(&tmp, 0, ",%s", data);
166         }
167         ast_str_append(&tmp, 0, "\n");
168         ast_iostream_write(stream, ast_str_buffer(tmp), strlen(ast_str_buffer(tmp)));
169         ast_str_truncate(tmp, -1);
170
171         ast_debug(1, "sent '%s'", ast_str_buffer(tmp));
172         ast_free(tmp);
173 }
174
175 static void *gen_alloc(struct ast_channel *chan, void *params)
176 {
177         struct ivr_localuser *u = params;
178         struct gen_state *state;
179
180         if (!(state = ast_calloc(1, sizeof(*state))))
181                 return NULL;
182
183         state->u = u;
184
185         return state;
186 }
187
188 static void gen_closestream(struct gen_state *state)
189 {
190         if (!state->stream)
191                 return;
192
193         ast_closestream(state->stream);
194         ast_channel_stream_set(state->u->chan, NULL);
195         state->stream = NULL;
196 }
197
198 static void gen_release(struct ast_channel *chan, void *data)
199 {
200         struct gen_state *state = data;
201
202         gen_closestream(state);
203         ast_free(data);
204 }
205
206 /* caller has the playlist locked */
207 static int gen_nextfile(struct gen_state *state)
208 {
209         struct ivr_localuser *u = state->u;
210         char *file_to_stream;
211
212         u->abort_current_sound = 0;
213         u->playing_silence = 0;
214         gen_closestream(state);
215
216         while (!state->stream) {
217                 state->current = AST_LIST_FIRST(&u->playlist);
218                 if (state->current) {
219                         file_to_stream = state->current->filename;
220                 } else {
221                         file_to_stream = "silence/10";
222                         u->playing_silence = 1;
223                 }
224
225                 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, ast_channel_language(u->chan), 1))) {
226                         ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
227                         AST_LIST_LOCK(&u->playlist);
228                         AST_LIST_REMOVE_HEAD(&u->playlist, list);
229                         AST_LIST_UNLOCK(&u->playlist);
230                         if (!u->playing_silence) {
231                                 continue;
232                         } else {
233                                 break;
234                         }
235                 }
236         }
237
238         return (!state->stream);
239 }
240
241 static struct ast_frame *gen_readframe(struct gen_state *state)
242 {
243         struct ast_frame *f = NULL;
244         struct ivr_localuser *u = state->u;
245
246         if (u->abort_current_sound ||
247                 (u->playing_silence && AST_LIST_FIRST(&u->playlist))) {
248                 gen_closestream(state);
249                 AST_LIST_LOCK(&u->playlist);
250                 gen_nextfile(state);
251                 AST_LIST_UNLOCK(&u->playlist);
252         }
253
254         if (!(state->stream && (f = ast_readframe(state->stream)))) {
255                 if (state->current) {
256                         /* remove finished file from playlist */
257                         AST_LIST_LOCK(&u->playlist);
258                         AST_LIST_REMOVE_HEAD(&u->playlist, list);
259                         AST_LIST_UNLOCK(&u->playlist);
260                         /* add finished file to finishlist */
261                         AST_LIST_LOCK(&u->finishlist);
262                         AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
263                         AST_LIST_UNLOCK(&u->finishlist);
264                         state->current = NULL;
265                 }
266                 if (!gen_nextfile(state))
267                         f = ast_readframe(state->stream);
268         }
269
270         return f;
271 }
272
273 static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
274 {
275         struct gen_state *state = data;
276         struct ast_frame *f = NULL;
277         int res = 0;
278
279         state->sample_queue += samples;
280
281         while (state->sample_queue > 0) {
282                 if (!(f = gen_readframe(state)))
283                         return -1;
284
285                 res = ast_write(chan, f);
286                 ast_frfree(f);
287                 if (res < 0) {
288                         ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
289                         return -1;
290                 }
291                 state->sample_queue -= f->samples;
292         }
293
294         return res;
295 }
296
297 static struct ast_generator gen =
298 {
299         .alloc = gen_alloc,
300         .release = gen_release,
301         .generate = gen_generate,
302 };
303
304 static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
305 {
306         /* original input data: "G,var1,var2," */
307         /* data passed as "data":  "var1,var2" */
308
309         char *inbuf, *variable;
310         const char *value;
311         int j;
312         struct ast_str *newstring = ast_str_alloca(outbuflen); 
313
314         outbuf[0] = '\0';
315
316         for (j = 1, inbuf = data; ; j++) {
317                 variable = strsep(&inbuf, ",");
318                 if (variable == NULL) {
319                         int outstrlen = strlen(outbuf);
320                         if (outstrlen && outbuf[outstrlen - 1] == ',') {
321                                 outbuf[outstrlen - 1] = 0;
322                         }
323                         break;
324                 }
325                 
326                 ast_channel_lock(chan);
327                 if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
328                         value = "";
329                 }
330
331                 ast_str_append(&newstring, 0, "%s=%s,", variable, value);
332                 ast_channel_unlock(chan);
333                 ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen);
334         }
335 }
336
337 static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
338 {
339         char *value;
340
341         char *inbuf = ast_strdupa(data), *variable;
342
343         for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) {
344                 ast_debug(1, "Setting up a variable: %s\n", variable);
345                 /* variable contains "varname=value" */
346                 value = strchr(variable, '=');
347                 if (!value) {
348                         value = "";
349                 } else {
350                         *value++ = '\0';
351                 }
352                 pbx_builtin_setvar_helper(chan, variable, value);
353         }
354 }
355
356 static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
357 {
358
359         char *data;
360         int dinterval = 0, duration = 0;
361         AST_DECLARE_APP_ARGS(args,
362                 AST_APP_ARG(digits);
363                 AST_APP_ARG(dinterval);
364                 AST_APP_ARG(duration);
365         );
366
367         data = ast_strdupa(vdata);
368         AST_STANDARD_APP_ARGS(args, data);
369
370         if (!ast_strlen_zero(args.dinterval)) {
371                 ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
372         }
373         if (!ast_strlen_zero(args.duration)) {
374                 ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
375         }
376         ast_verb(4, "Sending DTMF: %s %d %d\n", args.digits, dinterval <= 0 ? 250 : dinterval, duration);
377         ast_dtmf_stream(chan, NULL, args.digits, dinterval <= 0 ? 250 : dinterval, duration);
378 }
379
380 static struct playlist_entry *make_entry(const char *filename)
381 {
382         struct playlist_entry *entry;
383
384         if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
385                 return NULL;
386
387         strcpy(entry->filename, filename);
388
389         return entry;
390 }
391
392 static int app_exec(struct ast_channel *chan, const char *data)
393 {
394         struct ast_flags flags = { 0, };
395         char *opts[0];
396         struct playlist_entry *entry;
397         int child_stdin[2] = { -1, -1 };
398         int child_stdout[2] = { -1, -1 };
399         int child_stderr[2] = { -1, -1 };
400         struct ast_iostream *stream_stdin = NULL, *stream_stdout = NULL,
401                 *stream_stderr = NULL;
402         int res = -1;
403         int pid;
404
405         struct ast_tcptls_session_instance *ser = NULL;
406
407         struct ivr_localuser foo = {
408                 .playlist = AST_LIST_HEAD_INIT_VALUE,
409                 .finishlist = AST_LIST_HEAD_INIT_VALUE,
410                 .gen_active = 0,
411                 .playing_silence = 1,
412         };
413         struct ivr_localuser *u = &foo;
414
415         char *buf;
416         int j;
417         char *s, **app_args, *e; 
418         struct ast_str *comma_delim_args = ast_str_alloca(100);
419
420         AST_DECLARE_APP_ARGS(eivr_args,
421                 AST_APP_ARG(application);
422                 AST_APP_ARG(options);
423         );
424         AST_DECLARE_APP_ARGS(application_args,
425                 AST_APP_ARG(cmd)[32];
426         );
427
428         u->abort_current_sound = 0;
429         u->chan = chan;
430
431         if (ast_strlen_zero(data)) {
432                 ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n");
433                 goto exit;
434         }
435
436         buf = ast_strdupa(data);
437         AST_STANDARD_APP_ARGS(eivr_args, buf);
438
439         ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application);
440         ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options);
441
442         /* Parse out any application arguments */
443         if ((s = strchr(eivr_args.application, '('))) {
444                 s[0] = ',';
445                 if ((e = strrchr(s, ')'))) {
446                         *e = '\0';
447                 } else {
448                         ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n");
449                         goto exit;
450                 }
451         }
452
453         AST_STANDARD_APP_ARGS(application_args, eivr_args.application);
454         app_args = application_args.argv;
455
456         /* Put the application + the arguments in a , delimited list */
457         ast_str_reset(comma_delim_args);
458         for (j = 0; application_args.cmd[j] != NULL; j++) {
459                 ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
460         }
461
462         /* Get rid of any extraneous arguments */
463         if (eivr_args.options && (s = strchr(eivr_args.options, ','))) {
464                 *s = '\0';
465         }
466
467         /* Parse the ExternalIVR() arguments */
468         ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options);
469         ast_app_parse_options(app_opts, &flags, opts, eivr_args.options);
470         if (ast_test_flag(&flags, noanswer)) {
471                 ast_verb(4, "noanswer is set\n");
472         }
473         if (ast_test_flag(&flags, ignore_hangup)) {
474                 ast_verb(4, "ignore_hangup is set\n");
475         }
476         if (ast_test_flag(&flags, run_dead)) {
477                 ast_verb(4, "run_dead is set\n");
478         }
479         
480         if (!(ast_test_flag(&flags, noanswer))) {
481                 ast_verb(3, "Answering channel and starting generator\n");
482                 if (ast_channel_state(chan) != AST_STATE_UP) {
483                         if (ast_test_flag(&flags, run_dead)) {
484                                 ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
485                                 goto exit;
486                         }
487                         ast_answer(chan);
488                 }
489                 if (ast_activate_generator(chan, &gen, u) < 0) {
490                         ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n");
491                         goto exit;
492                 } else {
493                         u->gen_active = 1;
494                 }
495         }
496
497         if (!strncmp(app_args[0], "ivr://", sizeof("ivr://") - 1)) {
498                 struct ast_tcptls_session_args ivr_desc = {
499                         .accept_fd = -1,
500                         .name = "IVR",
501                 };
502                 struct ast_sockaddr *addrs;
503                 int num_addrs = 0, i = 0;
504                 char *host = app_args[0] + sizeof("ivr://") - 1;
505
506                 /* Communicate through socket to server */
507                 ast_debug(1, "Parsing hostname/port for socket connect from \"%s\"\n", host);
508
509                 if (!(num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC))) {
510                         ast_chan_log(LOG_ERROR, chan, "Unable to locate host '%s'\n", host);
511                         goto exit;
512                 }
513
514                 for (i = 0; i < num_addrs; i++) {
515                         if (!ast_sockaddr_port(&addrs[i])) {
516                                 /* Default port if not specified */
517                                 ast_sockaddr_set_port(&addrs[i], EXTERNALIVR_PORT);
518                         }
519                         ast_sockaddr_copy(&ivr_desc.remote_address, &addrs[i]);
520                         if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) {
521                                 continue;
522                         }
523                         break;
524                 }
525
526                 ast_free(addrs);
527
528                 if (i == num_addrs) {
529                         ast_chan_log(LOG_ERROR, chan, "Could not connect to any host.  ExternalIVR failed.\n");
530                         goto exit;
531                 }
532
533                 res = eivr_comm(chan, u, ser->stream, ser->stream, NULL, comma_delim_args, flags);
534
535         } else {
536                 if (pipe(child_stdin)) {
537                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno));
538                         goto exit;
539                 }
540                 if (pipe(child_stdout)) {
541                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno));
542                         goto exit;
543                 }
544                 if (pipe(child_stderr)) {
545                         ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
546                         goto exit;
547                 }
548         
549                 pid = ast_safe_fork(0);
550                 if (pid < 0) {
551                         ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno));
552                         goto exit;
553                 }
554         
555                 if (!pid) {
556                         /* child process */
557                         if (ast_opt_high_priority)
558                                 ast_set_priority(0);
559         
560                         dup2(child_stdin[0], STDIN_FILENO);
561                         dup2(child_stdout[1], STDOUT_FILENO);
562                         dup2(child_stderr[1], STDERR_FILENO);
563                         ast_close_fds_above_n(STDERR_FILENO);
564                         execv(app_args[0], app_args);
565                         fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
566                         _exit(1);
567                 } else {
568                         /* parent process */
569                         close(child_stdin[0]);
570                         child_stdin[0] = -1;
571                         close(child_stdout[1]);
572                         child_stdout[1] = -1;
573                         close(child_stderr[1]);
574                         child_stderr[1] = -1;
575
576                         stream_stdin  = ast_iostream_from_fd(&child_stdin[1]);
577                         stream_stdout = ast_iostream_from_fd(&child_stdout[0]);
578                         stream_stderr = ast_iostream_from_fd(&child_stderr[0]);
579
580                         res = eivr_comm(chan, u, stream_stdin, stream_stdout, stream_stderr, comma_delim_args, flags);
581                 }
582         }
583
584         exit:
585         if (u->gen_active) {
586                 ast_deactivate_generator(chan);
587         }
588         if (stream_stdin) {
589                 ast_iostream_close(stream_stdin);
590         }
591         if (stream_stdout) {
592                 ast_iostream_close(stream_stdout);
593         }
594         if (stream_stderr) {
595                 ast_iostream_close(stream_stderr);
596         }
597         if (child_stdin[0] > -1) {
598                 close(child_stdin[0]);
599         }
600         if (child_stdin[1] > -1) {
601                 close(child_stdin[1]);
602         }
603         if (child_stdout[0] > -1) {
604                 close(child_stdout[0]);
605         }
606         if (child_stdout[1] > -1) {
607                 close(child_stdout[1]);
608         }
609         if (child_stderr[0] > -1) {
610                 close(child_stderr[0]);
611         }
612         if (child_stderr[1] > -1) {
613                 close(child_stderr[1]);
614         }
615         if (ser) {
616                 ao2_ref(ser, -1);
617         }
618         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
619                 ast_free(entry);
620         }
621         return res;
622 }
623
624 static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, 
625                                 struct ast_iostream *eivr_events,
626                                 struct ast_iostream *eivr_commands,
627                                 struct ast_iostream *eivr_errors,
628                                 const struct ast_str *args, const struct ast_flags flags)
629 {
630         char input[1024];
631         struct playlist_entry *entry;
632         struct ast_frame *f;
633         int ms;
634         int exception;
635         int ready_fd;
636         int waitfds[2];
637         int r;
638         struct ast_channel *rchan;
639         int res = -1;
640         int hangup_info_sent = 0;
641
642         waitfds[0] = ast_iostream_get_fd(eivr_commands);
643         waitfds[1] = eivr_errors ? ast_iostream_get_fd(eivr_errors) : -1;
644
645         while (1) {
646                 if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)) {
647                         ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
648                         break;
649                 }
650                 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
651                         if (ast_test_flag(&flags, ignore_hangup)) {
652                                 ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
653                                 send_eivr_event(eivr_events, 'I', "HANGUP", chan);
654                                 hangup_info_sent = 1;
655                         } else {
656                                 ast_verb(3, "Got check_hangup\n");
657                                 send_eivr_event(eivr_events, 'H', NULL, chan);
658                                 break;
659                         }
660                 }
661  
662                 ready_fd = 0;
663                 ms = 100;
664                 errno = 0;
665                 exception = 0;
666  
667                 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors) ? 2 : 1, &exception, &ready_fd, &ms);
668  
669                 if (ast_channel_state(chan) == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
670                         AST_LIST_LOCK(&u->finishlist);
671                         while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
672                                 send_eivr_event(eivr_events, 'F', entry->filename, chan);
673                                 ast_free(entry);
674                         }
675                         AST_LIST_UNLOCK(&u->finishlist);
676                 }
677  
678                 if (ast_channel_state(chan) == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
679                         /* the channel has something */
680                         f = ast_read(chan);
681                         if (!f) {
682                                 ast_verb(3, "Returned no frame\n");
683                                 send_eivr_event(eivr_events, 'H', NULL, chan);
684                                 break;
685                         }
686                         if (f->frametype == AST_FRAME_DTMF) {
687                                 send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
688                                 if (u->option_autoclear) {
689                                         AST_LIST_LOCK(&u->playlist);
690                                         if (!u->abort_current_sound && !u->playing_silence) {
691                                                 /* send interrupted file as T data */
692                                                 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
693                                                         send_eivr_event(eivr_events, 'T', entry->filename, chan);
694                                                         ast_free(entry);
695                                                 }
696                                         }
697                                         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
698                                                 send_eivr_event(eivr_events, 'D', entry->filename, chan);
699                                                 ast_free(entry);
700                                         }
701                                         if (!u->playing_silence)
702                                                 u->abort_current_sound = 1;
703                                         AST_LIST_UNLOCK(&u->playlist);
704                                 }
705                         } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
706                                 ast_verb(3, "Got AST_CONTROL_HANGUP\n");
707                                 send_eivr_event(eivr_events, 'H', NULL, chan);
708                                 if (f->data.uint32) {
709                                         ast_channel_hangupcause_set(chan, f->data.uint32);
710                                 }
711                                 ast_frfree(f);
712                                 break;
713                         }
714                         ast_frfree(f);
715                 } else if (ready_fd == waitfds[0]) {
716                         if (exception) {
717                                 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
718                                 break;
719                         }
720   
721                         r = ast_iostream_gets(eivr_commands, input, sizeof(input));
722                         if (r <= 0) {
723                                 if (r == 0) {
724                                         ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
725                                         break;
726                                 }
727                                 continue;
728                         }
729
730                         ast_strip(input);
731                         ast_verb(4, "got command '%s'\n", input);
732
733                         if (strlen(input) < 3) {
734                                 continue;
735                         }
736
737                         if (input[0] == EIVR_CMD_PARM) {
738                                 struct ast_str *tmp = (struct ast_str *) args;
739                                 send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
740                         } else if (input[0] == EIVR_CMD_DTMF) {
741                                 ast_verb(4, "Sending DTMF: %s\n", &input[2]);
742                                 ast_eivr_senddtmf(chan, &input[2]);
743                         } else if (input[0] == EIVR_CMD_ANS) {
744                                 ast_verb(3, "Answering channel if needed and starting generator\n");
745                                 if (ast_channel_state(chan) != AST_STATE_UP) {
746                                         if (ast_test_flag(&flags, run_dead)) {
747                                                 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
748                                                 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
749                                                 continue;
750                                         }
751                                         if (ast_answer(chan)) {
752                                                 ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n");
753                                                 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
754                                                 continue;
755                                         }
756                                 }
757                                 if (!(u->gen_active)) {
758                                         if (ast_activate_generator(chan, &gen, u) < 0) {
759                                                 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
760                                                 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
761                                         } else {
762                                                 u->gen_active = 1;
763                                         }
764                                 }
765                         } else if (input[0] == EIVR_CMD_IRPT) {
766                                 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
767                                         ast_chan_log(LOG_WARNING, chan, "Queue 'I'nterrupt called on unanswered channel\n");
768                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
769                                         continue;
770                                 }
771                                 AST_LIST_LOCK(&u->playlist);
772                                 if (!u->abort_current_sound && !u->playing_silence) {
773                                         /* send interrupted file as T data */
774                                         if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
775                                                 send_eivr_event(eivr_events, 'T', entry->filename, chan);
776                                                 ast_free(entry);
777                                         }
778                                 }
779                                 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
780                                         send_eivr_event(eivr_events, 'D', entry->filename, chan);
781                                         ast_free(entry);
782                                 }
783                                 if (!u->playing_silence) {
784                                         u->abort_current_sound = 1;
785                                 }
786                                 AST_LIST_UNLOCK(&u->playlist);
787                         } else if (input[0] == EIVR_CMD_SQUE) {
788                                 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
789                                         ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n");
790                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
791                                         continue;
792                                 }
793                                 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
794                                         ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
795                                         send_eivr_event(eivr_events, 'Z', &input[2], chan);
796                                 } else {
797                                         AST_LIST_LOCK(&u->playlist);
798                                         if (!u->abort_current_sound && !u->playing_silence) {
799                                                 /* send interrupted file as T data */
800                                                 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
801                                                         send_eivr_event(eivr_events, 'T', entry->filename, chan);
802                                                         ast_free(entry);
803                                                 }
804                                         }
805                                         while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
806                                                 send_eivr_event(eivr_events, 'D', entry->filename, chan);
807                                                 ast_free(entry);
808                                         }
809                                         if (!u->playing_silence) {
810                                                 u->abort_current_sound = 1;
811                                         }
812                                         entry = make_entry(&input[2]);
813                                         if (entry) {
814                                                 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
815                                         }
816                                         AST_LIST_UNLOCK(&u->playlist);
817                                 }
818                         } else if (input[0] == EIVR_CMD_APND) {
819                                 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
820                                         ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
821                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
822                                         continue;
823                                 }
824                                 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
825                                         ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
826                                         send_eivr_event(eivr_events, 'Z', &input[2], chan);
827                                 } else {
828                                         entry = make_entry(&input[2]);
829                                         if (entry) {
830                                                 AST_LIST_LOCK(&u->playlist);
831                                                 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
832                                                 AST_LIST_UNLOCK(&u->playlist);
833                                         }
834                                 }
835                         } else if (input[0] == EIVR_CMD_GET) {
836                                 char response[2048];
837                                 ast_verb(4, "Retriving Variables from channel: %s\n", &input[2]);
838                                 ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
839                                 send_eivr_event(eivr_events, 'G', response, chan);
840                         } else if (input[0] == EIVR_CMD_SVAR) {
841                                 ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
842                                 ast_eivr_setvariable(chan, &input[2]);
843                         } else if (input[0] == EIVR_CMD_LOG) {
844                                 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
845                         } else if (input[0] == EIVR_CMD_XIT) {
846                                 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
847                                 ast_chan_log(LOG_WARNING, chan, "e'X'it command is depricated, use 'E'xit instead\n");
848                                 res = 0;
849                                 break;
850                         } else if (input[0] == EIVR_CMD_EXIT) {
851                                 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
852                                 send_eivr_event(eivr_events, 'E', NULL, chan);
853                                 res = 0;
854                                 break;
855                         } else if (input[0] == EIVR_CMD_HGUP) {
856                                 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
857                                 send_eivr_event(eivr_events, 'H', NULL, chan);
858                                 break;
859                         } else if (input[0] == EIVR_CMD_OPT) {
860                                 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
861                                         ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
862                                         send_eivr_event(eivr_events, 'Z', NULL, chan);
863                                         continue;
864                                 }
865                                 if (!strcasecmp(&input[2], "autoclear"))
866                                         u->option_autoclear = 1;
867                                 else if (!strcasecmp(&input[2], "noautoclear"))
868                                         u->option_autoclear = 0;
869                                 else
870                                         ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
871                         }
872                 } else if (ready_fd == waitfds[1]) {
873                         if (exception) {
874                                 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
875                                 break;
876                         }
877  
878                         r = ast_iostream_gets(eivr_errors, input, sizeof(input));
879                         if (r > 0) {
880                                 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
881                         } else if (r == 0) {
882                                 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
883                                 break;
884                         }
885                 } else if ((ready_fd < 0) && ms) { 
886                         if (errno == 0 || errno == EINTR)
887                                 continue;
888  
889                         ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
890                         break;
891                 }
892         }
893  
894         return res;
895 }
896
897 static int unload_module(void)
898 {
899         return ast_unregister_application(app);
900 }
901
902 static int load_module(void)
903 {
904         return ast_register_application_xml(app, app_exec);
905 }
906
907 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "External IVR Interface Application");
908