Asterisk - The Open Source Telephony Project  18.5.0
Data Structures | Macros | Enumerations | Functions | Variables
app_externalivr.c File Reference

External IVR application interface. More...

#include "asterisk.h"
#include <signal.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/linkedlists.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/tcptls.h"
#include "asterisk/astobj2.h"
Include dependency graph for app_externalivr.c:

Go to the source code of this file.

Data Structures

struct  ivr_localuser::finishlist
 
struct  gen_state
 
struct  ivr_localuser
 
struct  ivr_localuser::playlist
 
struct  playlist_entry
 

Macros

#define ast_chan_log(level, channel, format, ...)   ast_log(level, "%s: " format, ast_channel_name(channel) , ## __VA_ARGS__)
 
#define EIVR_CMD_ANS   'T' /* answer channel */
 
#define EIVR_CMD_APND   'A' /* append to prompt queue */
 
#define EIVR_CMD_DTMF   'D' /* send DTMF */
 
#define EIVR_CMD_EXIT   'E' /* exit */
 
#define EIVR_CMD_GET   'G' /* get channel varable(s) */
 
#define EIVR_CMD_HGUP   'H' /* hangup */
 
#define EIVR_CMD_IRPT   'I' /* interrupt */
 
#define EIVR_CMD_LOG   'L' /* log message */
 
#define EIVR_CMD_OPT   'O' /* option */
 
#define EIVR_CMD_PARM   'P' /* return supplied params */
 
#define EIVR_CMD_SQUE   'S' /* (re)set prompt queue */
 
#define EIVR_CMD_SVAR   'V' /* set channel varable(s) */
 
#define EIVR_CMD_XIT   'X' /* exit **depricated** */
 
#define EXTERNALIVR_PORT   2949
 

Enumerations

enum  options_flags { noanswer = (1 << 0), ignore_hangup = (1 << 1), run_dead = (1 << 2) }
 

Functions

static int app_exec (struct ast_channel *chan, const char *data)
 
