Restore Dial, Queue, and FollowMe 'I' option support.
[asterisk/asterisk.git] / res / parking / parking_bridge_features.c
index 6462dc5..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);
 
-       /* Since neither of the above cases were used, we are doing a simple park with a two party bridge. */
+       if (peer_count < 2) {
+               /* There is nothing to do if there is no one to park. */
+               return -1;
+       }
 
-       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;
+       /* 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;
+
+               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_common_setup(bridge_channel->chan, parker, app_data, NULL))) {
+       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,35 +394,85 @@ 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;
-       char *peername;
+       struct ast_context *park_dial_context;
+       const char *dial_string;
+       char *dial_string_flat;
        char parking_space[AST_MAX_EXTENSION];
 
+       char returnexten[AST_MAX_EXTENSION];
+       char *duplicate_returnexten;
+       struct ast_exten *existing_exten;
+       struct pbx_find_info pbx_finder = { .stacklen = 0 }; /* The rest is reset in pbx_find_extension */
+
+
        /* We are still in the bridge, so it's possible for other stuff to mess with the parked call before we leave the bridge
           to deal with this, lock the parked user, check and set resolution. */
        ao2_lock(user);
@@ -393,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);
@@ -401,12 +494,67 @@ static int parking_duration_callback(struct ast_bridge *bridge, struct ast_bridg
        pbx_builtin_setvar_helper(chan, "PARKINGSLOT", parking_space); /* Deprecated version of PARKING_SPACE */
        pbx_builtin_setvar_helper(chan, "PARKEDLOT", user->lot->name);
 
-       peername = ast_strdupa(user->parker->name);
-       flatten_peername(peername);
+       dial_string = user->parker_dial_string;
+       dial_string_flat = ast_strdupa(dial_string);
+       flatten_dial_string(dial_string_flat);
+
+       pbx_builtin_setvar_helper(chan, "PARKER", dial_string);
+       pbx_builtin_setvar_helper(chan, "PARKER_FLAT", dial_string_flat);
 
-       pbx_builtin_setvar_helper(chan, "PARKER", peername);
+       /* Dialplan generation for park-dial extensions */
 
-       /* TODO Dialplan generation for park-dial extensions */
+       if (ast_wrlock_contexts()) {
+               ast_log(LOG_ERROR, "Failed to lock the contexts list. Can't add the park-dial extension.\n");
+               return -1;
+       }
+
+       if (!(park_dial_context = ast_context_find_or_create(NULL, NULL, PARK_DIAL_CONTEXT, BASE_REGISTRAR))) {
+               ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", PARK_DIAL_CONTEXT);
+               if (ast_unlock_contexts()) {
+                       ast_assert(0);
+               }
+               goto abandon_extension_creation;
+       }
+
+       if (ast_wrlock_context(park_dial_context)) {
+               ast_log(LOG_ERROR, "failed to obtain write lock on context '%s'\n", PARK_DIAL_CONTEXT);
+               if (ast_unlock_contexts()) {
+                       ast_assert(0);
+               }
+               goto abandon_extension_creation;
+       }
+
+       if (ast_unlock_contexts()) {
+               ast_assert(0);
+       }
+
+       snprintf(returnexten, sizeof(returnexten), "%s,%u", dial_string,
+               user->lot->cfg->comebackdialtime);
+
+       duplicate_returnexten = ast_strdup(returnexten);
+
+       if (!duplicate_returnexten) {
+               ast_log(LOG_ERROR, "Failed to create parking redial parker extension %s@%s - Dial(%s)\n",
+                       dial_string_flat, PARK_DIAL_CONTEXT, returnexten);
+       }
+
+       /* If an extension already exists here because we registered it for another parked call timing out, then we may overwrite it. */
+       if ((existing_exten = pbx_find_extension(NULL, NULL, &pbx_finder, PARK_DIAL_CONTEXT, dial_string_flat, 1, NULL, NULL, E_MATCH)) &&
+           (strcmp(ast_get_extension_registrar(existing_exten), BASE_REGISTRAR))) {
+               ast_debug(3, "An extension for '%s@%s' was already registered by another registrar '%s'\n",
+                       dial_string_flat, PARK_DIAL_CONTEXT, ast_get_extension_registrar(existing_exten));
+       } else if (ast_add_extension2_nolock(park_dial_context, 1, dial_string_flat, 1, NULL, NULL,
+                       "Dial", duplicate_returnexten, ast_free_ptr, BASE_REGISTRAR)) {
+                       ast_free(duplicate_returnexten);
+               ast_log(LOG_ERROR, "Failed to create parking redial parker extension %s@%s - Dial(%s)\n",
+                       dial_string_flat, PARK_DIAL_CONTEXT, returnexten);
+       }
+
+       if (ast_unlock_context(park_dial_context)) {
+               ast_assert(0);
+       }
+
+abandon_extension_creation:
 
        /* async_goto the proper PBX destination - this should happen when we come out of the bridge */
        if (!ast_strlen_zero(user->comeback)) {
@@ -426,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);
        }
 }
 
@@ -457,23 +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;
 }