Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
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 
118  noanswer = (1 << 0),
119  ignore_hangup = (1 << 1),
120  run_dead = (1 << 2),
121 };
122 
124  AST_APP_OPTION('n', noanswer),
126  AST_APP_OPTION('d', run_dead),
127 });
128 
131  char filename[1];
132 };
133 
135  struct ast_channel *chan;
142 };
143 
144 
145 struct gen_state {
146  struct ivr_localuser *u;
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);
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);
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);
260  /* add finished file to 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;
362  AST_APP_ARG(digits);
363  AST_APP_ARG(dinterval);
364  AST_APP_ARG(duration);
365  );
366 
367  data = ast_strdupa(vdata);
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 = {
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);
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 */
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 */
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) {
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) {
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 
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  }
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;
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) {
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  }
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  }
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);
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 {
900 }
901 
902 static int load_module(void)
903 {
905 }
906 
907 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "External IVR Interface Application");
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:981
enum sip_cc_notify_state state
Definition: chan_sip.c:959
#define ast_channel_lock(chan)
Definition: channel.h:2945
Main Channel structure associated with a channel.
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:172
static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
static void * gen_alloc(struct ast_channel *chan, void *params)
struct ivr_localuser * u
#define EIVR_CMD_XIT
void *(* alloc)(struct ast_channel *chan, void *params)
Definition: channel.h:227
options_flags
struct ivr_localuser::playlist playlist
#define ast_test_flag(p, flag)
Definition: utils.h:63
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
Definition: channel.c:2960
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:171
ssize_t ast_iostream_write(struct ast_iostream *stream, const void *buffer, size_t count)
Write data to an iostream.
Definition: iostream.c:374
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
static int gen_generate(struct ast_channel *chan, void *data, int len, int samples)
static const char app[]
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define LOG_WARNING
Definition: logger.h:274
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
#define ast_chan_log(level, channel, format,...)
static int tmp()
Definition: bt_open.c:389
#define EIVR_CMD_SVAR
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:3042
#define EIVR_CMD_PARM
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4302
ast_channel_state
ast_channel states
Definition: channelstate.h:35
struct ast_iostream * ast_iostream_from_fd(int *fd)
Create an iostream from a file descriptor.
Definition: iostream.c:604
Definition: astman.c:222
struct playlist_entry * current
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "External IVR Interface Application")
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:1091
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream&#39;s file descriptor.
Definition: iostream.c:84
arguments for the accepting thread
Definition: tcptls.h:129
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:760
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
#define ast_str_alloca(init_len)
Definition: strings.h:800
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
const char * args
#define NULL
Definition: resample.c:96
static int inbuf(struct baseio *bio, FILE *fi)
utility used by inchar(), for base_encode()
static int load_module(void)
int value
Definition: syslog.c:37
Generic support for tcp/tls servers in Asterisk.
#define AST_FRAME_DTMF
static int input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1584
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
Socket address structure.
Definition: netsock2.h:97
struct ast_channel * ast_waitfor_nandfds(struct ast_channel **chan, int n, int *fds, int nfds, int *exception, int *outfd, int *ms)
Waits for activity on a group of channels.
Definition: channel.c:2997
#define ast_verb(level,...)
Definition: logger.h:463
static void send_eivr_event(struct ast_iostream *stream, const char event, const char *data, const struct ast_channel *chan)
static struct ast_frame * gen_readframe(struct gen_state *state)
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
struct ast_frame_subclass subclass
#define EIVR_CMD_IRPT
Utility functions.
char * ast_str_truncate(struct ast_str *buf, ssize_t len)
Truncates the enclosed string to the given length.
Definition: strings.h:738
#define ast_strlen_zero(foo)
Definition: strings.h:52
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:521
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
static char host[256]
Definition: muted.c:77
int ast_iostream_close(struct ast_iostream *stream)
Close an iostream.
Definition: iostream.c:528
General Asterisk PBX channel definitions.
#define EXTERNALIVR_PORT
static struct ast_generator gen
static int gen_nextfile(struct gen_state *state)
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:219
static void ast_eivr_senddtmf(struct ast_channel *chan, char *vdata)
struct ast_sockaddr remote_address
Definition: tcptls.h:132
#define ao2_ref(o, delta)
Definition: astobj2.h:464
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)
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
#define EIVR_CMD_APND
A set of macros to manage forward-linked lists.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
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:2906
Core PBX routines and definitions.
describes a server instance
Definition: tcptls.h:149
static void gen_closestream(struct gen_state *state)
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
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:1799
static int app_exec(struct ast_channel *chan, const char *data)
static int unload_module(void)
#define EIVR_CMD_EXIT
static void gen_release(struct ast_channel *chan, void *data)
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
#define EIVR_CMD_OPT
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
struct ast_filestream * stream
#define EIVR_CMD_DTMF
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:537
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition: file.c:899
#define EIVR_CMD_ANS
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int errno
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:3047
#define LOG_NOTICE
Definition: logger.h:263
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
#define ast_channel_unlock(chan)
Definition: channel.h:2946
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1068
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:5189
Structure used to handle boolean flags.
Definition: utils.h:199
struct ast_iostream * stream
Definition: tcptls.h:160
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...
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:300
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:653
void ast_deactivate_generator(struct ast_channel *chan)
Definition: channel.c:2902
struct playlist_entry::@23 list
char filename[1]
char * strsep(char **str, const char *delims)
static const struct ast_app_option app_opts[128]
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
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#define EIVR_CMD_SQUE
struct ast_tcptls_session_instance * ast_tcptls_client_create(struct ast_tcptls_session_args *desc)
Definition: tcptls.c:615
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1086
#define ast_frfree(fr)
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2814
Data structure associated with a single frame of data.
const char * ast_channel_language(const struct ast_channel *chan)
struct ivr_localuser::finishlist finishlist
Definition: search.h:40
struct ast_tcptls_session_instance * ast_tcptls_client_start(struct ast_tcptls_session_instance *tcptls_session)
attempts to connect and start tcptls session, on error the tcptls_session&#39;s ref count is decremented...
Definition: tcptls.c:585
#define EIVR_CMD_LOG
union ast_frame::@263 data
enum ast_frame_type frametype
static struct test_options options
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an 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:3113
#define AST_LIST_HEAD_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD.
Definition: linkedlists.h:233
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static struct playlist_entry * make_entry(const char *filename)
#define EIVR_CMD_HGUP
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
#define ast_opt_high_priority
Definition: options.h:110
#define EIVR_CMD_GET
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
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_APP_ARG(name)
Define an application argument.
struct ast_channel * chan