static void ast_eivr_getvariable (struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
 
static void ast_eivr_senddtmf (struct ast_channel *chan, char *vdata)
 
static void ast_eivr_setvariable (struct ast_channel *chan, char *data)
 
 AST_MODULE_INFO_STANDARD_EXTENDED (ASTERISK_GPL_KEY, "External IVR Interface Application")
 
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 * gen_alloc (struct ast_channel *chan, void *params)
 
static void gen_closestream (struct gen_state *state)
 
static int gen_generate (struct ast_channel *chan, void *data, int len, int samples)
 
static int gen_nextfile (struct gen_state *state)
 
static struct ast_framegen_readframe (struct gen_state *state)
 
static void gen_release (struct ast_channel *chan, void *data)
 
static int load_module (void)
 
static struct playlist_entrymake_entry (const char *filename)
 
static void send_eivr_event (struct ast_iostream *stream, const char event, const char *data, const struct ast_channel *chan)
 
static int unload_module (void)
 

Variables

static const char app [] = "ExternalIVR"
 
static const struct ast_app_option app_opts [128] = { [ 'n' ] = { .flag = noanswer }, [ 'i' ] = { .flag = ignore_hangup }, [ 'd' ] = { .flag = run_dead }, }
 
static struct ast_generator gen
 

Detailed Description

External IVR application interface.

Author
Kevin P. Fleming kpfle.nosp@m.ming.nosp@m.@digi.nosp@m.um.c.nosp@m.om
Note
Portions taken from the file-based music-on-hold work created by Anthony Minessale II in res_musiconhold.c

Definition in file app_externalivr.c.

Macro Definition Documentation

◆ ast_chan_log

#define ast_chan_log (   level,
  channel,
  format,
  ... 
)    ast_log(level, "%s: " format, ast_channel_name(channel) , ## __VA_ARGS__)

Definition at line 98 of file app_externalivr.c.

Referenced by app_exec(), eivr_comm(), gen_generate(), and gen_nextfile().

◆ EIVR_CMD_ANS

#define EIVR_CMD_ANS   'T' /* answer channel */

Definition at line 111 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_APND

#define EIVR_CMD_APND   'A' /* append to prompt queue */

Definition at line 101 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_DTMF

#define EIVR_CMD_DTMF   'D' /* send DTMF */

Definition at line 102 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_EXIT

#define EIVR_CMD_EXIT   'E' /* exit */

Definition at line 103 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_GET

#define EIVR_CMD_GET   'G' /* get channel varable(s) */

Definition at line 104 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_HGUP

#define EIVR_CMD_HGUP   'H' /* hangup */

Definition at line 105 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_IRPT

#define EIVR_CMD_IRPT   'I' /* interrupt */

Definition at line 106 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_LOG

#define EIVR_CMD_LOG   'L' /* log message */

Definition at line 107 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_OPT

#define EIVR_CMD_OPT   'O' /* option */

Definition at line 108 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_PARM

#define EIVR_CMD_PARM   'P' /* return supplied params */

Definition at line 109 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_SQUE

#define EIVR_CMD_SQUE   'S' /* (re)set prompt queue */

Definition at line 110 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_SVAR

#define EIVR_CMD_SVAR   'V' /* set channel varable(s) */

Definition at line 112 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EIVR_CMD_XIT

#define EIVR_CMD_XIT   'X' /* exit **depricated** */

Definition at line 113 of file app_externalivr.c.

Referenced by eivr_comm().

◆ EXTERNALIVR_PORT

#define EXTERNALIVR_PORT   2949

Definition at line 115 of file app_externalivr.c.

Referenced by app_exec().

Enumeration Type Documentation

◆ options_flags

Enumerator
noanswer 
ignore_hangup 
run_dead 

Definition at line 117 of file app_externalivr.c.

117  {
118  noanswer = (1 << 0),
119  ignore_hangup = (1 << 1),
120  run_dead = (1 << 2),
121 };

Function Documentation

◆ app_exec()

static int app_exec ( struct ast_channel chan,
const char *  data 
)
static

Definition at line 392 of file app_externalivr.c.

References ivr_localuser::abort_current_sound, ast_tcptls_session_args::accept_fd, ao2_ref, app_opts, ast_activate_generator(), AST_AF_UNSPEC, ast_answer(), AST_APP_ARG, ast_app_parse_options(), ast_chan_log, ast_close_fds_above_n(), ast_deactivate_generator(), ast_debug, AST_DECLARE_APP_ARGS, ast_free, ast_iostream_close(), ast_iostream_from_fd(), AST_LIST_HEAD_INIT_VALUE, AST_LIST_REMOVE_HEAD, ast_log, ast_opt_high_priority, ast_safe_fork(), ast_set_priority(), ast_sockaddr_copy(), ast_sockaddr_port, ast_sockaddr_resolve(), ast_sockaddr_set_port, AST_STANDARD_APP_ARGS, AST_STATE_UP, ast_str_alloca, ast_str_append(), ast_str_reset(), ast_strdupa, ast_strlen_zero, ast_tcptls_client_create(), ast_tcptls_client_start(), ast_test_flag, ast_verb, buf, ivr_localuser::chan, eivr_comm(), errno, EXTERNALIVR_PORT, ivr_localuser::gen_active, host, ignore_hangup, playlist_entry::list, LOG_ERROR, noanswer, NULL, options, ivr_localuser::playlist, ast_tcptls_session_args::remote_address, run_dead, and ast_tcptls_session_instance::stream.

Referenced by load_module().

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 }
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
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
#define ast_chan_log(level, channel, format,...)
unsigned int flags
Definition: utils.h:200
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
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
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
arguments for the accepting thread
Definition: tcptls.h:129
#define ast_str_alloca(init_len)
Definition: strings.h:800
#define NULL
Definition: resample.c:96
Socket address structure.
Definition: netsock2.h:97
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_strlen_zero(foo)
Definition: strings.h:52
#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
#define EXTERNALIVR_PORT
static struct ast_generator gen
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 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
describes a server instance
Definition: tcptls.h:149
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
#define LOG_ERROR
Definition: logger.h:285
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ast_sockaddr_set_port(addr, port)
Sets the port number of a socket address.
Definition: netsock2.h:537
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 ast_free(a)
Definition: astmm.h:182
Structure used to handle boolean flags.
Definition: utils.h:199
struct ast_iostream * stream
Definition: tcptls.h:160
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
static const struct ast_app_option app_opts[128]
struct ast_tcptls_session_instance * ast_tcptls_client_create(struct ast_tcptls_session_args *desc)
Definition: tcptls.c:615
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2814
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
static struct test_options options
#define AST_LIST_HEAD_INIT_VALUE
Defines initial values for a declaration of AST_LIST_HEAD.
Definition: linkedlists.h:233
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define ast_opt_high_priority
Definition: options.h:110
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

