Merge "AST-2018-005: Fix tdata leaks when calling pjsip_endpt_send_response(2)"
[asterisk/asterisk.git] / apps / app_bridgewait.c
index bdfd87e..b17cddf 100644 (file)
@@ -34,8 +34,6 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
 #include "asterisk/pbx.h"
@@ -48,6 +46,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/bridge.h"
 #include "asterisk/musiconhold.h"
 #include "asterisk/astobj2.h"
+#include "asterisk/causes.h"
 
 /*** DOCUMENTATION
        <application name="BridgeWait" language="en_US">
@@ -127,8 +126,9 @@ struct wait_bridge_wrapper {
 static void wait_bridge_wrapper_destructor(void *obj)
 {
        struct wait_bridge_wrapper *wrapper = obj;
+
        if (wrapper->bridge) {
-               ast_bridge_destroy(wrapper->bridge);
+               ast_bridge_destroy(wrapper->bridge, 0);
        }
 }
 
@@ -201,10 +201,11 @@ AST_APP_OPTIONS(bridgewait_opts, {
        AST_APP_OPTION_ARG('S', MUXFLAG_TIMEOUT, OPT_ARG_TIMEOUT),
 });
 
-static int bridgewait_timeout_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+static int bridgewait_timeout_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 {
        ast_verb(3, "Channel %s timed out.\n", ast_channel_name(bridge_channel->chan));
-       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END);
+       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END,
+               AST_CAUSE_NORMAL_CLEARING);
        return -1;
 }
 
@@ -223,7 +224,7 @@ static int apply_option_timeout(struct ast_bridge_features *features, char *dura
        }
 
        duration *= 1000;
-       if (ast_bridge_interval_hook(features, duration, bridgewait_timeout_callback,
+       if (ast_bridge_interval_hook(features, 0, duration, bridgewait_timeout_callback,
                NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
                ast_log(LOG_ERROR, "Timeout option 'S': Could not create timer.\n");
                return -1;
@@ -321,9 +322,8 @@ static struct wait_bridge_wrapper *wait_bridge_wrapper_alloc(const char *bridge_
 
        bridge_wrapper = ao2_alloc_options(sizeof(*bridge_wrapper) + strlen(bridge_name) + 1,
                wait_bridge_wrapper_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
-
        if (!bridge_wrapper) {
-               ast_bridge_destroy(bridge);
+               ast_bridge_destroy(bridge, 0);
                return NULL;
        }
 
@@ -349,10 +349,16 @@ static struct wait_bridge_wrapper *get_wait_bridge_wrapper(const char *bridge_na
                return wrapper;
        }
 
+       /*
+        * Holding bridges can allow local channel move/swap
+        * optimization to the bridge.  However, we cannot allow it for
+        * this holding bridge because the call will lose the channel
+        * roles and dialplan location as a result.
+        */
        bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_HOLDING,
                AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
-               | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED
-               | AST_BRIDGE_FLAG_DISSOLVE_EMPTY);
+               | AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM
+               | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED, APP_NAME, bridge_name, NULL);
 
        if (!bridge) {
                return NULL;
@@ -413,9 +419,10 @@ static int bridgewait_exec(struct ast_channel *chan, const char *data)
        struct ast_bridge_features chan_features;
        struct ast_flags flags = { 0 };
        char *parse;
-       int bridge_join_failed = 0;
        enum wait_bridge_roles role = ROLE_PARTICIPANT;
        char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
+       struct wait_bridge_wrapper *bridge_wrapper;
+       int res;
 
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(name);
@@ -460,36 +467,26 @@ static int bridgewait_exec(struct ast_channel *chan, const char *data)
                return -1;
        }
 
-       for (;;) {
-               RAII_VAR(struct wait_bridge_wrapper *, bridge_wrapper, get_wait_bridge_wrapper(bridge_name), wait_wrapper_removal);
-
-               if (!bridge_wrapper) {
-                       ast_log(LOG_WARNING, "Failed to find or create waiting bridge '%s' for '%s'.\n", bridge_name, ast_channel_name(chan));
-                       bridge_join_failed = 1;
-                       break;
-               }
-
-               ast_verb(3, "%s is entering waiting bridge %s:%s\n", ast_channel_name(chan), bridge_name, bridge_wrapper->bridge->uniqueid);
-
-               if (ast_bridge_join(bridge_wrapper->bridge, chan, NULL, &chan_features, NULL, 0)) {
-                       /* It's possible for a holding bridge to vanish out from under us since we can't lock it.
-                        * Unlink the wrapper and then loop if the bridge we try to enter is dissolved. */
-                       ast_verb(3, "Waiting bridge '%s:%s' is no longer joinable. Creating new bridge and trying again.\n",
-                               bridge_name, bridge_wrapper->bridge->uniqueid);
-                       ao2_unlink(wait_bridge_wrappers, bridge_wrapper);
-                       continue;
-               }
-
-               break;
+       bridge_wrapper = get_wait_bridge_wrapper(bridge_name);
+       if (!bridge_wrapper) {
+               ast_log(LOG_WARNING, "Failed to find or create waiting bridge '%s' for '%s'.\n", bridge_name, ast_channel_name(chan));
+               ast_bridge_features_cleanup(&chan_features);
+               return -1;
        }
 
+       ast_verb(3, "%s is entering waiting bridge %s:%s\n", ast_channel_name(chan), bridge_name, bridge_wrapper->bridge->uniqueid);
+       res = ast_bridge_join(bridge_wrapper->bridge, chan, NULL, &chan_features, NULL, 0);
+       wait_wrapper_removal(bridge_wrapper);
        ast_bridge_features_cleanup(&chan_features);
 
-       if (bridge_join_failed) {
-               return -1;
+       if (res) {
+               /* For the lifetime of the bridge wrapper the bridge itself will be valid, if an error occurs it is because
+                * of extreme situations.
+                */
+               ast_log(LOG_WARNING, "Failed to join waiting bridge '%s' for '%s'.\n", bridge_name, ast_channel_name(chan));
        }
 
-       return ast_check_hangup_locked(chan) ? -1 : 0;
+       return (res || ast_check_hangup_locked(chan)) ? -1 : 0;
 }
 
 static int unload_module(void)