Merge "AST-2018-005: Fix tdata leaks when calling pjsip_endpt_send_response(2)"
[asterisk/asterisk.git] / apps / app_stack.c
index 9260f2a..4560865 100644 (file)
@@ -21,7 +21,7 @@
  * \brief Stack applications Gosub, Return, etc.
  *
  * \author Tilghman Lesher <app_stack_v003@the-tilghman.com>
- * 
+ *
  * \ingroup applications
  */
 
@@ -31,8 +31,6 @@
  ***/
 
 #include "asterisk.h"
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/pbx.h"
 #include "asterisk/module.h"
@@ -211,7 +209,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <managerEventInstance class="EVENT_FLAG_DIALPLAN">
                        <synopsis>Raised when a variable local to the gosub stack frame is set due to a subroutine call.</synopsis>
                        <syntax>
-                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                               <channel_snapshot/>
                                <parameter name="Variable">
                                        <para>The LOCAL variable being set.</para>
                                        <note><para>The variable name will always be enclosed with
@@ -251,6 +249,8 @@ struct gosub_stack_frame {
        int priority;
        /*! TRUE if the return location marks the end of a special routine. */
        unsigned int is_special:1;
+       /*! Whether or not we were in a subroutine when this one was created */
+       unsigned int in_subroutine:1;
        char *context;
        char extension[0];
 };
@@ -273,8 +273,9 @@ static int frame_set_var(struct ast_channel *chan, struct gosub_stack_frame *fra
        }
 
        if (!found) {
-               variables = ast_var_assign(var, "");
-               AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
+               if ((variables = ast_var_assign(var, ""))) {
+                       AST_LIST_INSERT_HEAD(&frame->varshead, variables, entries);
+               }
                pbx_builtin_pushvar_helper(chan, var, value);
        } else {
                pbx_builtin_setvar_helper(chan, var, value);
@@ -302,14 +303,14 @@ static void gosub_release_frame(struct ast_channel *chan, struct gosub_stack_fra
         */
        while ((vardata = AST_LIST_REMOVE_HEAD(&frame->varshead, entries))) {
                if (chan)
-                       pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);   
+                       pbx_builtin_setvar_helper(chan, ast_var_name(vardata), NULL);
                ast_var_delete(vardata);
        }
 
        ast_free(frame);
 }
 
-static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, unsigned char arguments)
+static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const char *extension, int priority, int in_subroutine, unsigned char arguments)
 {
        struct gosub_stack_frame *new = NULL;
        int len_extension = strlen(extension), len_context = strlen(context);
@@ -320,6 +321,7 @@ static struct gosub_stack_frame *gosub_allocate_frame(const char *context, const
                new->context = new->extension + len_extension + 1;
                strcpy(new->context, context);
                new->priority = priority;
+               new->in_subroutine = in_subroutine ? 1 : 0;
                new->arguments = arguments;
        }
        return new;
@@ -415,6 +417,7 @@ static int return_exec(struct ast_channel *chan, const char *data)
                --oldframe->priority;
        }
        ast_channel_priority_set(chan, oldframe->priority);
+       ast_set2_flag(ast_channel_flags(chan), oldframe->in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
 
        gosub_release_frame(chan, oldframe);
 
@@ -523,6 +526,7 @@ static int gosub_exec(struct ast_channel *chan, const char *data)
        char *orig_exten;
        char *dest_context;
        char *dest_exten;
+       int orig_in_subroutine;
        int orig_priority;
        int dest_priority;
        int i;
@@ -562,6 +566,7 @@ static int gosub_exec(struct ast_channel *chan, const char *data)
        orig_context = ast_strdupa(ast_channel_context(chan));
        orig_exten = ast_strdupa(ast_channel_exten(chan));
        orig_priority = ast_channel_priority(chan);
+       orig_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
        ast_channel_unlock(chan);
 
        if (ast_parseable_goto(chan, label)) {
@@ -629,7 +634,7 @@ static int gosub_exec(struct ast_channel *chan, const char *data)
        }
 
        /* Create the return address */
-       newframe = gosub_allocate_frame(orig_context, orig_exten, orig_priority + 1, max_argc);
+       newframe = gosub_allocate_frame(orig_context, orig_exten, orig_priority + 1, orig_in_subroutine, max_argc);
        if (!newframe) {
                goto error_exit_locked;
        }
@@ -640,9 +645,11 @@ static int gosub_exec(struct ast_channel *chan, const char *data)
                frame_set_var(chan, newframe, argname, i < args2.argc ? args2.argval[i] : "");
                ast_debug(1, "Setting '%s' to '%s'\n", argname, i < args2.argc ? args2.argval[i] : "");
        }