◆ ast_eivr_getvariable()

static void ast_eivr_getvariable ( struct ast_channel chan,
char *  data,
char *  outbuf,
int  outbuflen 
)
static

Definition at line 304 of file app_externalivr.c.

References ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_str_alloca, ast_str_append(), ast_str_buffer(), inbuf(), NULL, pbx_builtin_getvar_helper(), strsep(), and value.

Referenced by eivr_comm().

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 }
#define ast_channel_lock(chan)
Definition: channel.h:2945
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
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
#define ast_str_alloca(init_len)
Definition: strings.h:800
#define NULL
Definition: resample.c:96
static int inbuf(struct baseio *bio, FILE *fi)
utility used by inchar(), for base_encode()
int value
Definition: syslog.c:37
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ast_channel_unlock(chan)
Definition: channel.h:2946
char * strsep(char **str, const char *delims)
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401

◆ ast_eivr_senddtmf()

static void ast_eivr_senddtmf ( struct ast_channel chan,
char *  vdata 
)
static

Definition at line 356 of file app_externalivr.c.

References args, AST_APP_ARG, ast_app_parse_timelen(), AST_DECLARE_APP_ARGS, ast_dtmf_stream(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero, ast_verb, NULL, and TIMELEN_MILLISECONDS.

Referenced by eivr_comm().

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 }
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
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
const char * args
#define NULL
Definition: resample.c:96
#define ast_verb(level,...)
Definition: logger.h:463
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
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_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define AST_APP_ARG(name)
Define an application argument.

◆ ast_eivr_setvariable()

static void ast_eivr_setvariable ( struct ast_channel chan,
char *  data 
)
static

Definition at line 337 of file app_externalivr.c.

References ast_debug, ast_strdupa, inbuf(), pbx_builtin_setvar_helper(), strsep(), and value.

Referenced by eivr_comm().

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 }
static int inbuf(struct baseio *bio, FILE *fi)
utility used by inchar(), for base_encode()
int value
Definition: syslog.c:37
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
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...
char * strsep(char **str, const char *delims)

◆ AST_MODULE_INFO_STANDARD_EXTENDED()

AST_MODULE_INFO_STANDARD_EXTENDED ( ASTERISK_GPL_KEY  ,
"External IVR Interface Application"   
)

Referenced by load_module().

◆ eivr_comm()

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

Definition at line 624 of file app_externalivr.c.

References ivr_localuser::abort_current_sound, ast_activate_generator(), ast_answer(), ast_chan_log, ast_channel_flags(), ast_channel_hangupcause_set(), ast_channel_language(), ast_check_hangup(), AST_CONTROL_HANGUP, ast_eivr_getvariable(), ast_eivr_senddtmf(), ast_eivr_setvariable(), ast_fileexists(), AST_FLAG_ZOMBIE, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_free, ast_frfree, ast_iostream_get_fd(), ast_iostream_gets(), AST_LIST_EMPTY, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_read(), AST_STATE_UP, ast_str_buffer(), ast_strip(), ast_test_flag, ast_verb, ast_waitfor_nandfds(), ivr_localuser::chan, ast_frame::data, EIVR_CMD_ANS, EIVR_CMD_APND, EIVR_CMD_DTMF, EIVR_CMD_EXIT, EIVR_CMD_GET, EIVR_CMD_HGUP, EIVR_CMD_IRPT, EIVR_CMD_LOG, EIVR_CMD_OPT, EIVR_CMD_PARM, EIVR_CMD_SQUE, EIVR_CMD_SVAR, EIVR_CMD_XIT, errno, playlist_entry::filename, ivr_localuser::finishlist, ast_frame::frametype, ivr_localuser::gen_active, ignore_hangup, input(), ast_frame_subclass::integer, playlist_entry::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, make_entry(), NULL, ivr_localuser::option_autoclear, ivr_localuser::playing_silence, ivr_localuser::playlist, run_dead, send_eivr_event(), ast_frame::subclass, tmp(), and ast_frame::uint32.

