mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-18 18:58:22 +00:00
Enhance ExternalIVR with new options and commands.
(closes issue #12705) Reported by: ctooley Patches: new_externalivr_argument_format-v2.diff uploaded by ctooley (license 136) new_externalivr_documentation.diff uploaded by ctooley (license 136) and a few additional fixes by me git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@117725 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
3
CHANGES
3
CHANGES
@@ -72,6 +72,9 @@ Application Changes
|
||||
words, if using the 'd' option, it is not possible to enter a number to append to
|
||||
the first argument to Chanspy(). Pressing 4 will change to spy mode, pressing 5 will
|
||||
change to whisper mode, and pressing 6 will change to barge mode.
|
||||
* ExternalIVR now takes several options that affect the way it performs, as
|
||||
well as having several new commands. Please see doc/externalivr.txt for the
|
||||
complete documentation.
|
||||
|
||||
SIP Changes
|
||||
-----------
|
||||
|
@@ -50,20 +50,38 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
static const char *app = "ExternalIVR";
|
||||
|
||||
static const char *synopsis = "Interfaces with an external IVR application";
|
||||
|
||||
static const char *descrip =
|
||||
" ExternalIVR(command|ivr://ivrhost[,arg[,arg...]]): Either forks a process\n"
|
||||
" ExternalIVR(command|ivr://ivrhosti([,arg[,arg...]])[,options]): Either forks a process\n"
|
||||
"to run given command or makes a socket to connect to given host and starts\n"
|
||||
"a generator on the channel. The generator's play list is controlled by the\n"
|
||||
"external application, which can add and clear entries via simple commands\n"
|
||||
"issued over its stdout. The external application will receive all DTMF events\n"
|
||||
"received on the channel, and notification if the channel is hung up. The\n"
|
||||
"application will not be forcibly terminated when the channel is hung up.\n"
|
||||
"See doc/externalivr.txt for a protocol specification.\n";
|
||||
"See doc/externalivr.txt for a protocol specification.\n"
|
||||
"The 'n' option tells ExternalIVR() not to answer the channel. \n"
|
||||
"The 'i' option tells ExternalIVR() not to send a hangup and exit when the\n"
|
||||
" channel receives a hangup, instead it sends an 'I' informative message\n"
|
||||
" meaning that the external application MUST hang up the call with an H command\n"
|
||||
"The 'd' option tells ExternalIVR() to run on a channel that has been hung up\n"
|
||||
" and will not look for hangups. The external application must exit with\n"
|
||||
" an 'E' command.\n";
|
||||
|
||||
/* XXX the parser in gcc 2.95 gets confused if you don't put a space between 'name' and the comma */
|
||||
#define ast_chan_log(level, channel, format, ...) ast_log(level, "%s: " format, channel->name , ## __VA_ARGS__)
|
||||
|
||||
enum {
|
||||
noanswer = (1 << 0),
|
||||
ignore_hangup = (1 << 1),
|
||||
run_dead = (1 << 2),
|
||||
} options_flags;
|
||||
|
||||
AST_APP_OPTIONS(app_opts, {
|
||||
AST_APP_OPTION('n', noanswer),
|
||||
AST_APP_OPTION('i', ignore_hangup),
|
||||
AST_APP_OPTION('d', run_dead),
|
||||
});
|
||||
|
||||
struct playlist_entry {
|
||||
AST_LIST_ENTRY(playlist_entry) list;
|
||||
char filename[1];
|
||||
@@ -76,6 +94,7 @@ struct ivr_localuser {
|
||||
int abort_current_sound;
|
||||
int playing_silence;
|
||||
int option_autoclear;
|
||||
int gen_active;
|
||||
};
|
||||
|
||||
|
||||
@@ -88,24 +107,22 @@ struct gen_state {
|
||||
|
||||
static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||
int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd,
|
||||
const char *args);
|
||||
const struct ast_str *args, const struct ast_flags flags);
|
||||
|
||||
int eivr_connect_socket(struct ast_channel *chan, const char *host, int port);
|
||||
|
||||
static void send_eivr_event(FILE *handle, const char event, const char *data,
|
||||
const struct ast_channel *chan)
|
||||
{
|
||||
char tmp[256];
|
||||
struct ast_str *tmp = ast_str_create(12);
|
||||
|
||||
if (!data) {
|
||||
snprintf(tmp, sizeof(tmp), "%c,%10d", event, (int)time(NULL));
|
||||
} else {
|
||||
snprintf(tmp, sizeof(tmp), "%c,%10d,%s", event, (int)time(NULL), data);
|
||||
ast_str_append(&tmp, 0, "%c,%10d", event, (int)time(NULL));
|
||||
if (data) {
|
||||
ast_str_append(&tmp, 0, "%s", data);
|
||||
}
|
||||
|
||||
fprintf(handle, "%s\n", tmp);
|
||||
if (option_debug)
|
||||
ast_chan_log(LOG_DEBUG, chan, "sent '%s'\n", tmp);
|
||||
fprintf(handle, "%s\n", tmp->str);
|
||||
ast_debug(1, "sent '%s'\n", tmp->str);
|
||||
}
|
||||
|
||||
static void *gen_alloc(struct ast_channel *chan, void *params)
|
||||
@@ -245,7 +262,7 @@ static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *out
|
||||
variable = strsep(&inbuf, ",");
|
||||
if (variable == NULL) {
|
||||
int outstrlen = strlen(outbuf);
|
||||
if(outstrlen && outbuf[outstrlen - 1] == ',') {
|
||||
if (outstrlen && outbuf[outstrlen - 1] == ',') {
|
||||
outbuf[outstrlen - 1] = 0;
|
||||
}
|
||||
break;
|
||||
@@ -260,7 +277,7 @@ static void ast_eivr_getvariable(struct ast_channel *chan, char *data, char *out
|
||||
ast_channel_unlock(chan);
|
||||
ast_copy_string(outbuf, newstring->str, outbuflen);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
|
||||
{
|
||||
@@ -273,21 +290,22 @@ static void ast_eivr_setvariable(struct ast_channel *chan, char *data)
|
||||
|
||||
for (j = 1, inbuf = data; ; j++, inbuf = NULL) {
|
||||
variable = strsep(&inbuf, ",");
|
||||
ast_chan_log(LOG_DEBUG, chan, "Setting up a variable: %s\n", variable);
|
||||
if(variable) {
|
||||
ast_debug(1, "Setting up a variable: %s\n", variable);
|
||||
if (variable) {
|
||||
/* variable contains "varname=value" */
|
||||
ast_copy_string(buf, variable, sizeof(buf));
|
||||
value = strchr(buf, '=');
|
||||
if(!value)
|
||||
value="";
|
||||
else
|
||||
if (!value) {
|
||||
value = "";
|
||||
} else {
|
||||
*value++ = '\0';
|
||||
}
|
||||
pbx_builtin_setvar_helper(chan, buf, value);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static struct playlist_entry *make_entry(const char *filename)
|
||||
{
|
||||
@@ -303,14 +321,14 @@ static struct playlist_entry *make_entry(const char *filename)
|
||||
|
||||
static int app_exec(struct ast_channel *chan, void *data)
|
||||
{
|
||||
struct ast_flags flags;
|
||||
char *opts[0];
|
||||
struct playlist_entry *entry;
|
||||
int child_stdin[2] = { 0,0 };
|
||||
int child_stdout[2] = { 0,0 };
|
||||
int child_stderr[2] = { 0,0 };
|
||||
int child_stdin[2] = { 0, 0 };
|
||||
int child_stdout[2] = { 0, 0 };
|
||||
int child_stderr[2] = { 0, 0 };
|
||||
int res = -1;
|
||||
int gen_active = 0;
|
||||
int pid;
|
||||
char *buf, *pipe_delim_argbuf, *pdargbuf_ptr;
|
||||
|
||||
char hostname[1024];
|
||||
char *port_str = NULL;
|
||||
@@ -320,29 +338,87 @@ static int app_exec(struct ast_channel *chan, void *data)
|
||||
struct ivr_localuser foo = {
|
||||
.playlist = AST_LIST_HEAD_INIT_VALUE,
|
||||
.finishlist = AST_LIST_HEAD_INIT_VALUE,
|
||||
.gen_active = 0,
|
||||
};
|
||||
struct ivr_localuser *u = &foo;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
|
||||
char *buf;
|
||||
int j;
|
||||
char *s, **app_args, *e;
|
||||
struct ast_str *pipe_delim_args = ast_str_create(100);
|
||||
|
||||
AST_DECLARE_APP_ARGS(eivr_args,
|
||||
AST_APP_ARG(cmd)[32];
|
||||
);
|
||||
AST_DECLARE_APP_ARGS(application_args,
|
||||
AST_APP_ARG(cmd)[32];
|
||||
);
|
||||
|
||||
u->abort_current_sound = 0;
|
||||
u->chan = chan;
|
||||
|
||||
buf = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(eivr_args, buf);
|
||||
|
||||
if ((s = strchr(eivr_args.cmd[0], '('))) {
|
||||
s[0] = ',';
|
||||
if (( e = strrchr(s, ')')) ) {
|
||||
*e = '\0';
|
||||
} else {
|
||||
ast_log(LOG_ERROR, "Parse error, no closing paren?\n");
|
||||
}
|
||||
AST_STANDARD_APP_ARGS(application_args, eivr_args.cmd[0]);
|
||||
app_args = application_args.argv;
|
||||
|
||||
/* Put the application + the arguments in a | delimited list */
|
||||
ast_str_reset(pipe_delim_args);
|
||||
for (j = 0; application_args.cmd[j] != NULL; j++) {
|
||||
ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", application_args.cmd[j]);
|
||||
}
|
||||
|
||||
/* Parse the ExternalIVR() arguments */
|
||||
if (option_debug)
|
||||
ast_debug(1, "Parsing options from: [%s]\n", eivr_args.cmd[1]);
|
||||
ast_app_parse_options(app_opts, &flags, opts, eivr_args.cmd[1]);
|
||||
if (option_debug) {
|
||||
if (ast_test_flag(&flags, noanswer))
|
||||
ast_debug(1, "noanswer is set\n");
|
||||
if (ast_test_flag(&flags, ignore_hangup))
|
||||
ast_debug(1, "ignore_hangup is set\n");
|
||||
if (ast_test_flag(&flags, run_dead))
|
||||
ast_debug(1, "run_dead is set\n");
|
||||
}
|
||||
|
||||
} else {
|
||||
app_args = eivr_args.argv;
|
||||
for (j = 0; eivr_args.cmd[j] != NULL; j++) {
|
||||
ast_str_append(&pipe_delim_args, 0, "%s%s", j == 0 ? "" : "|", eivr_args.cmd[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = ast_strdupa(data);
|
||||
AST_STANDARD_APP_ARGS(args, buf);
|
||||
if (!(ast_test_flag(&flags, noanswer))) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Answering channel and starting generator\n");
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
if (ast_test_flag(&flags, run_dead)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
|
||||
goto exit;
|
||||
}
|
||||
ast_answer(chan);
|
||||
}
|
||||
if (ast_activate_generator(chan, &gen, u) < 0) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
|
||||
goto exit;
|
||||
} else {
|
||||
u->gen_active = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* copy args and replace commas with pipes */
|
||||
pipe_delim_argbuf = ast_strdupa(data);
|
||||
while((pdargbuf_ptr = strchr(pipe_delim_argbuf, ',')) != NULL)
|
||||
pdargbuf_ptr[0] = '|';
|
||||
|
||||
if(!strncmp(args.cmd[0], "ivr://", 6)) {
|
||||
if (!strncmp(app_args[0], "ivr://", 6)) {
|
||||
struct server_args ivr_desc = {
|
||||
.accept_fd = -1,
|
||||
.name = "IVR",
|
||||
@@ -350,25 +426,16 @@ static int app_exec(struct ast_channel *chan, void *data)
|
||||
struct ast_hostent hp;
|
||||
|
||||
/*communicate through socket to server*/
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
ast_answer(chan);
|
||||
}
|
||||
if (ast_activate_generator(chan, &gen, u) < 0) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
|
||||
goto exit;
|
||||
} else {
|
||||
gen_active = 1;
|
||||
}
|
||||
|
||||
ast_chan_log(LOG_DEBUG, chan, "Parsing hostname:port for socket connect from \"%s\"\n", args.cmd[0]);
|
||||
strncpy(hostname, args.cmd[0] + 6, sizeof(hostname));
|
||||
if((port_str = strchr(hostname, ':')) != NULL) {
|
||||
ast_debug(1, "Parsing hostname:port for socket connect from \"%s\"\n", app_args[0]);
|
||||
ast_copy_string(hostname, app_args[0] + 6, sizeof(hostname));
|
||||
if ((port_str = strchr(hostname, ':')) != NULL) {
|
||||
port_str[0] = 0;
|
||||
port_str += 1;
|
||||
port = atoi(port_str);
|
||||
}
|
||||
if(!port)
|
||||
port = 2949; /*default port, if one is not provided*/
|
||||
if (!port) {
|
||||
port = 2949; /* default port, if one is not provided */
|
||||
}
|
||||
|
||||
ast_gethostbyname(hostname, &hp);
|
||||
ivr_desc.sin.sin_family = AF_INET;
|
||||
@@ -378,8 +445,9 @@ static int app_exec(struct ast_channel *chan, void *data)
|
||||
|
||||
if (!ser) {
|
||||
goto exit;
|
||||
}
|
||||
res = eivr_comm(chan, u, ser->fd, ser->fd, -1, pipe_delim_argbuf);
|
||||
}
|
||||
res = eivr_comm(chan, u, ser->fd, ser->fd, -1, pipe_delim_args, flags);
|
||||
|
||||
} else {
|
||||
|
||||
if (pipe(child_stdin)) {
|
||||
@@ -394,15 +462,6 @@ static int app_exec(struct ast_channel *chan, void *data)
|
||||
ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
|
||||
goto exit;
|
||||
}
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
ast_answer(chan);
|
||||
}
|
||||
if (ast_activate_generator(chan, &gen, u) < 0) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
|
||||
goto exit;
|
||||
} else {
|
||||
gen_active = 1;
|
||||
}
|
||||
|
||||
pid = ast_safe_fork(0);
|
||||
if (pid < 0) {
|
||||
@@ -419,24 +478,23 @@ static int app_exec(struct ast_channel *chan, void *data)
|
||||
dup2(child_stdout[1], STDOUT_FILENO);
|
||||
dup2(child_stderr[1], STDERR_FILENO);
|
||||
ast_close_fds_above_n(STDERR_FILENO);
|
||||
execv(args.cmd[0], args.cmd);
|
||||
fprintf(stderr, "Failed to execute '%s': %s\n", args.cmd[0], strerror(errno));
|
||||
execv(app_args[0], app_args);
|
||||
fprintf(stderr, "Failed to execute '%s': %s\n", app_args[0], strerror(errno));
|
||||
_exit(1);
|
||||
} else {
|
||||
/* parent process */
|
||||
|
||||
close(child_stdin[0]);
|
||||
child_stdin[0] = 0;
|
||||
close(child_stdout[1]);
|
||||
child_stdout[1] = 0;
|
||||
close(child_stderr[1]);
|
||||
child_stderr[1] = 0;
|
||||
res = eivr_comm(chan, u, child_stdin[1], child_stdout[0], child_stderr[0], pipe_delim_argbuf);
|
||||
res = eivr_comm(chan, u, child_stdin[1], child_stdout[0], child_stderr[0], pipe_delim_args, flags);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (gen_active)
|
||||
if (u->gen_active)
|
||||
ast_deactivate_generator(chan);
|
||||
|
||||
if (child_stdin[0])
|
||||
@@ -456,12 +514,10 @@ static int app_exec(struct ast_channel *chan, void *data)
|
||||
|
||||
if (child_stderr[1])
|
||||
close(child_stderr[1]);
|
||||
|
||||
if (ser) {
|
||||
fclose(ser->f);
|
||||
ast_tcptls_session_instance_destroy(ser);
|
||||
}
|
||||
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
|
||||
ast_free(entry);
|
||||
|
||||
@@ -470,7 +526,7 @@ static int app_exec(struct ast_channel *chan, void *data)
|
||||
|
||||
static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||
int eivr_events_fd, int eivr_commands_fd, int eivr_errors_fd,
|
||||
const char *args)
|
||||
const struct ast_str *args, const struct ast_flags flags)
|
||||
{
|
||||
struct playlist_entry *entry;
|
||||
struct ast_frame *f;
|
||||
@@ -482,6 +538,7 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||
char *command;
|
||||
int res = -1;
|
||||
int test_available_fd = -1;
|
||||
int hangup_info_sent = 0;
|
||||
|
||||
FILE *eivr_commands = NULL;
|
||||
FILE *eivr_errors = NULL;
|
||||
@@ -506,8 +563,9 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||
|
||||
setvbuf(eivr_events, NULL, _IONBF, 0);
|
||||
setvbuf(eivr_commands, NULL, _IONBF, 0);
|
||||
if(eivr_errors)
|
||||
if (eivr_errors) {
|
||||
setvbuf(eivr_errors, NULL, _IONBF, 0);
|
||||
}
|
||||
|
||||
res = 0;
|
||||
|
||||
@@ -517,11 +575,17 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
if (ast_check_hangup(chan)) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
|
||||
send_eivr_event(eivr_events, 'H', NULL, chan);
|
||||
res = -1;
|
||||
break;
|
||||
if (!hangup_info_sent && !(ast_test_flag(&flags, run_dead)) && ast_check_hangup(chan)) {
|
||||
if (ast_test_flag(&flags, ignore_hangup)) {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Got check_hangup, but ignore_hangup set so sending 'I' command\n");
|
||||
send_eivr_event(eivr_events, 'I', "HANGUP", chan);
|
||||
hangup_info_sent = 1;
|
||||
} else {
|
||||
ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
|
||||
send_eivr_event(eivr_events, 'H', NULL, chan);
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ready_fd = 0;
|
||||
@@ -531,7 +595,7 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||
|
||||
rchan = ast_waitfor_nandfds(&chan, 1, waitfds, (eivr_errors_fd < 0) ? 1 : 2, &exception, &ready_fd, &ms);
|
||||
|
||||
if (!AST_LIST_EMPTY(&u->finishlist)) {
|
||||
if (chan->_state == AST_STATE_UP && !AST_LIST_EMPTY(&u->finishlist)) {
|
||||
AST_LIST_LOCK(&u->finishlist);
|
||||
while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
|
||||
send_eivr_event(eivr_events, 'F', entry->filename, chan);
|
||||
@@ -540,7 +604,7 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||
AST_LIST_UNLOCK(&u->finishlist);
|
||||
}
|
||||
|
||||
if (rchan) {
|
||||
if (chan->_state == AST_STATE_UP && !(ast_check_hangup(chan)) && rchan) {
|
||||
/* the channel has something */
|
||||
f = ast_read(chan);
|
||||
if (!f) {
|
||||
@@ -589,15 +653,37 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||
command = ast_strip(input);
|
||||
|
||||
if (option_debug)
|
||||
ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input);
|
||||
ast_debug(1, "got command '%s'\n", input);
|
||||
|
||||
if (strlen(input) < 4)
|
||||
continue;
|
||||
|
||||
if (input[0] == 'P') {
|
||||
send_eivr_event(eivr_events, 'P', args, chan);
|
||||
|
||||
send_eivr_event(eivr_events, 'P', args->str, chan);
|
||||
} else if ( input[0] == 'T' ) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Answering channel if needed and starting generator\n");
|
||||
if (chan->_state != AST_STATE_UP) {
|
||||
if (ast_test_flag(&flags, run_dead)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Running ExternalIVR with 'd'ead flag on non-hungup channel isn't supported\n");
|
||||
send_eivr_event(eivr_events, 'Z', "ANSWER_FAILURE", chan);
|
||||
continue;
|
||||
}
|
||||
ast_answer(chan);
|
||||
}
|
||||
if (!(u->gen_active)) {
|
||||
if (ast_activate_generator(chan, &gen, u) < 0) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
|
||||
send_eivr_event(eivr_events, 'Z', "GENERATOR_FAILURE", chan);
|
||||
} else {
|
||||
u->gen_active = 1;
|
||||
}
|
||||
}
|
||||
} else if (input[0] == 'S') {
|
||||
if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Queue 'S'et called on unanswered channel\n");
|
||||
send_eivr_event(eivr_events, 'Z', NULL, chan);
|
||||
continue;
|
||||
}
|
||||
if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
|
||||
send_eivr_event(eivr_events, 'Z', NULL, chan);
|
||||
@@ -617,6 +703,11 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||
AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
|
||||
AST_LIST_UNLOCK(&u->playlist);
|
||||
} else if (input[0] == 'A') {
|
||||
if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Queue 'A'ppend called on unanswered channel\n");
|
||||
send_eivr_event(eivr_events, 'Z', NULL, chan);
|
||||
continue;
|
||||
}
|
||||
if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
|
||||
send_eivr_event(eivr_events, 'Z', NULL, chan);
|
||||
@@ -657,6 +748,11 @@ static int eivr_comm(struct ast_channel *chan, struct ivr_localuser *u,
|
||||
res = -1;
|
||||
break;
|
||||
} else if (input[0] == 'O') {
|
||||
if (chan->_state != AST_STATE_UP || ast_check_hangup(chan)) {
|
||||
ast_chan_log(LOG_WARNING, chan, "Option called on unanswered channel\n");
|
||||
send_eivr_event(eivr_events, 'Z', NULL, chan);
|
||||
continue;
|
||||
}
|
||||
if (!strcasecmp(&input[2], "autoclear"))
|
||||
u->option_autoclear = 1;
|
||||
else if (!strcasecmp(&input[2], "noautoclear"))
|
||||
|
@@ -24,7 +24,8 @@ connected to the Asterisk process as follows:
|
||||
|
||||
stdin (0) - DTMF and hangup events will be received on this handle
|
||||
stdout (1) - Playback and hangup commands can be sent on this handle
|
||||
There are no error messages available when using ExternalIVR over TCP.
|
||||
There are no error messages available when using ExternalIVR over TCP,
|
||||
use the 'L' command as a replacement for this.
|
||||
|
||||
The application will also create an audio generator to play audio to
|
||||
the channel, and will start playing silence. When your application
|
||||
@@ -47,6 +48,15 @@ or you could cause the channel to become non-responsive.
|
||||
If the child process dies, ExternalIVR() will notice this and hang up
|
||||
the channel immediately (and also send a message to the log).
|
||||
|
||||
ExternalIVR() Options
|
||||
----------------------
|
||||
n: 'n'oanswer, don't answer an otherwise unanswered channel.
|
||||
i: 'i'gnore_hangup, instead of sending an 'H' event and exiting
|
||||
ExternalIVR() upon channel hangup, it instead sends an 'I' event
|
||||
and expects the external application to exit the process.
|
||||
d: 'd'ead, allows the operation of ExternalIVR() on channels that
|
||||
have already been hung up.
|
||||
|
||||
DTMF (and other) events
|
||||
-----------------------
|
||||
|
||||
@@ -71,6 +81,10 @@ D: a file was dropped from the play list due to interruption (the
|
||||
data element will be the dropped file name)
|
||||
F: a file has finished playing (the data element will be the file
|
||||
name)
|
||||
P: a response to the 'P' command (see below)
|
||||
G: a response to the 'G' command (see below)
|
||||
I: a Inform message, meant to "inform" the client that something
|
||||
has occured. (see Inform Messages below)
|
||||
|
||||
The timestamp will be 10 digits long, and will be a decimal
|
||||
representation of a standard Unix epoch-based timestamp.
|
||||
@@ -87,7 +101,11 @@ A,filename
|
||||
H,message
|
||||
E,message
|
||||
O,option
|
||||
V,name=value
|
||||
V,name=value[,name=value[,name=value]]
|
||||
G,name[,name[,name]]
|
||||
L,log_message
|
||||
P,TIMESTAMP
|
||||
T,TIMESTAMP
|
||||
|
||||
The 'S' command checks to see if there is a playable audio file with
|
||||
the specified name, and if so, clear's the generator's playlist and
|
||||
@@ -116,7 +134,25 @@ ExternalIVR() application. The supported options are:
|
||||
Automatically interrupt and clear the playlist upon reception
|
||||
of DTMF input.
|
||||
|
||||
The 'V' command sets the specified channel variable to the specified value.
|
||||
The 'T' command will answer and unanswered channel. If it fails either
|
||||
answering the channel or starting the generator it sends a Z response
|
||||
of "Z,TIMESTAMP,ANSWER_FAILED" or "Z,TIMESTAMP,GENERATOR_FAILED"
|
||||
respectively.
|
||||
|
||||
The 'V' command sets the specified channel variable(s) to the specified
|
||||
value(s).
|
||||
|
||||
The 'G' command gets the specified channel variable(s). Multiple
|
||||
variables are separated by commas. Response is in name=value format.
|
||||
|
||||
The 'P' command gets the parameters passed into ExternalIVR() minus
|
||||
the options to ExternalIVR() itself:
|
||||
If ExternalIVR() is executed as:
|
||||
ExternalIVR(/usr/bin/foo(arg1,arg2),n)
|
||||
The response to the 'P' command would be:
|
||||
P,TIMESTAMP,/usr/bin/foo|arg1|arg2
|
||||
|
||||
The 'L' command puts a message into the Asterisk log.
|
||||
|
||||
Errors
|
||||
------
|
||||
|
Reference in New Issue
Block a user