app_page.c: Fix crash when forwarding with a predial handler.
authorRichard Mudgett <rmudgett@digium.com>
Tue, 22 Sep 2015 22:08:49 +0000 (17:08 -0500)
committerRichard Mudgett <rmudgett@digium.com>
Tue, 22 Sep 2015 22:32:03 +0000 (17:32 -0500)
Page uses the async method of dialing with the dial API.  When a call gets
forwarded there is no calling channel available.  If the predial handler
was set then the calling channel could not be put into auto-service
for the forwarded call because it doesn't exist.  A crash is the result.

* Moved the callee predial parameter string processing to before the
string is passed to the dial API rather than having the dial API do it.
There are a few benefits do doing this.  The first is the predial
parameter string processing doesn't need to be done for each channel
called by the dial API.  The second is in async mode and the forwarded
channel is to have the predial handler executed on it then the
non-existent calling channel does not need to be present to process the
predial parameter string.

* Don't start auto-service on a non-existent calling channel to execute
the predial handler when the dial API is in async mode and forwarding a
call.

ASTERISK-25384 #close
Reported by: Chet Stevens

Change-Id: If53892b286d29f6cf955e2545b03dcffa2610981

apps/app_page.c
main/dial.c

index cea75cb..10a96b6 100644 (file)
@@ -249,12 +249,18 @@ static void page_state_callback(struct ast_dial *dial)
 
 static int page_exec(struct ast_channel *chan, const char *data)
 {
-       char *tech, *resource, *tmp;
-       char confbridgeopts[128], originator[AST_CHANNEL_NAME];
+       char *tech;
+       char *resource;
+       char *tmp;
+       char *predial_callee = NULL;
+       char confbridgeopts[128];
+       char originator[AST_CHANNEL_NAME];
        struct page_options options = { { 0, }, { 0, } };
        unsigned int confid = ast_random();
        struct ast_app *app;
-       int res = 0, pos = 0, i = 0;
+       int res = 0;
+       int pos = 0;
+       int i = 0;
        struct ast_dial **dial_list;
        unsigned int num_dials;
        int timeout = 0;
@@ -310,6 +316,15 @@ static int page_exec(struct ast_channel *chan, const char *data)
                return -1;
        }
 
+       /* PREDIAL: Preprocess any callee gosub arguments. */
+       if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLEE)
+               && !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLEE])) {
+               ast_replace_subargument_delimiter(options.opts[OPT_ARG_PREDIAL_CALLEE]);
+               predial_callee =
+                       (char *) ast_app_expand_sub_args(chan, options.opts[OPT_ARG_PREDIAL_CALLEE]);
+       }
+
+       /* PREDIAL: Run gosub on the caller's channel */
        if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLER)
                && !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLER])) {
                ast_replace_subargument_delimiter(options.opts[OPT_ARG_PREDIAL_CALLER]);
@@ -360,9 +375,8 @@ static int page_exec(struct ast_channel *chan, const char *data)
                /* Set ANSWER_EXEC as global option */
                ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, confbridgeopts);
 
-               if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLEE)
-                       && !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLEE])) {
-                       ast_dial_option_global_enable(dial, AST_DIAL_OPTION_PREDIAL, options.opts[OPT_ARG_PREDIAL_CALLEE]);
+               if (predial_callee) {
+                       ast_dial_option_global_enable(dial, AST_DIAL_OPTION_PREDIAL, predial_callee);
                }
 
                if (timeout) {
@@ -383,6 +397,8 @@ static int page_exec(struct ast_channel *chan, const char *data)
                dial_list[pos++] = dial;
        }
 
+       ast_free(predial_callee);
+
        if (!ast_test_flag(&options.flags, PAGE_QUIET)) {
                res = ast_streamfile(chan, "beep", ast_channel_language(chan));
                if (!res)
index b935b6d..34d2f70 100644 (file)
@@ -378,14 +378,13 @@ static int begin_dial_prerun(struct ast_dial_channel *channel, struct ast_channe
        ast_channel_unlock(channel->owner);
 
        if (!ast_strlen_zero(predial_string)) {
-               const char *predial_callee = ast_app_expand_sub_args(chan, predial_string);
-               if (!predial_callee) {
-                       ast_log(LOG_ERROR, "Could not expand subroutine arguments in predial request '%s'\n", predial_string);
+               if (chan) {
+                       ast_autoservice_start(chan);
+               }
+               ast_pre_call(channel->owner, predial_string);
+               if (chan) {
+                       ast_autoservice_stop(chan);
                }
-               ast_autoservice_start(chan);
-               ast_pre_call(channel->owner, predial_callee);
-               ast_autoservice_stop(chan);
-               ast_free((char *) predial_callee);
        }
 
        return 0;
@@ -397,10 +396,6 @@ int ast_dial_prerun(struct ast_dial *dial, struct ast_channel *chan, struct ast_
        int res = -1;
        char *predial_string = dial->options[AST_DIAL_OPTION_PREDIAL];
 
-       if (!ast_strlen_zero(predial_string)) {
-               ast_replace_subargument_delimiter(predial_string);
-       }
-
        AST_LIST_LOCK(&dial->channels);
        AST_LIST_TRAVERSE(&dial->channels, channel, list) {
                if ((res = begin_dial_prerun(channel, chan, cap, predial_string))) {
@@ -450,10 +445,6 @@ static int begin_dial(struct ast_dial *dial, struct ast_channel *chan, int async
        int success = 0;
        char *predial_string = dial->options[AST_DIAL_OPTION_PREDIAL];
 
-       if (!ast_strlen_zero(predial_string)) {
-               ast_replace_subargument_delimiter(predial_string);
-       }
-
        /* Iterate through channel list, requesting and calling each one */
        AST_LIST_LOCK(&dial->channels);
        AST_LIST_TRAVERSE(&dial->channels, channel, list) {
@@ -473,10 +464,6 @@ static int handle_call_forward(struct ast_dial *dial, struct ast_dial_channel *c
        char *tech = "Local", *device = tmp, *stuff;
        char *predial_string = dial->options[AST_DIAL_OPTION_PREDIAL];
 
-       if (!ast_strlen_zero(predial_string)) {
-               ast_replace_subargument_delimiter(predial_string);
-       }
-
        /* If call forwarding is disabled just drop the original channel and don't attempt to dial the new one */
        if (FIND_RELATIVE_OPTION(dial, channel, AST_DIAL_OPTION_DISABLE_CALL_FORWARDING)) {
                ast_hangup(original);