Referenced by app_exec().

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 }
Main Channel structure associated with a channel.
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *outbuf, int outbuflen)
#define EIVR_CMD_XIT
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
void ast_channel_hangupcause_set(struct ast_channel *chan, int value)
#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
#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
int ast_iostream_get_fd(struct ast_iostream *stream)
Get an iostream&#39;s file descriptor.
Definition: iostream.c:84
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
#define NULL
Definition: resample.c:96
#define AST_FRAME_DTMF
static int input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1584
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)
struct ast_frame_subclass subclass
#define EIVR_CMD_IRPT
static struct ast_generator gen
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)
#define EIVR_CMD_APND
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
#define EIVR_CMD_EXIT
#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
#define EIVR_CMD_DTMF
#define EIVR_CMD_ANS
int errno
#define LOG_NOTICE
Definition: logger.h:263
#define ast_free(a)
Definition: astmm.h:182
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
char filename[1]
#define EIVR_CMD_SQUE
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
#define EIVR_CMD_LOG
union ast_frame::@263 data
enum ast_frame_type frametype
struct ast_flags * ast_channel_flags(struct ast_channel *chan)
static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
static struct playlist_entry * make_entry(const char *filename)
#define EIVR_CMD_HGUP
#define EIVR_CMD_GET
struct ast_channel * chan

◆ gen_alloc()

static void* gen_alloc ( struct ast_channel chan,
void *  params 
)
static

Definition at line 175 of file app_externalivr.c.

References ast_calloc, NULL, state, and gen_state::u.

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 }
enum sip_cc_notify_state state
Definition: chan_sip.c:959
struct ivr_localuser * u
#define NULL
Definition: resample.c:96
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204

◆ gen_closestream()

static void gen_closestream ( struct gen_state state)
static

Definition at line 188 of file app_externalivr.c.

References ast_channel_stream_set(), ast_closestream(), ivr_localuser::chan, NULL, gen_state::stream, and gen_state::u.

Referenced by gen_nextfile(), gen_readframe(), and gen_release().

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 }
struct ivr_localuser * u
#define NULL
Definition: resample.c:96
void ast_channel_stream_set(struct ast_channel *chan, struct ast_filestream *value)
struct ast_filestream * stream
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1068
struct ast_channel * chan

◆ gen_generate()

static int gen_generate ( struct ast_channel chan,
void *  data,
int  len,
int  samples 
)
static

Definition at line 273 of file app_externalivr.c.

References ast_chan_log, ast_frfree, ast_write(), errno, gen_readframe(), LOG_WARNING, NULL, gen_state::sample_queue, and ast_frame::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 }
#define LOG_WARNING
Definition: logger.h:274
#define ast_chan_log(level, channel, format,...)
#define NULL
Definition: resample.c:96
static struct ast_frame * gen_readframe(struct gen_state *state)
int errno
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
#define ast_frfree(fr)
Data structure associated with a single frame of data.

◆ gen_nextfile()

static int gen_nextfile ( struct gen_state state)
static

Definition at line 207 of file app_externalivr.c.

References ivr_localuser::abort_current_sound, ast_chan_log, ast_channel_language(), AST_LIST_FIRST, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_openstream_full(), ivr_localuser::chan, gen_state::current, errno, playlist_entry::filename, gen_closestream(), playlist_entry::list, LOG_WARNING, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.

