summaryrefslogtreecommitdiff
path: root/res/parking
diff options
context:
space:
mode:
authorMatthew Jordan <mjordan@digium.com>2013-08-01 20:55:17 +0000
committerMatthew Jordan <mjordan@digium.com>2013-08-01 20:55:17 +0000
commit5c4b482471edb8a839d754dd654dc72605caac66 (patch)
treed164a3ebd03e7101a39929283b75d2318a2f6761 /res/parking
parentc8a91b5b013c568f3f99e8fb7abc662775c60520 (diff)
Support externally initiated parking requests; remove some dead code
This patch does the following: * It adds support for externally initiated parking requests. In particular, chan_skinny has a protocol level message that initiates a call park. This patch now supports that option, as well as the protocol specific mechanisms in chan_dahdi/sig_analog and chan_mgcp. * A parking bridge features virtual table has been added that provides access to the parking functionality that the Bridging API needs. This includes requests to park an entire 'call' (with little or no additional information, thank you chan_skinny), perform a blind transfer to a parking extension, determine if an extension is a parking extension, as well as the actual "do the parking" request from the Bridging API. * Refactoring in chan_mgcp, chan_skinny, and chan_dahdi to make use of the new functions * The removal of some - but not all - dead parking code from features.c This also fixed blind transferring a multi-party bridge to a parking lot (which was implemented, but had at least one code path where using the parking features kK might not have worked) Review: https://reviewboard.asterisk.org/r/2710 (closes issue ASTERISK-22134) Reported by: Matt Jordan git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396028 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res/parking')
-rw-r--r--res/parking/parking_bridge_features.c214
1 files changed, 157 insertions, 57 deletions
diff --git a/res/parking/parking_bridge_features.c b/res/parking/parking_bridge_features.c
index e44a7695b..06987dcee 100644
--- a/res/parking/parking_bridge_features.c
+++ b/res/parking/parking_bridge_features.c
@@ -186,7 +186,7 @@ static int create_parked_subscription(struct ast_channel *chan, const char *park
* 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];
@@ -242,78 +242,121 @@ static struct ast_channel *park_local_transfer(struct ast_channel *parker, const
return parkee;
}
-static int park_feature_helper(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;
+
+ 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;
+ }
+
+ app_at_exten = ast_get_extension_app(exten_obj);
+ if (!app_at_exten || strcasecmp(PARK_APPLICATION, app_at_exten)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/*!
+ * \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;
+
+ if (ast_strlen_zero(context) || ast_strlen_zero(exten)) {
+ return -1;
+ }
+
+ if (!bridge_channel->in_bridge) {
+ return -1;
+ }
+
+ if (!parking_is_exten_park(context, exten)) {
+ return -1;
+ }
ast_bridge_channel_lock_bridge(bridge_channel);
- bridge_peers = ast_bridge_peers_nolock(bridge_channel->bridge);
+ 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 (ao2_container_count(bridge_peers) < 2) {
+ if (peer_count < 2) {
/* There is nothing to do if there is no one to park. */
- return 0;
+ return -1;
}
- 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. */
+ /* 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;
- 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;
- }
-
- transfer_chan = park_local_transfer(bridge_channel->chan,
- ast_get_extension_name(park_exten), ast_get_context_name(ast_get_extension_context(park_exten)));
-
+ transfer_chan = park_local_transfer(bridge_channel->chan, context, exten);
if (!transfer_chan) {
- return 0;
+ return -1;
}
if (ast_bridge_impart(bridge_channel->bridge, transfer_chan, NULL, NULL, 1)) {
ast_hangup(transfer_chan);
+ return -1;
}
-
return 0;
}
- /* Since neither of the above cases were used, we are doing a simple park with a two party bridge. */
-
- 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;
- }
- }
- ao2_iterator_destroy(&iter);
-
- if (!other) {
- ast_assert(0);
- return -1;
- }
-
/* 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);
@@ -321,7 +364,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);
@@ -329,12 +372,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));
@@ -346,7 +389,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 */
@@ -356,13 +399,60 @@ 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;
+}
+
+/*!
+ * \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)
+{
+ 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 int feature_park(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+static int feature_park_call(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
- park_feature_helper(bridge_channel, NULL);
- return 0;
+ return parking_park_call(bridge_channel, NULL, 0);
}
/*! \internal
@@ -524,17 +614,27 @@ void parking_set_duration(struct ast_bridge_features *features, struct parked_us
}
}
+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);
+ if (ast_parking_register_bridge_features(&parking_provider)) {
+ return -1;
+ }
+
+ ast_bridge_features_register(AST_BRIDGE_BUILTIN_PARKCALL, feature_park_call, NULL);
return 0;
}