Restore Dial, Queue, and FollowMe 'I' option support.
[asterisk/asterisk.git] / res / parking / parking_bridge_features.c
index aee4edb..0e5e05d 100644 (file)
@@ -32,12 +32,17 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/astobj2.h"
 #include "asterisk/logger.h"
 #include "asterisk/pbx.h"
-#include "asterisk/bridging.h"
-#include "asterisk/bridging_features.h"
+#include "asterisk/bridge.h"
+#include "asterisk/bridge_internal.h"
+#include "asterisk/bridge_channel.h"
+#include "asterisk/bridge_features.h"
 #include "asterisk/features.h"
 #include "asterisk/say.h"
 #include "asterisk/datastore.h"
 #include "asterisk/stasis.h"
+#include "asterisk/module.h"
+#include "asterisk/core_local.h"
+#include "asterisk/causes.h"
 
 struct parked_subscription_datastore {
        struct stasis_subscription *parked_subscription;
@@ -181,20 +186,16 @@ static int create_parked_subscription(struct ast_channel *chan, const char *park
 /*!
  * \internal
  * \brief Helper function that creates an outgoing channel and returns it immediately. This function is nearly
- *        identical to the dial_transfer function in bridge_builtin_features.c, however it doesn't swap the
+ *        identical to the dial_transfer function in bridge_basic.c, however it doesn't swap the
  *        local channel and the channel that instigated the park.
  */
-static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *exten, const char *context)
+static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten)
 {
-       RAII_VAR(struct ast_channel *, parkee_side_2, NULL, ao2_cleanup);
        char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1];
        struct ast_channel *parkee;
+       struct ast_channel *parkee_side_2;
        int cause;
 
-       /* Used for side_2 hack */
-       char *parkee_name;
-       char *semi_pos;
-
        /* Fill the variable with the extension and context we want to call */
        snprintf(destination, sizeof(destination), "%s@%s", exten, context);
 