Referenced by gen_readframe().

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);
230  if (!u->playing_silence) {
231  continue;
232  } else {
233  break;
234  }
235  }
236  }
237 
238  return (!state->stream);
239 }
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
struct ivr_localuser * u
struct ivr_localuser::playlist playlist
#define LOG_WARNING
Definition: logger.h:274
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
#define ast_chan_log(level, channel, format,...)
struct playlist_entry * current
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_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
static void gen_closestream(struct gen_state *state)
struct ast_filestream * stream
int errno
char filename[1]
const char * ast_channel_language(const struct ast_channel *chan)
struct ast_channel * chan

◆ gen_readframe()

static struct ast_frame* gen_readframe ( struct gen_state state)
static

Definition at line 241 of file app_externalivr.c.

References ivr_localuser::abort_current_sound, AST_LIST_FIRST, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_readframe(), gen_state::current, ivr_localuser::finishlist, gen_closestream(), gen_nextfile(), playlist_entry::list, NULL, ivr_localuser::playing_silence, ivr_localuser::playlist, gen_state::stream, and gen_state::u.

Referenced by gen_generate().

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);
258  AST_LIST_REMOVE_HEAD(&u->playlist, list);
260  /* add finished file to finishlist */
262  AST_LIST_INSERT_TAIL(&u->finishlist, state->current, list);
264  state->current = NULL;
265  }
266  if (!gen_nextfile(state))
267  f = ast_readframe(state->stream);
268  }
269 
270  return f;
271 }
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:420
struct ivr_localuser * u
struct ivr_localuser::playlist playlist
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
struct playlist_entry * current
#define NULL
Definition: resample.c:96
static int gen_nextfile(struct gen_state *state)
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
static void gen_closestream(struct gen_state *state)
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
struct ast_filestream * stream
struct ast_frame * ast_readframe(struct ast_filestream *s)
Read a frame from a filestream.
Definition: file.c:899
Data structure associated with a single frame of data.
struct ivr_localuser::finishlist finishlist

◆ gen_release()

static void gen_release ( struct ast_channel chan,
void *  data 
)
static

Definition at line 198 of file app_externalivr.c.

References ast_free, and gen_closestream().

199 {
200  struct gen_state *state = data;
201 
202  gen_closestream(state);
203  ast_free(data);
204 }
static void gen_closestream(struct gen_state *state)
#define ast_free(a)
Definition: astmm.h:182

◆ load_module()

static int load_module ( void  )
static

Definition at line 902 of file app_externalivr.c.

References app, app_exec(), AST_MODULE_INFO_STANDARD_EXTENDED(), ast_register_application_xml, and ASTERISK_GPL_KEY.

903 {
905 }
static const char app[]
static int app_exec(struct ast_channel *chan, const char *data)
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626

◆ make_entry()

static struct playlist_entry* make_entry ( const char *  filename)
static

Definition at line 380 of file app_externalivr.c.

References ast_calloc, playlist_entry::filename, and NULL.

Referenced by eivr_comm().

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 }
#define NULL
Definition: resample.c:96
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
char filename[1]
Definition: search.h:40

◆ send_eivr_event()

static void send_eivr_event ( struct ast_iostream stream,
const char  event,
const char *  data,
const struct ast_channel chan 
)
static

Definition at line 158 of file app_externalivr.c.

References ast_debug, ast_free, ast_iostream_write(), ast_str_append(), ast_str_buffer(), ast_str_create, ast_str_truncate(), NULL, and tmp().

Referenced by eivr_comm().

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 }
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 * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
static int tmp()
Definition: bt_open.c:389
Definition: astman.c:222
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
#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:738
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ast_free(a)
Definition: astmm.h:182
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 897 of file app_externalivr.c.

References app, and ast_unregister_application().

898 {
900 }
static const char app[]
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392

Variable Documentation

◆ app

const char app[] = "ExternalIVR"
static

Definition at line 95 of file app_externalivr.c.

Referenced by load_module(), and unload_module().

◆ app_opts

const struct ast_app_option app_opts[128] = { [ 'n' ] = { .flag = noanswer }, [ 'i' ] = { .flag = ignore_hangup }, [ 'd' ] = { .flag = run_dead }, }
static

Definition at line 127 of file app_externalivr.c.

Referenced by app_exec().

◆ gen

struct ast_generator gen
static