mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-03 03:20:57 +00:00
Fix connected-line/redirecting interception gosubs executing more than intended.
* Redo ast_app_run_sub()/ast_app_exec_sub() to use a known return point so execution will stop after the routine returns there. (s@gosub_virtual_context:1) * Create ast_app_exec_macro() and ast_app_exec_sub() to run the macro and gosub application respectively with the parameter string already created. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@362962 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -801,6 +801,8 @@ static struct agi_command gosub_agi_command =
|
||||
|
||||
static int unload_module(void)
|
||||
{
|
||||
struct ast_context *con;
|
||||
|
||||
ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
|
||||
|
||||
ast_unregister_application(app_return);
|
||||
@@ -811,11 +813,30 @@ static int unload_module(void)
|
||||
ast_custom_function_unregister(&peek_function);
|
||||
ast_custom_function_unregister(&stackpeek_function);
|
||||
|
||||
con = ast_context_find("gosub_virtual_context");
|
||||
if (con) {
|
||||
/* leave nothing behind */
|
||||
ast_context_remove_extension2(con, "s", 1, NULL, 0);
|
||||
ast_context_destroy(con, "app_stack");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
struct ast_context *con;
|
||||
|
||||
/* Create internal gosub return target to indicate successful completion. */
|
||||
con = ast_context_find_or_create(NULL, NULL, "gosub_virtual_context", "app_stack");
|
||||
if (!con) {
|
||||
ast_log(LOG_ERROR, "'gosub_virtual_context' does not exist and unable to create\n");
|
||||
} else {
|
||||
ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp",
|
||||
ast_strdup("Internal Gosub call complete GOSUB_RETVAL=${GOSUB_RETVAL}"),
|
||||
ast_free_ptr, "app_stack");
|
||||
}
|
||||
|
||||
ast_agi_register(ast_module_info->self, &gosub_agi_command);
|
||||
|
||||
ast_register_application_xml(app_pop, pop_exec);
|
||||
|
@@ -115,44 +115,96 @@ int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxl
|
||||
int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd);
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
* \brief Run a macro on a channel, placing a second channel into autoservice.
|
||||
* \brief Run a macro on a channel, placing an optional second channel into autoservice.
|
||||
* \since 11.0
|
||||
*
|
||||
* This is a shorthand method that makes it very easy to run a macro on any given
|
||||
* channel. It is perfectly reasonable to supply a NULL autoservice_chan here in case
|
||||
* there is no channel to place into autoservice. It is very important that the
|
||||
* autoservice_chan parameter is not locked prior to calling ast_app_run_macro. A
|
||||
* deadlock could result, otherwise.
|
||||
* \details
|
||||
* This is a shorthand method that makes it very easy to run a
|
||||
* macro on any given channel. It is perfectly reasonable to
|
||||
* supply a NULL autoservice_chan here in case there is no
|
||||
* channel to place into autoservice.
|
||||
*
|
||||
* \note It is very important that the autoservice_chan is not
|
||||
* locked prior to calling. Otherwise, a deadlock could result.
|
||||
*
|
||||
* \param autoservice_chan A channel to place into autoservice while the macro is run
|
||||
* \param macro_chan The channel to run the macro on
|
||||
* \param macro_name The name of the macro to run
|
||||
* \param macro_args The arguments to pass to the macro
|
||||
* \param macro_chan Channel to execute macro on.
|
||||
* \param macro_args Macro application argument string.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
* \retval -1 on error
|
||||
*/
|
||||
int ast_app_exec_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_args);
|
||||
|
||||
/*!
|
||||
* \since 1.8
|
||||
* \brief Run a macro on a channel, placing an optional second channel into autoservice.
|
||||
*
|
||||
* \details
|
||||
* This is a shorthand method that makes it very easy to run a
|
||||
* macro on any given channel. It is perfectly reasonable to
|
||||
* supply a NULL autoservice_chan here in case there is no
|
||||
* channel to place into autoservice.
|
||||
*
|
||||
* \note It is very important that the autoservice_chan is not
|
||||
* locked prior to calling. Otherwise, a deadlock could result.
|
||||
*
|
||||
* \param autoservice_chan A channel to place into autoservice while the macro is run
|
||||
* \param macro_chan Channel to execute macro on.
|
||||
* \param macro_name The name of the macro to run.
|
||||
* \param macro_args The arguments to pass to the macro.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 on error
|
||||
*/
|
||||
int ast_app_run_macro(struct ast_channel *autoservice_chan,
|
||||
struct ast_channel *macro_chan, const char *macro_name, const char *macro_args);
|
||||
|
||||
/*!
|
||||
* \since 11
|
||||
* \brief Run a subroutine on a channel, placing a second channel into autoservice.
|
||||
* \brief Run a subroutine on a channel, placing an optional second channel into autoservice.
|
||||
*
|
||||
* This is a shorthand method that makes it very easy to run a subroutine on any given
|
||||
* channel. It is perfectly reasonable to supply a NULL autoservice_chan here in case
|
||||
* there is no channel to place into autoservice. It is very important that the
|
||||
* autoservice_chan parameter is not locked prior to calling ast_app_run_sub. A
|
||||
* deadlock could result, otherwise.
|
||||
* \details
|
||||
* This is a shorthand method that makes it very easy to run a
|
||||
* subroutine on any given channel. It is perfectly reasonable
|
||||
* to supply a NULL autoservice_chan here in case there is no
|
||||
* channel to place into autoservice.
|
||||
*
|
||||
* \note It is very important that the autoservice_chan is not
|
||||
* locked prior to calling. Otherwise, a deadlock could result.
|
||||
*
|
||||
* \param autoservice_chan A channel to place into autoservice while the subroutine is run
|
||||
* \param sub_chan The channel to run the subroutine on
|
||||
* \param name The name of the subroutine to run
|
||||
* \param args The arguments to pass to the subroutien
|
||||
* \param sub_chan Channel to execute subroutine on.
|
||||
* \param sub_args Gosub application argument string.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
* \retval -1 on error
|
||||
*/
|
||||
int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args);
|
||||
|
||||
/*!
|
||||
* \since 11
|
||||
* \brief Run a subroutine on a channel, placing an optional second channel into autoservice.
|
||||
*
|
||||
* \details
|
||||
* This is a shorthand method that makes it very easy to run a
|
||||
* subroutine on any given channel. It is perfectly reasonable
|
||||
* to supply a NULL autoservice_chan here in case there is no
|
||||
* channel to place into autoservice.
|
||||
*
|
||||
* \note It is very important that the autoservice_chan is not
|
||||
* locked prior to calling. Otherwise, a deadlock could result.
|
||||
*
|
||||
* \param autoservice_chan A channel to place into autoservice while the subroutine is run
|
||||
* \param sub_chan Channel to execute subroutine on.
|
||||
* \param sub_location The location of the subroutine to run.
|
||||
* \param sub_args The arguments to pass to the subroutine.
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 on error
|
||||
*/
|
||||
int ast_app_run_sub(struct ast_channel *autoservice_chan,
|
||||
struct ast_channel *sub_chan, const char *name, const char *args);
|
||||
struct ast_channel *sub_chan, const char *sub_location, const char *sub_args);
|
||||
|
||||
/*!
|
||||
* \brief Set voicemail function callbacks
|
||||
|
196
main/app.c
196
main/app.c
@@ -247,79 +247,175 @@ int ast_app_getdata_full(struct ast_channel *c, const char *prompt, char *s, int
|
||||
return res;
|
||||
}
|
||||
|
||||
static int app_exec_dialplan(struct ast_channel *autoservice_chan, struct ast_channel *exec_chan, const char * const args, int use_gosub)
|
||||
int ast_app_exec_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_args)
|
||||
{
|
||||
|
||||
struct ast_app *app;
|
||||
struct ast_app *macro_app;
|
||||
int res;
|
||||
char * app_type = use_gosub ? "GoSub" : "Macro";
|
||||
|
||||
app = pbx_findapp(app_type);
|
||||
if (!app) {
|
||||
ast_log(LOG_WARNING, "Cannot run '%s' because the '%s' application is not available\n", args, app_type);
|
||||
macro_app = pbx_findapp("Macro");
|
||||
if (!macro_app) {
|
||||
ast_log(LOG_WARNING,
|
||||
"Cannot run 'Macro(%s)'. The application is not available.\n", macro_args);
|
||||
return -1;
|
||||
}
|
||||
if (autoservice_chan) {
|
||||
ast_autoservice_start(autoservice_chan);
|
||||
}
|
||||
res = pbx_exec(exec_chan, app, args);
|
||||
if (use_gosub && !res) {
|
||||
struct ast_pbx_args gosub_args = {{0}};
|
||||
struct ast_pbx *pbx = ast_channel_pbx(exec_chan);
|
||||
/* supress warning about a pbx already being on the channel */
|
||||
ast_channel_pbx_set(exec_chan, NULL);
|
||||
gosub_args.no_hangup_chan = 1;
|
||||
ast_pbx_run_args(exec_chan, &gosub_args);
|
||||
if (ast_channel_pbx(exec_chan)) {
|
||||
ast_free(ast_channel_pbx(exec_chan));
|
||||
}
|
||||
ast_channel_pbx_set(exec_chan, pbx);
|
||||
|
||||
ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(macro_chan),
|
||||
ast_channel_context(macro_chan), ast_channel_exten(macro_chan),
|
||||
ast_channel_priority(macro_chan));
|
||||
|
||||
res = pbx_exec(macro_chan, macro_app, macro_args);
|
||||
ast_debug(4, "Macro exited with status %d\n", res);
|
||||
|
||||
/*
|
||||
* Assume anything negative from Macro is an error.
|
||||
* Anything else is success.
|
||||
*/
|
||||
if (res < 0) {
|
||||
res = -1;
|
||||
} else {
|
||||
res = 0;
|
||||
}
|
||||
|
||||
ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(macro_chan),
|
||||
ast_channel_context(macro_chan), ast_channel_exten(macro_chan),
|
||||
ast_channel_priority(macro_chan));
|
||||
|
||||
if (autoservice_chan) {
|
||||
ast_autoservice_stop(autoservice_chan);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *name, const char *args)
|
||||
int ast_app_run_macro(struct ast_channel *autoservice_chan, struct ast_channel *macro_chan, const char *macro_name, const char *macro_args)
|
||||
{
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf), "%s%s%s", name, ast_strlen_zero(args) ? "" : ",", S_OR(args, ""));
|
||||
return app_exec_dialplan(autoservice_chan, macro_chan, buf, 0);
|
||||
int res;
|
||||
char *args_str;
|
||||
size_t args_len;
|
||||
|
||||
if (ast_strlen_zero(macro_args)) {
|
||||
return ast_app_exec_macro(autoservice_chan, macro_chan, macro_name);
|
||||
}
|
||||
|
||||
/* Create the Macro application argument string. */
|
||||
args_len = strlen(macro_name) + strlen(macro_args) + 2;
|
||||
args_str = ast_malloc(args_len);
|
||||
if (!args_str) {
|
||||
return -1;
|
||||
}
|
||||
snprintf(args_str, args_len, "%s,%s", macro_name, macro_args);
|
||||
|
||||
res = ast_app_exec_macro(autoservice_chan, macro_chan, args_str);
|
||||
ast_free(args_str);
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_app_run_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *location, const char *args)
|
||||
int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args)
|
||||
{
|
||||
char buf[1024];
|
||||
size_t offset = snprintf(buf, sizeof(buf), "%s", location);
|
||||
struct ast_app *sub_app;
|
||||
const char *saved_context;
|
||||
const char *saved_exten;
|
||||
int saved_priority;
|
||||
int saved_autoloopflag;
|
||||
int res;
|
||||
|
||||
/* need to bump the priority by one if we already have a pbx */
|
||||
if (ast_channel_pbx(sub_chan)) {
|
||||
int iprio;
|
||||
const char *priority = location;
|
||||
const char *next = strchr(priority,',');
|
||||
sub_app = pbx_findapp("Gosub");
|
||||
if (!sub_app) {
|
||||
ast_log(LOG_WARNING,
|
||||
"Cannot run 'Gosub(%s)'. The application is not available.\n", sub_args);
|
||||
return -1;
|
||||
}
|
||||
if (autoservice_chan) {
|
||||
ast_autoservice_start(autoservice_chan);
|
||||
}
|
||||
|
||||
/* jump to the priority portion of the location */
|
||||
if (next) {
|
||||
priority = next + 1;
|
||||
}
|
||||
next = strchr(priority,',');
|
||||
if (next) {
|
||||
priority = next + 1;
|
||||
}
|
||||
/* if the priority isn't numeric, it's as if we never took this branch... */
|
||||
if (sscanf(priority, "%d", &iprio)) {
|
||||
offset = priority - location;
|
||||
iprio++;
|
||||
if (offset < sizeof(buf)) {
|
||||
offset += snprintf(buf + offset, sizeof(buf) - offset, "%d", iprio);
|
||||
}
|
||||
}
|
||||
ast_channel_lock(sub_chan);
|
||||
|
||||
/* Save current dialplan location */
|
||||
saved_context = ast_strdupa(ast_channel_context(sub_chan));
|
||||
saved_exten = ast_strdupa(ast_channel_exten(sub_chan));
|
||||
saved_priority = ast_channel_priority(sub_chan);
|
||||
|
||||
/*
|
||||
* Save flag to restore at the end so we don't have to play with
|
||||
* the priority in the gosub location string.
|
||||
*/
|
||||
saved_autoloopflag = ast_test_flag(ast_channel_flags(sub_chan), AST_FLAG_IN_AUTOLOOP);
|
||||
ast_clear_flag(ast_channel_flags(sub_chan), AST_FLAG_IN_AUTOLOOP);
|
||||
|
||||
/* Set known location for Gosub to return - 1 */
|
||||
ast_channel_context_set(sub_chan, "gosub_virtual_context");
|
||||
ast_channel_exten_set(sub_chan, "s");
|
||||
ast_channel_priority_set(sub_chan, 1 - 1);
|
||||
|
||||
ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(sub_chan),
|
||||
saved_context, saved_exten, saved_priority);
|
||||
|
||||
ast_channel_unlock(sub_chan);
|
||||
res = pbx_exec(sub_chan, sub_app, sub_args);
|
||||
ast_debug(4, "Gosub exited with status %d\n", res);
|
||||
ast_channel_lock(sub_chan);
|
||||
if (!res) {
|
||||
struct ast_pbx_args gosub_args = {{0}};
|
||||
struct ast_pbx *saved_pbx;
|
||||
|
||||
/* supress warning about a pbx already being on the channel */
|
||||
saved_pbx = ast_channel_pbx(sub_chan);
|
||||
ast_channel_pbx_set(sub_chan, NULL);
|
||||
|
||||
ast_channel_unlock(sub_chan);
|
||||
gosub_args.no_hangup_chan = 1;
|
||||
ast_pbx_run_args(sub_chan, &gosub_args);
|
||||
ast_channel_lock(sub_chan);
|
||||
|
||||
/* Restore pbx. */
|
||||
ast_free(ast_channel_pbx(sub_chan));
|
||||
ast_channel_pbx_set(sub_chan, saved_pbx);
|
||||
}
|
||||
if (!ast_strlen_zero(args) && offset < sizeof(buf)) {
|
||||
snprintf(buf + offset, sizeof(buf) - offset, "(%s)", args);
|
||||
|
||||
ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(sub_chan),
|
||||
ast_channel_context(sub_chan), ast_channel_exten(sub_chan),
|
||||
ast_channel_priority(sub_chan));
|
||||
|
||||
/* Restore flag */
|
||||
ast_set2_flag(ast_channel_flags(sub_chan), saved_autoloopflag, AST_FLAG_IN_AUTOLOOP);
|
||||
|
||||
/* Restore dialplan location */
|
||||
ast_channel_context_set(sub_chan, saved_context);
|
||||
ast_channel_exten_set(sub_chan, saved_exten);
|
||||
ast_channel_priority_set(sub_chan, saved_priority);
|
||||
|
||||
ast_channel_unlock(sub_chan);
|
||||
|
||||
if (autoservice_chan) {
|
||||
ast_autoservice_stop(autoservice_chan);
|
||||
}
|
||||
return app_exec_dialplan(autoservice_chan, sub_chan, buf, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_app_run_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_location, const char *sub_args)
|
||||
{
|
||||
int res;
|
||||
char *args_str;
|
||||
size_t args_len;
|
||||
|
||||
if (ast_strlen_zero(sub_args)) {
|
||||
return ast_app_exec_sub(autoservice_chan, sub_chan, sub_location);
|
||||
}
|
||||
|
||||
/* Create the Gosub application argument string. */
|
||||
args_len = strlen(sub_location) + strlen(sub_args) + 3;
|
||||
args_str = ast_malloc(args_len);
|
||||
if (!args_str) {
|
||||
return -1;
|
||||
}
|
||||
snprintf(args_str, args_len, "%s(%s)", sub_location, sub_args);
|
||||
|
||||
res = ast_app_exec_sub(autoservice_chan, sub_chan, args_str);
|
||||
ast_free(args_str);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
|
||||
|
Reference in New Issue
Block a user