Asterisk - The Open Source Telephony Project GIT-master-773870a
Loading...
Searching...
No Matches
app_externalivr.c
Go to the documentation of this file.
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"
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 <since>
56 <version>1.2.0</version>
57 </since>
58 <synopsis>
59 Interfaces with an external IVR application.
60 </synopsis>
61 <syntax>
62 <parameter name="command|ivr://host" required="true" hasparams="true">
63 <argument name="arg1" />
64 <argument name="arg2" multiple="yes" />
65 </parameter>
66 <parameter name="options">
67 <optionlist>
68 <option name="n">
69 <para>Tells ExternalIVR() not to answer the channel.</para>
70 </option>
71 <option name="i">
72 <para>Tells ExternalIVR() not to send a hangup and exit when the
73 channel receives a hangup, instead it sends an <literal>I</literal>
74 informative message meaning that the external application MUST hang
75 up the call with an <literal>H</literal> command.</para>
76 </option>
77 <option name="d">
78 <para>Tells ExternalIVR() to run on a channel that has been hung up
79 and will not look for hangups. The external application must exit with
80 an <literal>E</literal> command.</para>
81 </option>
82 </optionlist>
83 </parameter>
84 </syntax>
85 <description>
86 <para>Either forks a process to run given command or makes a socket to connect
87 to given host and starts a generator on the channel. The generator's play list
88 is controlled by the external application, which can add and clear entries via
89 simple commands issued over its stdout. The external application will receive
90 all DTMF events received on the channel, and notification if the channel is
91 hung up. The received on the channel, and notification if the channel is hung
92 up. The application will not be forcibly terminated when the channel is hung up.
93 For more information see <filename>doc/AST.pdf</filename>.</para>
94 </description>
95 </application>
96 ***/
97
98static const char app[] = "ExternalIVR";
99
100/* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
101#define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, ast_channel_name(channel) , ## __VA_ARGS__)
102
103/* Commands */
104#define EIVR_CMD_APND 'A' /* append to prompt queue */
105#define EIVR_CMD_DTMF 'D' /* send DTMF */
106#define EIVR_CMD_EXIT 'E' /* exit */
107#define EIVR_CMD_GET 'G' /* get channel varable(s) */
108#define EIVR_CMD_HGUP 'H' /* hangup */
109#define EIVR_CMD_IRPT 'I' /* interrupt */
110#define EIVR_CMD_LOG 'L' /* log message */
111#define EIVR_CMD_OPT 'O' /* option */
112#define EIVR_CMD_PARM 'P' /* return supplied params */
113#define EIVR_CMD_SQUE 'S' /* (re)set prompt queue */
114#define EIVR_CMD_ANS 'T' /* answer channel */
115#define EIVR_CMD_SVAR 'V' /* set channel varable(s) */
116#define EIVR_CMD_XIT 'X' /* exit **depricated** */
117
118#define EXTERNALIVR_PORT 2949
119
121 noanswer = (1 << 0),
122 ignore_hangup = (1 << 1),
123 run_dead = (1 << 2),
124};
125
130});
131
136
146
147
154
155static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
156 struct ast_iostream *eivr_events,
157 struct ast_iostream *eivr_commands,
158 struct ast_iostream *eivr_errors,
159 const struct ast_str *args, const struct ast_flags flags);
160
161static void send_eivr_event(struct ast_iostream *stream, const char event, const char *data,
162 const struct ast_channel *chan)
163{
164 struct ast_str *tmp = ast_str_create(12);
165
166 ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
167 if (data) {
168 ast_str_append(&tmp, 0, ",%s", data);
169 }
170 ast_str_append(&tmp, 0, "\n");
171 ast_iostream_write(stream, ast_str_buffer(tmp), strlen(ast_str_buffer(tmp)));
172 ast_str_truncate(tmp, -1);
173
174 ast_debug(1, "sent '%s'", ast_str_buffer(tmp));
175 ast_free(tmp);
176}
177
178static void *gen_alloc(struct ast_channel *chan, void *params)
179{
180 struct ivr_localuser *u = params;
181 struct gen_state *state;
182
183 if (!(state = ast_calloc(1, sizeof(*state))))
184 return NULL;
185
186 state->u = u;
187
188 return state;
189}
190
191static void gen_closestream(struct gen_state *state)
192{
193 if (!state->stream)
194 return;
195
196 ast_closestream(state->stream);
198 state->stream = NULL;
199}
200
201static void gen_release(struct ast_channel *chan, void *data)
202{
203 struct gen_state *state = data;
204
206 ast_free(data);
207}
208
209/* caller has the playlist locked */
210static int gen_nextfile(struct gen_state *state)
211{
212 struct ivr_localuser *u = state->u;
213 char *file_to_stream;
214
215 u->abort_current_sound = 0;
216 u->playing_silence = 0;
218
219 while (!state->stream) {
220 state->current = AST_LIST_FIRST(&u->playlist);
221 if (state->current) {
222 file_to_stream = state->current->filename;
223 } else {
224 file_to_stream = "silence/10";
225 u->playing_silence = 1;
226 }
227
228 if (!(state->stream = ast_openstream_full(u->chan, file_to_stream, ast_channel_language(u->chan), 1))) {
229 ast_chan_log(LOG_WARNING, u->chan, "File '%s' could not be opened: %s\n", file_to_stream, strerror(errno));
233 if (!u->playing_silence) {
234 continue;
235 } else {
236 break;
237 }
238 }
239 }
240
241 return (!state->stream);
242}
243
244static struct ast_frame *gen_readframe(struct gen_state *state)
245{
246 struct ast_frame *f = NULL;
247 struct ivr_localuser *u = state->u;
248
249 if (u->abort_current_sound ||
255 }
256
257 if (!(state->stream && (f = ast_readframe(state->stream)))) {
258 if (state->current) {
259 /* remove finished file from playlist */
263 /* add finished file to finishlist */
265 AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
267 state->current = NULL;
268 }
269 if (!gen_nextfile(state))
270 f = ast_readframe(state->stream);
271 }
272
273 return f;
274}
275
276static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
277{
278 struct gen_state *state = data;
279 struct ast_frame *f = NULL;
280 int res = 0;
281
282 state->sample_queue += samples;
283
284 while (state->sample_queue > 0) {
285 if (!(f = gen_readframe(state)))
286 return -1;
287
288 res = ast_write(chan, f);
289 ast_frfree(f);
290 if (res < 0) {
291 ast_chan_log(LOG_WARNING, chan, "Failed to write frame: %s\n", strerror(errno));
292 return -1;
293 }
294 state->sample_queue -= f->samples;
295 }
296
297 return res;
298}
299
300static struct ast_generator gen =
301{
302 .alloc = gen_alloc,
303 .release = gen_release,
304 .generate = gen_generate,
305};
306
307static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
308{
309 /* original input data: "G,var1,var2," */
310 /* data passed as "data": "var1,var2" */
311
312 char *inbuf, *variable;
313 const char *value;
314 struct ast_str *newstring = ast_str_alloca(outbuflen);
315
316 outbuf[0] = '\0';
317
318 for (inbuf = data; ; ) {
319 variable = strsep(&inbuf, ",");
320 if (variable == NULL) {
321 int outstrlen = strlen(outbuf);
322 if (outstrlen && outbuf[outstrlen - 1] == ',') {
323 outbuf[outstrlen - 1] = 0;
324 }
325 break;
326 }
327
328 ast_channel_lock(chan);
329 if (!(value = pbx_builtin_getvar_helper(chan, variable))) {
330 value = "";
331 }
332
333 ast_str_append(&newstring, 0, "%s=%s,", variable, value);
334 ast_channel_unlock(chan);
335 ast_copy_string(outbuf, ast_str_buffer(newstring), outbuflen);
336 }
337}
338
339static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
340{
341 char *value;
342
343 char *inbuf = ast_strdupa(data), *variable;
344
345 for (variable = strsep(&inbuf, ","); variable; variable = strsep(&inbuf, ",")) {
346 ast_debug(1, "Setting up a variable: %s\n", variable);
347 /* variable contains "varname=value" */
348 value = strchr(variable, '=');
349 if (!value) {
350 value = "";
351 } else {
352 *value++ = '\0';
353 }
354 pbx_builtin_setvar_helper(chan, variable, value);
355 }
356}
357
358static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
359{
360
361 char *data;
362 int dinterval = 0, duration = 0;
364 AST_APP_ARG(digits);
365 AST_APP_ARG(dinterval);
366 AST_APP_ARG(duration);
367 );
368
369 data = ast_strdupa(vdata);
371
372 if (!ast_strlen_zero(args.dinterval)) {
373 ast_app_parse_timelen(args.dinterval, &dinterval, TIMELEN_MILLISECONDS);
374 }
375 if (!ast_strlen_zero(args.duration)) {
376 ast_app_parse_timelen(args.duration, &duration, TIMELEN_MILLISECONDS);
377 }
378 ast_verb(4, "Sending DTMF: %s %d %d\n", args.digits, dinterval <= 0 ? 250 : dinterval, duration);
379 ast_dtmf_stream(chan, NULL, args.digits, dinterval <= 0 ? 250 : dinterval, duration);
380}
381
382static struct playlist_entry *make_entry(const char *filename)
383{
384 struct playlist_entry *entry;
385
386 if (!(entry = ast_calloc(1, sizeof(*entry) + strlen(filename) + 10))) /* XXX why 10 ? */
387 return NULL;
388
389 strcpy(entry->filename, filename);
390
391 return entry;
392}
393
394static int app_exec(struct ast_channel *chan, const char *data)
395{
396 struct ast_flags flags = { 0, };
397 char *opts[0];
398 struct playlist_entry *entry;
399 int child_stdin[2] = { -1, -1 };
400 int child_stdout[2] = { -1, -1 };
401 int child_stderr[2] = { -1, -1 };
402 struct ast_iostream *stream_stdin = NULL, *stream_stdout = NULL,
403 *stream_stderr = NULL;
404 int res = -1;
405 int pid;
406
407 struct ast_tcptls_session_instance *ser = NULL;
408
409 struct ivr_localuser foo = {
411 .finishlist = AST_LIST_HEAD_INIT_VALUE,
412 .gen_active = 0,
413 .playing_silence = 1,
414 };
415 struct ivr_localuser *u = &foo;
416
417 char *buf;
418 int j;
419 char *s, **app_args, *e;
420 struct ast_str *comma_delim_args = ast_str_alloca(100);
421
422 AST_DECLARE_APP_ARGS(eivr_args,
423 AST_APP_ARG(application);
425 );
426
427#define MAX_EIVR_APPLICATION_ARGS 32
428
429 AST_DECLARE_APP_ARGS(application_args,
431 );
432
433 u->abort_current_sound = 0;
434 u->chan = chan;
435
436 if (ast_strlen_zero(data)) {
437 ast_log(LOG_ERROR, "ExternalIVR requires a command to execute\n");
438 goto exit;
439 }
440
441 buf = ast_strdupa(data);
442 AST_STANDARD_APP_ARGS(eivr_args, buf);
443
444 ast_verb(4, "ExternalIVR received application and arguments: %s\n", eivr_args.application);
445 ast_verb(4, "ExternalIVR received options: %s\n", eivr_args.options);
446
447 /* Parse out any application arguments */
448 if ((s = strchr(eivr_args.application, '('))) {
449 s[0] = ',';
450 if ((e = strrchr(s, ')'))) {
451 *e = '\0';
452 } else {
453 ast_log(LOG_ERROR, "Parse error, missing closing parenthesis\n");
454 goto exit;
455 }
456 }
457
458 AST_STANDARD_APP_ARGS(application_args, eivr_args.application);
459 app_args = application_args.argv;
460
461 /* Put the application + the arguments in a , delimited list */
462 ast_str_reset(comma_delim_args);
463 for (j = 0; j < MAX_EIVR_APPLICATION_ARGS && application_args.cmd[j]; j++) {
464 ast_str_append(&comma_delim_args, 0, "%s%s", j == 0 ? "" : ",", application_args.cmd[j]);
465 }
466
467 /* Get rid of any extraneous arguments */
468 if (eivr_args.options && (s = strchr(eivr_args.options, ','))) {
469 *s = '\0';
470 }
471
472 /* Parse the ExternalIVR() arguments */
473 ast_verb(4, "Parsing options from: [%s]\n", eivr_args.options);
474 ast_app_parse_options(app_opts, &flags, opts, eivr_args.options);
475 if (ast_test_flag(&flags, noanswer)) {
476 ast_verb(4, "noanswer is set\n");
477 }
478 if (ast_test_flag(&flags, ignore_hangup)) {
479 ast_verb(4, "ignore_hangup is set\n");
480 }
481 if (ast_test_flag(&flags, run_dead)) {
482 ast_verb(4, "run_dead is set\n");
483 }
484
485 if (!(ast_test_flag(&flags, noanswer))) {
486 ast_verb(3, "Answering channel and starting generator\n");
487 if (ast_channel_state(chan) != AST_STATE_UP) {
488 if (ast_test_flag(&flags, run_dead)) {
489 ast_chan_log(LOG_ERROR, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
490 goto exit;
491 }
492 ast_answer(chan);
493 }
494 if (ast_activate_generator(chan, &gen, u) < 0) {
495 ast_chan_log(LOG_ERROR, chan, "Failed to activate generator\n");
496 goto exit;
497 } else {
498 u->gen_active = 1;
499 }
500 }
501
502 if (!strncmp(app_args[0], "ivr://", sizeof("ivr://") - 1)) {
503 struct ast_tcptls_session_args ivr_desc = {
504 .accept_fd = -1,
505 .name = "IVR",
506 };
507 struct ast_sockaddr *addrs;
508 int num_addrs = 0, i = 0;
509 char *host = app_args[0] + sizeof("ivr://") - 1;
510
511 /* Communicate through socket to server */
512 ast_debug(1, "Parsing hostname/port for socket connect from \"%s\"\n", host);
513
514 if (!(num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC))) {
515 ast_chan_log(LOG_ERROR, chan, "Unable to locate host '%s'\n", host);
516 goto exit;
517 }
518
519 for (i = 0; i < num_addrs; i++) {
520 if (!ast_sockaddr_port(&addrs[i])) {
521 /* Default port if not specified */
523 }
524 ast_sockaddr_copy(&ivr_desc.remote_address, &addrs[i]);
525 if (!(ser = ast_tcptls_client_create(&ivr_desc)) || !(ser = ast_tcptls_client_start(ser))) {
526 continue;
527 }
528 break;
529 }
530
531 ast_free(addrs);
532
533 if (i == num_addrs) {
534 ast_chan_log(LOG_ERROR, chan, "Could not connect to any host. ExternalIVR failed.\n");
535 goto exit;
536 }
537
538 res = eivr_comm(chan, u, ser->stream, ser->stream, NULL, comma_delim_args, flags);
539
540 } else {
541 if (pipe(child_stdin)) {
542 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child input: %s\n", strerror(errno));
543 goto exit;
544 }
545 if (pipe(child_stdout)) {
546 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child output: %s\n", strerror(errno));
547 goto exit;
548 }
549 if (pipe(child_stderr)) {
550 ast_chan_log(LOG_ERROR, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
551 goto exit;
552 }
553
554 pid = ast_safe_fork(0);
555 if (pid < 0) {
556 ast_log(LOG_ERROR, "Failed to fork(): %s\n", strerror(errno));
557 goto exit;
558 }
559
560 if (!pid) {
561 /* child process */
564
565 dup2(child_stdin[0], STDIN_FILENO);
566 dup2(child_stdout[1], STDOUT_FILENO);
567 dup2(child_stderr[1], STDERR_FILENO);
568 ast_close_fds_above_n(STDERR_FILENO);
569 execv(app_args[0], app_args);
570 fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
571 _exit(1);
572 } else {
573 /* parent process */
574 close(child_stdin[0]);
575 child_stdin[0] = -1;
576 close(child_stdout[1]);
577 child_stdout[1] = -1;
578 close(child_stderr[1]);
579 child_stderr[1] = -1;
580
581 stream_stdin = ast_iostream_from_fd(&child_stdin[1]);
582 stream_stdout = ast_iostream_from_fd(&child_stdout[0]);
583 stream_stderr = ast_iostream_from_fd(&child_stderr[0]);
584
585 res = eivr_comm(chan, u, stream_stdin, stream_stdout, stream_stderr, comma_delim_args, flags);
586 }
587 }
588
589 exit:
590 if (u->gen_active) {
592 }
593 if (stream_stdin) {
594 ast_iostream_close(stream_stdin);
595 }
596 if (stream_stdout) {
597 ast_iostream_close(stream_stdout);
598 }
599 if (stream_stderr) {
600 ast_iostream_close(stream_stderr);
601 }
602 if (child_stdin[0] > -1) {
603 close(child_stdin[0]);
604 }
605 if (child_stdin[1] > -1) {
606 close(child_stdin[1]);
607 }
608 if (child_stdout[0] > -1) {
609 close(child_stdout[0]);
610 }
611 if (child_stdout[1] > -1) {
612 close(child_stdout[1]);
613 }
614 if (child_stderr[0] > -1) {
615 close(child_stderr[0]);
616 }
617 if (child_stderr[1] > -1) {
618 close(child_stderr[1]);
619 }
620 if (ser) {
621 ao2_ref(ser, -1);
622 }
623 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
624 ast_free(entry);
625 }
626 return res;
627}
628
629static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
630 struct ast_iostream *eivr_events,
631 struct ast_iostream *eivr_commands,
632 struct ast_iostream *eivr_errors,
633 const struct ast_str *args, const struct ast_flags flags)
634{
635 char input[1024];
636 struct playlist_entry *entry;
637 struct ast_frame *f;
638 int ms;
639 int exception;
640 int ready_fd;
641 int waitfds[2];
642 int r;
643 struct ast_channel *rchan;
644 int res = -1;
645 int hangup_info_sent = 0;
646
647 waitfds[0] = ast_iostream_get_fd(eivr_commands);
648 waitfds[1] = eivr_errors ? ast_iostream_get_fd(eivr_errors) : -1;
649
650 while (1) {
652 ast_chan_log(LOG_ERROR, chan, "Is a zombie\n");
653 break;
654 }
655 if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
657 ast_verb(3, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
658 send_eivr_event(eivr_events, 'I', "HANGUP", chan);
659 hangup_info_sent = 1;
660 } else {
661 ast_verb(3, "Got check_hangup\n");
662 send_eivr_event(eivr_events, 'H', NULL, chan);
663 break;
664 }
665 }
666
667 ready_fd = 0;
668 ms = 100;
669 errno = 0;
670 exception = 0;
671
672 rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors) ? 2 : 1, &exception, &ready_fd, &ms);
673
676 while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
677 send_eivr_event(eivr_events, 'F', entry->filename, chan);
678 ast_free(entry);
679 }
681 }
682
683 if (ast_channel_state(chan) == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
684 /* the channel has something */
685 f = ast_read(chan);
686 if (!f) {
687 ast_verb(3, "Returned no frame\n");
688 send_eivr_event(eivr_events, 'H', NULL, chan);
689 break;
690 }
691 if (f->frametype == AST_FRAME_DTMF) {
692 send_eivr_event(eivr_events, f->subclass.integer, NULL, chan);
693 if (u->option_autoclear) {
695 if (!u->abort_current_sound && !u->playing_silence) {
696 /* send interrupted file as T data */
697 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
698 send_eivr_event(eivr_events, 'T', entry->filename, chan);
699 ast_free(entry);
700 }
701 }
702 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
703 send_eivr_event(eivr_events, 'D', entry->filename, chan);
704 ast_free(entry);
705 }
706 if (!u->playing_silence)
707 u->abort_current_sound = 1;
709 }
710 } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP)) {
711 ast_verb(3, "Got AST_CONTROL_HANGUP\n");
712 send_eivr_event(eivr_events, 'H', NULL, chan);
713 if (f->data.uint32) {
715 }
716 ast_frfree(f);
717 break;
718 }
719 ast_frfree(f);
720 } else if (ready_fd == waitfds[0]) {
721 if (exception) {
722 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
723 break;
724 }
725
726 r = ast_iostream_gets(eivr_commands, input, sizeof(input));
727 if (r <= 0) {
728 if (r == 0) {
729 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
730 break;
731 }
732 continue;
733 }
734
735 ast_strip(input);
736 ast_verb(4, "got command '%s'\n", input);
737
738 if (strlen(input) < 3) {
739 continue;
740 }
741
742 if (input[0] == EIVR_CMD_PARM) {
743 struct ast_str *tmp = (struct ast_str *) args;
744 send_eivr_event(eivr_events, 'P', ast_str_buffer(tmp), chan);
745 } else if (input[0] == EIVR_CMD_DTMF) {
746 ast_verb(4, "Sending DTMF: %s\n", &input[2]);
747 ast_eivr_senddtmf(chan, &input[2]);
748 } else if (input[0] == EIVR_CMD_ANS) {
749 ast_verb(3, "Answering channel if needed and starting generator\n");
750 if (ast_channel_state(chan) != AST_STATE_UP) {
751 if (ast_test_flag(&flags, run_dead)) {
752 ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
753 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
754 continue;
755 }
756 if (ast_answer(chan)) {
757 ast_chan_log(LOG_WARNING, chan, "Failed to answer channel\n");
758 send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
759 continue;
760 }
761 }
762 if (!(u->gen_active)) {
763 if (ast_activate_generator(chan, &gen, u) < 0) {
764 ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
765 send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
766 } else {
767 u->gen_active = 1;
768 }
769 }
770 } else if (input[0] == EIVR_CMD_IRPT) {
771 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
772 ast_chan_log(LOG_WARNING, chan, "Queue 'I'nterrupt called on unanswered channel\n");
773 send_eivr_event(eivr_events, 'Z', NULL, chan);
774 continue;
775 }
777 if (!u->abort_current_sound && !u->playing_silence) {
778 /* send interrupted file as T data */
779 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
780 send_eivr_event(eivr_events, 'T', entry->filename, chan);
781 ast_free(entry);
782 }
783 }
784 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
785 send_eivr_event(eivr_events, 'D', entry->filename, chan);
786 ast_free(entry);
787 }
788 if (!u->playing_silence) {
789 u->abort_current_sound = 1;
790 }
792 } else if (input[0] == EIVR_CMD_SQUE) {
793 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
794 ast_chan_log(LOG_WARNING, chan, "Queue re'S'et called on unanswered channel\n");
795 send_eivr_event(eivr_events, 'Z', NULL, chan);
796 continue;
797 }
798 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
799 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
800 send_eivr_event(eivr_events, 'Z', &input[2], chan);
801 } else {
803 if (!u->abort_current_sound && !u->playing_silence) {
804 /* send interrupted file as T data */
805 if ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
806 send_eivr_event(eivr_events, 'T', entry->filename, chan);
807 ast_free(entry);
808 }
809 }
810 while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
811 send_eivr_event(eivr_events, 'D', entry->filename, chan);
812 ast_free(entry);
813 }
814 if (!u->playing_silence) {
815 u->abort_current_sound = 1;
816 }
817 entry = make_entry(&input[2]);
818 if (entry) {
819 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
820 }
822 }
823 } else if (input[0] == EIVR_CMD_APND) {
824 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
825 ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
826 send_eivr_event(eivr_events, 'Z', NULL, chan);
827 continue;
828 }
829 if (!ast_fileexists(&input[2], NULL, ast_channel_language(u->chan))) {
830 ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
831 send_eivr_event(eivr_events, 'Z', &input[2], chan);
832 } else {
833 entry = make_entry(&input[2]);
834 if (entry) {
836 AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
838 }
839 }
840 } else if (input[0] == EIVR_CMD_GET) {
841 char response[2048];
842 ast_verb(4, "Retrieving Variables from channel: %s\n", &input[2]);
843 ast_eivr_getvariable(chan, &input[2], response, sizeof(response));
844 send_eivr_event(eivr_events, 'G', response, chan);
845 } else if (input[0] == EIVR_CMD_SVAR) {
846 ast_verb(4, "Setting Variables in channel: %s\n", &input[2]);
847 ast_eivr_setvariable(chan, &input[2]);
848 } else if (input[0] == EIVR_CMD_LOG) {
849 ast_chan_log(LOG_NOTICE, chan, "Log message from EIVR: %s\n", &input[2]);
850 } else if (input[0] == EIVR_CMD_XIT) {
851 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
852 ast_chan_log(LOG_WARNING, chan, "e'X'it command is deprecated, use 'E'xit instead\n");
853 res = 0;
854 break;
855 } else if (input[0] == EIVR_CMD_EXIT) {
856 ast_chan_log(LOG_NOTICE, chan, "Exiting: %s\n", &input[2]);
857 send_eivr_event(eivr_events, 'E', NULL, chan);
858 res = 0;
859 break;
860 } else if (input[0] == EIVR_CMD_HGUP) {
861 ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
862 send_eivr_event(eivr_events, 'H', NULL, chan);
863 break;
864 } else if (input[0] == EIVR_CMD_OPT) {
865 if (ast_channel_state(chan) != AST_STATE_UP || ast_check_hangup(chan)) {
866 ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
867 send_eivr_event(eivr_events, 'Z', NULL, chan);
868 continue;
869 }
870 if (!strcasecmp(&input[2], "autoclear"))
871 u->option_autoclear = 1;
872 else if (!strcasecmp(&input[2], "noautoclear"))
873 u->option_autoclear = 0;
874 else
875 ast_chan_log(LOG_WARNING, chan, "Unknown option requested: %s\n", &input[2]);
876 }
877 } else if (ready_fd == waitfds[1]) {
878 if (exception) {
879 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
880 break;
881 }
882
883 r = ast_iostream_gets(eivr_errors, input, sizeof(input));
884 if (r > 0) {
885 ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", ast_strip(input));
886 } else if (r == 0) {
887 ast_chan_log(LOG_ERROR, chan, "Child process went away\n");
888 break;
889 }
890 } else if ((ready_fd < 0) && ms) {
891 if (errno == 0 || errno == EINTR)
892 continue;
893
894 ast_chan_log(LOG_ERROR, chan, "Wait failed (%s)\n", strerror(errno));
895 break;
896 }
897 }
898
899 return res;
900}
901
902static int unload_module(void)
903{
905}
906
907static int load_module(void)
908{
910}
911
912AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "External IVR Interface Application");
static const char app_exec[]
Definition app_exec.c:138
#define EIVR_CMD_EXIT
#define EIVR_CMD_XIT
#define EIVR_CMD_SVAR
#define EIVR_CMD_DTMF
#define EIVR_CMD_OPT
static const char app[]
#define EXTERNALIVR_PORT
static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u, struct ast_iostream *eivr_events, struct ast_iostream *eivr_commands, struct ast_iostream *eivr_errors, const struct ast_str *args, const struct ast_flags flags)
static void send_eivr_event(struct ast_iostream *stream, const char event, const char *data, const struct ast_channel *chan)
static struct playlist_entry * make_entry(const char *filename)
static struct ast_frame * gen_readframe(struct gen_state *state)
#define EIVR_CMD_ANS
#define EIVR_CMD_GET
#define ast_chan_log(level, channel, format,...)
static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
#define EIVR_CMD_APND
static const struct ast_app_option app_opts[128]
static int gen_nextfile(struct gen_state *state)
#define EIVR_CMD_IRPT
static void gen_release(struct ast_channel *chan, void *data)
static struct ast_generator gen
static int load_module(void)
static void * gen_alloc(struct ast_channel *chan, void *params)
#define EIVR_CMD_LOG
#define EIVR_CMD_HGUP
static int unload_module(void)
#define EIVR_CMD_PARM
options_flags
@ run_dead
@ ignore_hangup
@ noanswer
static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
#define EIVR_CMD_SQUE
static void gen_closestream(struct gen_state *state)
#define MAX_EIVR_APPLICATION_ARGS
char * strsep(char **str, const char *delims)
Asterisk main include file. File version handling, generic pbx functions.
int ast_set_priority(int)
We set ourselves to a high priority, that we might pre-empt everything else. If your PBX has heavy ac...
Definition asterisk.c:1854
#define ast_free(a)
Definition astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define ast_log
Definition astobj2.c:42
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition astobj2.h:459
General Asterisk PBX channel definitions.
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Definition channel.c:2948
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
struct ast_channel * ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds, int *exception, int *outfd, int *ms)
Waits for activity on a group of channels.
Definition channel.c:2985
#define ast_channel_lock(chan)
Definition channel.h:2982
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
void ast_deactivate_generator(struct ast_channel *chan)
Definition channel.c:2890
int ast_write(struct ast_channel *chan, struct ast_frame *frame)
Write a frame to a channel This function writes the given frame to the indicated channel.
Definition channel.c:5139
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition channel.c:4250
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition channel.c:445
const char * ast_channel_language(const struct ast_channel *chan)
@ AST_FLAG_ZOMBIE
Definition channel.h:1007
void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition channel.c:2803
#define ast_channel_unlock(chan)
Definition channel.h:2983
ast_channel_state
ast_channel states
@ AST_STATE_UP
char buf[BUFSIZE]
Definition eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
struct ast_filestream * ast_openstream_full(struct ast_channel *chan, const char *filename, const char *preflang, int asis)
Opens stream for use in seeking, playing.
Definition file.c:861
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition file.c:955
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition file.c:1130
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition file.c:1148
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
int ast_app_parse_timelen(const char *timestr, int *result, enum ast_timelen defunit)
Common routine to parse time lengths, with optional time unit specifier.
Definition main/app.c:3274
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
int ast_safe_fork(int stop_reaper)
Common routine to safely fork without a chance of a signal handler firing badly in the child.
Definition main/app.c:3208
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
@ TIMELEN_MILLISECONDS
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)
Send a string of DTMF digits to a channel.
Definition main/app.c:1127
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition main/app.c:3067
void ast_close_fds_above_n(int n)
Common routine for child processes, to close all fds prior to exec(2)
Definition main/app.c:3203
#define AST_FRAME_DTMF
#define ast_frfree(fr)
@ AST_FRAME_CONTROL
@ AST_CONTROL_HANGUP
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
ssize_t ast_iostream_gets(struct ast_iostream *stream, char *buffer, size_t size)
Read a LF-terminated string from an iostream.
Definition iostream.c:311
struct ast_iostream * ast_iostream_from_fd(int *fd)
Create an iostream from a file descriptor.
Definition iostream.c:611
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t count)
Write data to an iostream.
Definition iostream.c:385
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream's file descriptor.
Definition iostream.c:85
int ast_iostream_close(struct ast_iostream *stream)
Close an iostream.
Definition iostream.c:539
A set of macros to manage forward-linked lists.
#define AST_LIST_HEAD_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD.
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_LOCK(head)
Locks a list.
Definition linkedlists.h:40
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Asterisk locking-related definitions:
int errno
Asterisk module definitions.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition pbx_app.c:392
#define AST_MODULE_INFO_STANDARD_EXTENDED(keystr, desc)
Definition module.h:589
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition module.h:640
@ AST_AF_UNSPEC
Definition netsock2.h:54
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition netsock2.h:517
static void ast_sockaddr_copy(struct ast_sockaddr *dst, const struct ast_sockaddr *src)
Copies the data from one ast_sockaddr to another.
Definition netsock2.h:167
int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str, int flags, int family)
Parses a string with an IPv4 or IPv6 address and place results into an array.
Definition netsock2.c:280
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition netsock2.h:532
#define ast_opt_high_priority
Definition options.h:122
Core PBX routines and definitions.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
static struct @522 args
#define NULL
Definition resample.c:96
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
Definition strings.h:786
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition strings.h:1139
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
#define ast_str_alloca(init_len)
Definition strings.h:848
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition strings.h:693
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition strings.h:659
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition strings.h:223
Main Channel structure associated with a channel.
struct ast_flags flags
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition mod_format.h:101
Structure used to handle boolean flags.
Definition utils.h:220
unsigned int flags
Definition utils.h:221
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
enum ast_frame_type frametype
union ast_frame::@239 data
void *(* alloc)(struct ast_channel *chan, void *params)
Definition channel.h:228
Socket address structure.
Definition netsock2.h:97
Support for dynamic strings.
Definition strings.h:623
arguments for the accepting thread
Definition tcptls.h:130
struct ast_sockaddr remote_address
Definition tcptls.h:133
describes a server instance
Definition tcptls.h:151
struct ast_iostream * stream
Definition tcptls.h:162
struct ast_filestream * stream
struct playlist_entry * current
struct ivr_localuser * u
struct ast_channel * chan
struct ivr_localuser::playlist playlist
struct ivr_localuser::finishlist finishlist
struct playlist_entry::@22 list
char filename[1]
int value
Definition syslog.c:37
Generic support for tcp/tls servers in Asterisk.
struct ast_tcptls_session_instance * ast_tcptls_client_create(struct ast_tcptls_session_args *desc)
Creates a client connection's ast_tcptls_session_instance.
Definition tcptls.c:686
struct ast_tcptls_session_instance * ast_tcptls_client_start(struct ast_tcptls_session_instance *tcptls_session)
Attempt to connect and start a tcptls session.
Definition tcptls.c:681
static struct test_options options
static int inbuf(struct baseio *bio, FILE *fi)
utility used by inchar(), for base_encode()
Definition utils.c:588
Utility functions.
#define ast_test_flag(p, flag)
Definition utils.h:64