@@ -210,27 +211,19 @@ static struct ast_channel *park_local_transfer(struct ast_channel *parker, const
        ast_connected_line_copy_from_caller(ast_channel_connected(parkee), ast_channel_caller(parker));
        ast_channel_inherit_variables(parker, parkee);
        ast_channel_datastore_inherit(parker, parkee);
-       ast_channel_unlock(parkee);
        ast_channel_unlock(parker);
 
-       /* BUGBUG Use Richard's unreal channel stuff here instead of this hack */
-       parkee_name = ast_strdupa(ast_channel_name(parkee));
-
-       semi_pos = strrchr(parkee_name, ';');
-       if (!semi_pos) {
-               /* There should always be a semicolon present in the string if is used since it's a local channel. */
-               ast_assert(0);
-               return NULL;
-       }
-
-       parkee_name[(semi_pos - parkee_name) + 1] = '2';
-       parkee_side_2 = ast_channel_get_by_name(parkee_name);
+       parkee_side_2 = ast_local_get_peer(parkee);
+       ast_assert(parkee_side_2 != NULL);
+       ast_channel_unlock(parkee);
 
        /* We need to have the parker subscribe to the new local channel before hand. */
        create_parked_subscription(parker, ast_channel_uniqueid(parkee_side_2));
 
        pbx_builtin_setvar_helper(parkee_side_2, "BLINDTRANSFER", ast_channel_name(parker));
 
+       ast_channel_unref(parkee_side_2);
+
        /* Since the above worked fine now we actually call it and return the channel */
        if (ast_call(parkee, destination, 0)) {
                ast_hangup(parkee);
@@ -240,76 +233,125 @@ static struct ast_channel *park_local_transfer(struct ast_channel *parker, const
        return parkee;
 }
 
-static int park_feature_helper(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_exten *park_exten)
+/*!
+ * \internal
+ * \brief Determine if an extension is a parking extension
+ */
+static int parking_is_exten_park(const char *context, const char *exten)
 {
-       RAII_VAR(struct ast_channel *, other, NULL, ao2_cleanup);
-       RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
-       RAII_VAR(struct parked_user *, pu, NULL, ao2_cleanup);
-       RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
-       RAII_VAR(struct ao2_container *, bridge_peers, NULL, ao2_cleanup);
-       struct ao2_iterator iter;
+       struct ast_exten *exten_obj;
+       struct pbx_find_info info = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
+       const char *app_at_exten;
 
-       bridge_peers = ast_bridge_peers(bridge);
+       ast_debug(4, "Checking if %s@%s is a parking exten\n", exten, context);
+       exten_obj = pbx_find_extension(NULL, NULL, &info, context, exten, 1, NULL, NULL, E_MATCH);
+       if (!exten_obj) {
+               return 0;
+       }
 
-       if (ao2_container_count(bridge_peers) < 2) {
-               /* There is nothing to do if there is no one to park. */
+       app_at_exten = ast_get_extension_app(exten_obj);
+       if (!app_at_exten || strcasecmp(PARK_APPLICATION, app_at_exten)) {
                return 0;
        }
 
-       if (ao2_container_count(bridge_peers) > 2) {
-               /* With a multiparty bridge, we need to do a regular blind transfer. We link the existing bridge to the parking lot with a
-                * local channel rather than transferring others. */
-               struct ast_channel *transfer_chan = NULL;
+       return 1;
+}
 
-               if (!park_exten) {
-                       /* This simply doesn't work. The user attempted to one-touch park the parking lot and we can't originate a local channel
-                        * without knowing an extension to transfer it to.
-                        * XXX However, when parking lots are changed to be able to register extensions then this will be doable. */
-                       ast_log(LOG_ERROR, "Can not one-touch park a multiparty bridge.\n");
-                       return 0;
-               }
+/*!
+ * \internal
+ * \since 12.0.0
+ * \brief Perform a blind transfer to a parking lot
+ *
+ * In general, most parking features should work to call this function. This will safely
+ * park either a channel in the bridge with \ref bridge_channel or will park the entire
+ * bridge if more than one channel is in the bridge. It will create the correct data to
+ * pass to the \ref AstBridging Bridging API to safely park the channel.
+ *
+ * \param bridge_channel The bridge_channel representing the channel performing the park
+ * \param context The context to blind transfer to
+ * \param exten The extension to blind transfer to
+ *
+ * \retval 0 on success
+ * \retval non-zero on error
+ */
+static int parking_blind_transfer_park(struct ast_bridge_channel *bridge_channel,
+               const char *context, const char *exten)
+{
+       RAII_VAR(struct ast_bridge_channel *, other, NULL, ao2_cleanup);
+       int peer_count;
 
-               transfer_chan = park_local_transfer(bridge_channel->chan,
-                       ast_get_extension_name(park_exten), ast_get_context_name(ast_get_extension_context(park_exten)));
+       if (ast_strlen_zero(context) || ast_strlen_zero(exten)) {
+               return -1;
+       }
 
-               if (!transfer_chan) {
-                       return 0;
-               }
+       if (!bridge_channel->in_bridge) {
+               return -1;
+       }
 
-               if (ast_bridge_impart(bridge_channel->bridge, transfer_chan, NULL, NULL, 1)) {
-                       ast_hangup(transfer_chan);
-               }
+       if (!parking_is_exten_park(context, exten)) {
+               return -1;
+       }
 
-               return 0;
+       ast_bridge_channel_lock_bridge(bridge_channel);
+       peer_count = bridge_channel->bridge->num_channels;
+       if (peer_count == 2) {
+               other = ast_bridge_channel_peer(bridge_channel);
+               ao2_ref(other, +1);
+       }
+       ast_bridge_unlock(bridge_channel->bridge);
+
+       if (peer_count < 2) {
+               /* There is nothing to do if there is no one to park. */
+               return -1;
        }
 
-       /* Since neither of the above cases were used, we are doing a simple park with a two party bridge. */
+       /* With a multiparty bridge, we need to do a regular blind transfer. We link the
+        * existing bridge to the parking lot with a Local channel rather than
+        * transferring others. */
+       if (peer_count > 2) {
+               struct ast_channel *transfer_chan = NULL;
 
-       for (iter = ao2_iterator_init(bridge_peers, 0); (other = ao2_iterator_next(&iter)); ao2_ref(other, -1)) {
-               /* We need the channel that isn't the bridge_channel's channel. */
-               if (strcmp(ast_channel_uniqueid(other), ast_channel_uniqueid(bridge_channel->chan))) {
-                       break;
+               transfer_chan = park_local_transfer(bridge_channel->chan, context, exten);
+               if (!transfer_chan) {
+                       return -1;
                }
-       }
-       ao2_iterator_destroy(&iter);
 
-       if (!other) {
-               ast_assert(0);
-               return -1;
+               if (ast_bridge_impart(bridge_channel->bridge, transfer_chan, NULL, NULL,
+                       AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
+                       ast_hangup(transfer_chan);
+                       return -1;
+               }
+               return 0;
        }
 
        /* Subscribe to park messages with the other channel entering */
-       if (create_parked_subscription(bridge_channel->chan, ast_channel_uniqueid(other))) {
+       if (create_parked_subscription(bridge_channel->chan, ast_channel_uniqueid(other->chan))) {
                return -1;
        }
 
        /* Write the park frame with the intended recipient and other data out to the bridge. */
-       ast_bridge_channel_write_park(bridge_channel, ast_channel_uniqueid(other), ast_channel_uniqueid(bridge_channel->chan), ast_get_extension_app_data(park_exten));
+       ast_bridge_channel_write_park(bridge_channel,
+               ast_channel_uniqueid(other->chan),
+               ast_channel_uniqueid(bridge_channel->chan),
+               NULL);
 
        return 0;
 }
 
-static void park_bridge_channel(struct ast_bridge_channel *bridge_channel, const char *uuid_parkee, const char *uuid_parker, const char *app_data)
+
+/*!
+ * \internal
+ * \since 12.0.0
+ * \brief Perform a direct park on a channel in a bridge
+ *
+ * \note This will be called from within the \ref AstBridging Bridging API
+ *
+ * \param bridge_channel The bridge_channel representing the channel to be parked
+ * \param uuid_parkee The UUID of the channel being parked
+ * \param uuid_parker The UUID of the channel performing the park
+ * \param app_data Application parseable data to pass to the parking application
+ */
+static int parking_park_bridge_channel(struct ast_bridge_channel *bridge_channel, const char *uuid_parkee, const char *uuid_parker, const char *app_data)
 {
        RAII_VAR(struct ast_bridge *, parking_bridge, NULL, ao2_cleanup);
        RAII_VAR(struct ast_bridge *, original_bridge, NULL, ao2_cleanup);
@@ -317,7 +359,7 @@ static void park_bridge_channel(struct ast_bridge_channel *bridge_channel, const
 
        if (strcmp(ast_channel_uniqueid(bridge_channel->chan), uuid_parkee)) {
                /* We aren't the parkee, so ignore this action. */
-               return;
+               return -1;
        }
 
        parker = ast_channel_get_by_name(uuid_parker);
@@ -325,12 +367,12 @@ static void park_bridge_channel(struct ast_bridge_channel *bridge_channel, const
        if (!parker) {
                ast_log(LOG_NOTICE, "Channel with uuid %s left before we could start parking the call. Parking canceled.\n", uuid_parker);
                publish_parked_call_failure(bridge_channel->chan);
-               return;
+               return -1;
        }
 
        if (!(parking_bridge = park_application_setup(bridge_channel->chan, parker, app_data, NULL))) {
                publish_parked_call_failure(bridge_channel->chan);
-               return;
+               return -1;
        }
 
        pbx_builtin_setvar_helper(bridge_channel->chan, "BLINDTRANSFER", ast_channel_name(parker));
@@ -342,7 +384,7 @@ static void park_bridge_channel(struct ast_bridge_channel *bridge_channel, const
        if (!original_bridge) {
                ao2_unlock(bridge_channel);
                publish_parked_call_failure(bridge_channel->chan);
-               return;
+               return -1;
        }
 
        ao2_ref(original_bridge, +1); /* Cleaned by RAII_VAR */
@@ -352,29 +394,71 @@ static void park_bridge_channel(struct ast_bridge_channel *bridge_channel, const
        if (ast_bridge_move(parking_bridge, original_bridge, bridge_channel->chan, NULL, 1)) {
                ast_log(LOG_ERROR, "Failed to move %s into the parking bridge.\n",
                        ast_channel_name(bridge_channel->chan));
+               return -1;
        }
+
+       return 0;
 }
 
-static int feature_park(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+/*!
+ * \internal
+ * \since 12.0.0
+ * \brief Park a call
+ *
+ * \param parker The bridge_channel parking the call
+ * \param exten Optional. The extension where the call was parked.
+ * \param length Optional. If \c exten is specified, the length of the buffer.
+ *
+ * \note This will determine the context and extension to park the channel based on
+ * the configuration of the \ref ast_channel associated with \ref parker. It will then
+ * park either the channel or the entire bridge.
+ *
+ * \retval 0 on success
+ * \retval -1 on error
+ */
+static int parking_park_call(struct ast_bridge_channel *parker, char *exten, size_t length)
 {
-       park_feature_helper(bridge, bridge_channel, NULL);
-       return 0;
+       RAII_VAR(struct parking_lot *, lot, NULL, ao2_cleanup);
+       const char *lot_name = NULL;
+
+       ast_channel_lock(parker->chan);
+       lot_name = find_channel_parking_lot_name(parker->chan);
+       if (!ast_strlen_zero(lot_name)) {
+               lot_name = ast_strdupa(lot_name);
+       }
+       ast_channel_unlock(parker->chan);
+
+       if (ast_strlen_zero(lot_name)) {
+               return -1;
+       }
+
+       lot = parking_lot_find_by_name(lot_name);
+       if (!lot) {
+               ast_log(AST_LOG_WARNING, "Cannot Park %s: lot %s unknown\n",
+                       ast_channel_name(parker->chan), lot_name);
+               return -1;
+       }
+
+       if (exten) {
+               ast_copy_string(exten, lot->cfg->parkext, length);
+       }
+       return parking_blind_transfer_park(parker, lot->cfg->parking_con, lot->cfg->parkext);
 }
 
-static void parking_duration_cb_destroyer(void *hook_pvt)
+static int feature_park_call(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 {
-       struct parked_user *user = hook_pvt;
-       ao2_ref(user, -1);
+       SCOPED_MODULE_USE(parking_get_module_info()->self);
+
+       return parking_park_call(bridge_channel, NULL, 0);
 }
 
 /*! \internal
  * \brief Interval hook. Pulls a parked call from the parking bridge after the timeout is passed and sets the resolution to timeout.
  *
- * \param bridge Which bridge the channel was parked in
  * \param bridge_channel bridge channel this interval hook is being executed on
  * \param hook_pvt A pointer to the parked_user struct associated with the channel is stuffed in here
  */
-static int parking_duration_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+static int parking_duration_callback(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 {
        struct parked_user *user = hook_pvt;
        struct ast_channel *chan = user->chan;
@@ -401,7 +485,8 @@ static int parking_duration_callback(struct ast_bridge *bridge, struct ast_bridg
        user->resolution = PARK_TIMEOUT;
        ao2_unlock(user);
 
-       ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE,
+               AST_CAUSE_NORMAL_CLEARING);
 
        /* Set parking timeout channel variables */
        snprintf(parking_space, sizeof(parking_space), "%d", user->parking_space);
@@ -489,14 +574,17 @@ void say_parking_space(struct ast_bridge_channel *bridge_channel, const char *pa
        if (sscanf(payload, "%u %u", &hangup_after, &numeric_value) != 2) {
                /* If say_parking_space is called with a non-numeric string, we have a problem. */
                ast_assert(0);
-               ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+               ast_bridge_channel_leave_bridge(bridge_channel,
+                       BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING);
                return;
        }
 
-       ast_say_digits(bridge_channel->chan, numeric_value, "", ast_channel_language(bridge_channel->chan));
+       ast_say_digits(bridge_channel->chan, numeric_value, "",
+               ast_channel_language(bridge_channel->chan));
 
        if (hangup_after) {
-               ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+               ast_bridge_channel_leave_bridge(bridge_channel,
+                       BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING);
        }
 }
 
@@ -520,24 +608,39 @@ void parking_set_duration(struct ast_bridge_features *features, struct parked_us
        /* The interval hook is going to need a reference to the parked_user */
        ao2_ref(user, +1);
 
-       if (ast_bridge_interval_hook(features, time_limit,
-               parking_duration_callback, user, parking_duration_cb_destroyer, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
-               ast_log(LOG_ERROR, "Failed to apply duration limits to the parking call.\n");
+       if (ast_bridge_interval_hook(features, 0, time_limit,
+               parking_duration_callback, user, __ao2_cleanup, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
+               ast_log(LOG_ERROR, "Failed to apply duration limit to the parked call.\n");
                ao2_ref(user, -1);
        }
 }
 
+struct ast_parking_bridge_feature_fn_table parking_provider = {
+       .module_version = PARKING_MODULE_VERSION,
+       .module_name = __FILE__,
+       .parking_is_exten_park = parking_is_exten_park,
+       .parking_blind_transfer_park = parking_blind_transfer_park,
+       .parking_park_bridge_channel = parking_park_bridge_channel,
+       .parking_park_call = parking_park_call,
+};
+
 void unload_parking_bridge_features(void)
 {
        ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_PARKCALL);
-       ast_uninstall_park_blind_xfer_func();
-       ast_uninstall_bridge_channel_park_func();
+       ast_parking_unregister_bridge_features(parking_provider.module_name);
 }
 
 int load_parking_bridge_features(void)
 {
-       ast_bridge_features_register(AST_BRIDGE_BUILTIN_PARKCALL, feature_park, NULL);
-       ast_install_park_blind_xfer_func(park_feature_helper);
-       ast_install_bridge_channel_park_func(park_bridge_channel);
+       parking_provider.module_info = parking_get_module_info();
+
+       if (ast_parking_register_bridge_features(&parking_provider)) {
+               return -1;
+       }
+
+       if (ast_bridge_features_register(AST_BRIDGE_BUILTIN_PARKCALL, feature_park_call, NULL)) {
+               return -1;
+       }
+
        return 0;
 }