-       snprintf(argname, sizeof(argname), "%d", args2.argc);
+       snprintf(argname, sizeof(argname), "%u", args2.argc);
        frame_set_var(chan, newframe, "ARGC", argname);
 
+       ast_set_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
+
        /* And finally, save our return address */
        AST_LIST_LOCK(oldlist);
        AST_LIST_INSERT_HEAD(oldlist, newframe, entries);
@@ -707,6 +714,11 @@ static int local_read(struct ast_channel *chan, const char *cmd, char *data, cha
        struct gosub_stack_frame *frame;
        struct ast_var_t *variables;
 
+       if (!chan) {
+               ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
+               return -1;
+       }
+
        ast_channel_lock(chan);
        if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
                ast_channel_unlock(chan);
@@ -741,6 +753,11 @@ static int local_write(struct ast_channel *chan, const char *cmd, char *var, con
        struct gosub_stack_list *oldlist;
        struct gosub_stack_frame *frame;
 
+       if (!chan) {
+               ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
+               return -1;
+       }
+
        ast_channel_lock(chan);
        if (!(stack_store = ast_channel_datastore_find(chan, &stack_info, NULL))) {
                ast_log(LOG_ERROR, "Tried to set LOCAL(%s), but we aren't within a Gosub routine\n", var);
@@ -783,6 +800,12 @@ static int peek_read(struct ast_channel *chan, const char *cmd, char *data, char
        }
 
        AST_STANDARD_RAW_ARGS(args, data);
+
+       if (ast_strlen_zero(args.n) || ast_strlen_zero(args.name)) {
+               ast_log(LOG_ERROR, "LOCAL_PEEK requires parameters n and varname\n");
+               return -1;
+       }
+
        n = atoi(args.n);
        *buf = '\0';
 
@@ -822,6 +845,11 @@ static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data,
        data = ast_strdupa(data);
        AST_STANDARD_APP_ARGS(args, data);
 
+       if (ast_strlen_zero(args.n) || ast_strlen_zero(args.which)) {
+               ast_log(LOG_ERROR, "STACK_PEEK requires parameters n and which\n");
+               return -1;
+       }
+
        n = atoi(args.n);
        if (n <= 0) {
                ast_log(LOG_ERROR, "STACK_PEEK must be called with a positive peek value\n");
@@ -851,6 +879,7 @@ static int stackpeek_read(struct ast_channel *chan, const char *cmd, char *data,
                if (!ast_true(args.suppress)) {
                        ast_log(LOG_ERROR, "Stack peek of '%s' is more stack frames than I have\n", args.n);
                }
+               AST_LIST_UNLOCK(oldlist);
                ast_channel_unlock(chan);
                return -1;
        }
@@ -944,6 +973,7 @@ static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_
        int saved_priority;
        int saved_hangup_flags;
        int saved_autoloopflag;
+       int saved_in_subroutine;
        int res;
 
        ast_channel_lock(chan);
@@ -953,10 +983,9 @@ static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_
 
        /* Save non-hangup softhangup flags. */
        saved_hangup_flags = ast_channel_softhangup_internal_flag(chan)
-               & (AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
+               & AST_SOFTHANGUP_ASYNCGOTO;
        if (saved_hangup_flags) {
-               ast_channel_clear_softhangup(chan,
-                       AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE);
+               ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_ASYNCGOTO);
        }
 
        /* Save autoloop flag */
@@ -968,6 +997,9 @@ static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_
        saved_exten = ast_strdupa(ast_channel_exten(chan));
        saved_priority = ast_channel_priority(chan);
 
+       /* Save whether or not we are in a subroutine */
+       saved_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
+
        ast_debug(4, "%s Original location: %s,%s,%d\n", ast_channel_name(chan),
                saved_context, saved_exten, saved_priority);
 
@@ -1005,10 +1037,6 @@ static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_
                 */
                do {
                        /* Check for hangup. */
-                       if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_UNBRIDGE) {
-                               saved_hangup_flags |= AST_SOFTHANGUP_UNBRIDGE;
-                               ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_UNBRIDGE);
-                       }
                        if (ast_check_hangup(chan)) {
                                if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
                                        ast_log(LOG_ERROR, "%s An async goto just messed up our execution location.\n",
@@ -1073,6 +1101,9 @@ static int gosub_run(struct ast_channel *chan, const char *sub_args, int ignore_
        /* Restore autoloop flag */
        ast_set2_flag(ast_channel_flags(chan), saved_autoloopflag, AST_FLAG_IN_AUTOLOOP);
 
+       /* Restore subroutine flag */
+       ast_set2_flag(ast_channel_flags(chan), saved_in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
+
        /* Restore non-hangup softhangup flags. */
        if (saved_hangup_flags) {
                ast_softhangup_nolock(chan, saved_hangup_flags);
@@ -1088,6 +1119,7 @@ static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char
        int res;
        int priority;
        int old_autoloopflag;
+       int old_in_subroutine;
        int old_priority;
        const char *old_context;
        const char *old_extension;
@@ -1136,6 +1168,9 @@ static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char
        old_autoloopflag = ast_test_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
        ast_set_flag(ast_channel_flags(chan), AST_FLAG_IN_AUTOLOOP);
 
+       /* Save subroutine flag */
+       old_in_subroutine = ast_test_flag(ast_channel_flags(chan), AST_FLAG_SUBROUTINE_EXEC);
+
        /* Save previous location, since we're going to change it */
        old_context = ast_strdupa(ast_channel_context(chan));
        old_extension = ast_strdupa(ast_channel_exten(chan));
@@ -1214,8 +1249,7 @@ static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char
                ast_agi_send(agi->fd, chan, "200 result=%d Gosub failed\n", res);
        }
 
-       /* Must use free because the memory was allocated by asprintf(). */
-       free(gosub_args);
+       ast_free(gosub_args);
 
        ast_channel_lock(chan);
        ast_debug(4, "%s Ending location: %s,%s,%d\n", ast_channel_name(chan),
@@ -1229,6 +1263,9 @@ static int handle_gosub(struct ast_channel *chan, AGI *agi, int argc, const char
 
        /* Restore autoloop flag */
        ast_set2_flag(ast_channel_flags(chan), old_autoloopflag, AST_FLAG_IN_AUTOLOOP);
+
+       /* Restore subroutine flag */
+       ast_set2_flag(ast_channel_flags(chan), old_in_subroutine, AST_FLAG_SUBROUTINE_EXEC);
        ast_channel_unlock(chan);
 
        return RESULT_SUCCESS;
@@ -1241,7 +1278,7 @@ static int unload_module(void)
 {
        ast_install_stack_functions(NULL);
 
-       ast_agi_unregister(ast_module_info->self, &gosub_agi_command);
+       ast_agi_unregister(&gosub_agi_command);
 
        ast_unregister_application(app_return);
        ast_unregister_application(app_pop);
@@ -1279,8 +1316,9 @@ static int load_module(void)
 }
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT | AST_MODFLAG_LOAD_ORDER, "Dialplan subroutines (Gosub, Return, etc)",
-               .load = load_module,
-               .unload = unload_module,
-               .load_pri = AST_MODPRI_APP_DEPEND,
-               .nonoptreq = "res_agi",
-               );
+       .support_level = AST_MODULE_SUPPORT_CORE,
+       .load = load_module,
+       .unload = unload_module,
+       .load_pri = AST_MODPRI_APP_DEPEND,
+       .optional_modules = "res_agi",
+);