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
changes/78/78/1
Matthew Jordan 13 years ago
parent c8a91b5b01
commit 5c4b482471

@ -126,6 +126,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/features_config.h"
#include "asterisk/bridge.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/parking.h"
#include "chan_dahdi.h"
#include "dahdi/bridge_native_dahdi.h"
@ -9230,6 +9231,10 @@ static void *analog_ss_thread(void *data)
int idx;
struct ast_format tmpfmt;
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
int is_exten_parking;
const char *pickupexten;
ast_mutex_lock(&ss_thread_lock);
@ -9560,11 +9565,13 @@ static void *analog_ss_thread(void *data)
exten[len++]=res;
exten[len] = '\0';
}
if (!ast_ignore_pattern(ast_channel_context(chan), exten))
if (!ast_ignore_pattern(ast_channel_context(chan), exten)) {
tone_zone_play_tone(p->subs[idx].dfd, -1);
else
} else {
tone_zone_play_tone(p->subs[idx].dfd, DAHDI_TONE_DIALTONE);
if (ast_exists_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num) && !ast_parking_ext_valid(exten, chan, ast_channel_context(chan))) {
}
is_exten_parking = (parking_provider ? parking_provider->parking_is_exten_park(ast_channel_context(chan), exten) : 0);
if (ast_exists_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num) && !is_exten_parking) {
if (!res || !ast_matchmore_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num)) {
if (getforward) {
/* Record this as the forwarding extension */
@ -9700,14 +9707,17 @@ static void *analog_ss_thread(void *data)
getforward = 0;
memset(exten, 0, sizeof(exten));
len = 0;
} else if ((p->transfer || p->canpark) && ast_parking_ext_valid(exten, chan, ast_channel_context(chan)) &&
p->subs[SUB_THREEWAY].owner &&
ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
} else if ((p->transfer || p->canpark) && is_exten_parking &&
p->subs[SUB_THREEWAY].owner) {
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
/* This is a three way call, the main call being a real channel,
and we're parking the first call. */
ast_masq_park_call_exten(ast_bridged_channel(p->subs[SUB_THREEWAY].owner),
chan, exten, ast_channel_context(chan), 0, NULL);
ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan));
ast_channel_lock(chan);
bridge_channel = ast_channel_get_bridge_channel(chan);
ast_channel_unlock(chan);
if (bridge_channel && !parking_provider->parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten)) {
ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan));
}
break;
} else if (p->hidecallerid && !strcmp(exten, "*82")) {
ast_verb(3, "Enabling Caller*ID on %s\n", ast_channel_name(chan));

@ -9202,7 +9202,7 @@ static void dp_lookup(int callno, const char *context, const char *callednum, co
memset(&ied1, 0, sizeof(ied1));
mm = ast_matchmore_extension(NULL, context, callednum, 1, callerid);
/* Must be started */
if (ast_parking_ext_valid(callednum, NULL, context) || ast_exists_extension(NULL, context, callednum, 1, callerid)) {
if (ast_exists_extension(NULL, context, callednum, 1, callerid)) {
dpstatus = IAX_DPSTATUS_EXISTS;
} else if (ast_canmatch_extension(NULL, context, callednum, 1, callerid)) {
dpstatus = IAX_DPSTATUS_CANEXIST;

@ -84,6 +84,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/stasis.h"
#include "asterisk/bridge.h"
#include "asterisk/features_config.h"
#include "asterisk/parking.h"
/*
* Define to work around buggy dlink MGCP phone firmware which
@ -2980,6 +2981,9 @@ static void *mgcp_ss(void *data)
int getforward = 0;
int loop_pause = 100;
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
const char *pickupexten;
len = strlen(p->dtmf_buf);
@ -3148,13 +3152,17 @@ static void *mgcp_ss(void *data)
getforward = 0;
memset(p->dtmf_buf, 0, sizeof(p->dtmf_buf));
len = 0;
} else if (ast_parking_ext_valid(p->dtmf_buf, chan, ast_channel_context(chan)) &&
sub->next->owner && ast_bridged_channel(sub->next->owner)) {
} else if (parking_provider && parking_provider->parking_is_exten_park(ast_channel_context(chan), p->dtmf_buf) &&
sub->next->owner) {
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
/* This is a three way call, the main call being a real channel,
and we're parking the first call. */
ast_masq_park_call_exten(ast_bridged_channel(sub->next->owner), chan,
p->dtmf_buf, ast_channel_context(chan), 0, NULL);
ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan));
and we're parking the first call. */
ast_channel_lock(chan);
bridge_channel = ast_channel_get_bridge_channel(chan);
ast_channel_unlock(chan);
if (bridge_channel && !parking_provider->parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), p->dtmf_buf)) {
ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan));
}
break;
} else if (!ast_strlen_zero(p->lastcallerid) && !strcmp(p->dtmf_buf, "*60")) {
ast_verb(3, "Blacklisting number %s\n", p->lastcallerid);

@ -82,6 +82,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/linkedlists.h"
#include "asterisk/stasis_endpoints.h"
#include "asterisk/bridge.h"
#include "asterisk/parking.h"
/*** DOCUMENTATION
<manager name="SKINNYdevices" language="en_US">
@ -6404,24 +6405,37 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
break;
case STIMULUS_CALLPARK:
{
int extout;
char extout[AST_MAX_EXTENSION];
char message[32];
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
SKINNY_DEBUG(DEBUG_PACKET, 3, "Received STIMULUS_CALLPARK from %s, inst %d, callref %d\n",
d->name, instance, callreference);
if ((sub && sub->owner) && (ast_channel_state(sub->owner) == AST_STATE_UP)){
if (!parking_provider) {
transmit_displaynotify(d, "Call Park not available", 10);
break;
}
if ((sub && sub->owner) && (ast_channel_state(sub->owner) == AST_STATE_UP)) {
c = sub->owner;
if (ast_bridged_channel(c)) {
if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
snprintf(message, sizeof(message), "Call Parked at: %d", extout);
transmit_displaynotify(d, message, 10);
} else {
transmit_displaynotify(d, "Call Park failed", 10);
}
} else {
transmit_displaynotify(d, "Call Park not available", 10);
ast_channel_lock(c);
bridge_channel = ast_channel_get_bridge_channel(c);
ast_channel_unlock(c);
if (!bridge_channel) {
transmit_displaynotify(d, "Call Park failed", 10);
break;
}
if (!parking_provider->parking_park_call(bridge_channel, extout, sizeof(extout))) {
snprintf(message, sizeof(message), "Call Parked at: %s", extout);
transmit_displaynotify(d, message, 10);
break;
}
transmit_displaynotify(d, "Call Park failed", 10);
} else {
transmit_displaynotify(d, "Call Park not available", 10);
}
@ -7141,24 +7155,37 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
break;
case SOFTKEY_PARK:
{
int extout;
char extout[AST_MAX_EXTENSION];
char message[32];
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
SKINNY_DEBUG(DEBUG_PACKET, 3, "Received SOFTKEY_PARK from %s, inst %d, callref %d\n",
d->name, instance, callreference);
if ((sub && sub->owner) && (ast_channel_state(sub->owner) == AST_STATE_UP)){
if (!parking_provider) {
transmit_displaynotify(d, "Call Park not available", 10);
break;
}
if ((sub && sub->owner) && (ast_channel_state(sub->owner) == AST_STATE_UP)) {
c = sub->owner;
if (ast_bridged_channel(c)) {
if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
snprintf(message, sizeof(message), "Call Parked at: %d", extout);
transmit_displaynotify(d, message, 10);
} else {
transmit_displaynotify(d, "Call Park failed", 10);
}
} else {
transmit_displaynotify(d, "Call Park not available", 10);
ast_channel_lock(c);
bridge_channel = ast_channel_get_bridge_channel(c);
ast_channel_unlock(c);
if (!bridge_channel) {
transmit_displaynotify(d, "Call Park failed", 10);
break;
}
if (!parking_provider->parking_park_call(bridge_channel, extout, sizeof(extout))) {
snprintf(message, sizeof(message), "Call Parked at: %s", extout);
transmit_displaynotify(d, message, 10);
break;
}
transmit_displaynotify(d, "Call Park failed", 10);
} else {
transmit_displaynotify(d, "Call Park not available", 10);
}

@ -44,6 +44,7 @@
#include "asterisk/causes.h"
#include "asterisk/features_config.h"
#include "asterisk/bridge.h"
#include "asterisk/parking.h"
#include "sig_analog.h"
@ -1713,7 +1714,11 @@ static void *__analog_ss_thread(void *data)
int idx;
struct ast_callid *callid;
RAII_VAR(struct ast_features_pickup_config *, pickup_cfg, NULL, ao2_cleanup);
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
const char *pickupexten;
int is_exten_parking;
analog_increase_ss_count();
@ -2094,7 +2099,8 @@ static void *__analog_ss_thread(void *data)
} else {
analog_play_tone(p, idx, ANALOG_TONE_DIALTONE);
}
if (ast_exists_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num) && !ast_parking_ext_valid(exten, chan, ast_channel_context(chan))) {
is_exten_parking = (parking_provider ? parking_provider->parking_is_exten_park(ast_channel_context(chan), exten) : 0);
if (ast_exists_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num) && !is_exten_parking) {
if (!res || !ast_matchmore_extension(chan, ast_channel_context(chan), exten, 1, p->cid_num)) {
if (getforward) {
/* Record this as the forwarding extension */
@ -2238,15 +2244,18 @@ static void *__analog_ss_thread(void *data)
getforward = 0;
memset(exten, 0, sizeof(exten));
len = 0;
} else if ((p->transfer || p->canpark) && ast_parking_ext_valid(exten, chan, ast_channel_context(chan)) &&
p->subs[ANALOG_SUB_THREEWAY].owner &&
ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) {
} else if ((p->transfer || p->canpark) && is_exten_parking &&
p->subs[ANALOG_SUB_THREEWAY].owner) {
struct ast_bridge_channel *bridge_channel;
/* This is a three way call, the main call being a real channel,
and we're parking the first call. */
ast_masq_park_call_exten(
ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner), chan, exten,
ast_channel_context(chan), 0, NULL);
ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan));
ast_channel_lock(chan);
bridge_channel = ast_channel_get_bridge_channel(chan);
ast_channel_unlock(chan);
if (bridge_channel && !parking_provider->parking_blind_transfer_park(bridge_channel, ast_channel_context(chan), exten)) {
ast_verb(3, "Parking call to '%s'\n", ast_channel_name(chan));
}
ao2_ref(bridge_channel, -1);
break;
} else if (!ast_strlen_zero(p->lastcid_num) && !strcmp(exten, "*60")) {
ast_verb(3, "Blacklisting number %s\n", p->lastcid_num);

@ -63,50 +63,6 @@ enum {
AST_FEATURE_FLAG_BYBOTH = (3 << 3),
};
/*!
* \brief Park a call via a masqueraded channel
*
* \param park_me Channel to be parked.
* \param parker Channel parking the call.
* \param timeout is a timeout in milliseconds
* \param extout is a parameter to an int that will hold the parked location, or NULL if you want.
*
* \details
* Masquerade the park_me channel into a new, empty channel which is then parked.
*
* \note Use ast_masq_park_call_exten() instead.
*
* \retval 0 on success.
* \retval -1 on failure.
*/
int ast_masq_park_call(struct ast_channel *park_me, struct ast_channel *parker, int timeout, int *extout);
/*!
* \brief Park a call via a masqueraded channel
* \since 1.8.9
*
* \param park_me Channel to be parked.
* \param parker Channel parking the call.
* \param park_exten Parking lot access extension
* \param park_context Parking lot context
* \param timeout is a timeout in milliseconds
* \param extout is a parameter to an int that will hold the parked location, or NULL if you want.
*
* \details
* Masquerade the park_me channel into a new, empty channel which is then parked.
*
* \retval 0 on success.
* \retval -1 on failure.
*/
int ast_masq_park_call_exten(struct ast_channel *park_me, struct ast_channel *parker, const char *park_exten, const char *park_context, int timeout, int *extout);
/*!
* \brief Determine if parking extension exists in a given context
* \retval 0 if extension does not exist
* \retval 1 if extension does exist
*/
int ast_parking_ext_valid(const char *exten_str, struct ast_channel *chan, const char *context);
/*! \brief Bridge a call, optionally allowing redirection */
int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config);

@ -25,6 +25,9 @@
#include "asterisk/stringfields.h"
/*!
* \brief The default parking application that Asterisk expects.
*/
#define PARK_APPLICATION "Park"
/*!
@ -79,6 +82,10 @@ struct ast_parked_call_payload *ast_parked_call_payload_create(enum ast_parked_c
struct ast_channel_snapshot *retriever_snapshot, const char *parkinglot,
unsigned int parkingspace, unsigned long int timeout, unsigned long int duration);
/*! \addtogroup StasisTopicsAndMessages
* @{
*/
/*!
* \brief accessor for the parking stasis topic
* \since 12
@ -97,75 +104,110 @@ struct stasis_topic *ast_parking_topic(void);
*/
struct stasis_message_type *ast_parked_call_type(void);
/*!
* \brief invoke an installable park callback to asynchronously park a bridge_channel in a bridge
* \since 12
*
* \param bridge_channel the bridge channel that initiated parking
* \parkee_uuid channel id of the channel being parked
* \parker_uuid channel id of the channel that initiated parking
* \param app_data string of application data that might be applied to parking
*/
void ast_bridge_channel_park(struct ast_bridge_channel *bridge_channel,
const char *parkee_uuid,
const char *parker_uuid,
const char *app_data);
/*! @} */
typedef int (*ast_park_blind_xfer_fn)(struct ast_bridge_channel *parker, struct ast_exten *park_exten);
#define PARKING_MODULE_VERSION 1
/*!
* \brief install a callback for handling blind transfers to a parking extension
* \since 12
*
* \param parking_func Function to use for transfers to 'Park' applications
* \brief A function table providing parking functionality to the \ref AstBridging
* Bridging API and other consumers
*/
void ast_install_park_blind_xfer_func(ast_park_blind_xfer_fn park_blind_xfer_func);
struct ast_parking_bridge_feature_fn_table {
/*!
* \brief The version of this function table. If the ABI for this table
* changes, the module version (/ref PARKING_MODULE_VERSION) should be
* incremented.
*/
unsigned int module_version;
/*!
* \brief The name of the module that provides this parking functionality
*/
const char *module_name;
/*!
* \brief Determine if the context/exten is a "parking" extension
*
* \retval 0 if the extension is not a parking extension
* \retval 1 if the extension is a parking extension
*/
int (* parking_is_exten_park)(const char *context, const char *exten);
/*!
* \brief Park the bridge and/or callers that this channel is in
*
* \param parker The bridge_channel parking the bridge
* \param exten Optional. The extension the channel or bridge was parked at if the
* call succeeds.
* \param length Optional. If \c exten is specified, the size of the buffer.
*
* \note This is safe to be called outside of the \ref AstBridging Bridging API.
*
* \retval 0 on success
* \retval non-zero on error
*/
int (* parking_park_call)(struct ast_bridge_channel *parker, char *exten, size_t length);
/*!
* \brief Perform a blind transfer to a parking extension.
*
* \param parker The \ref bridge_channel object that is initiating the parking
* \param context The context to blind transfer to
* \param exten The extension to blind transfer to
*
* \note If the bridge \ref parker is in has more than one other occupant, the entire
* bridge will be parked using a Local channel
*
* \note This is safe to be called outside of the \ref AstBridging Bridging API.
*
* \retval 0 on success
* \retval non-zero on error
*/
int (* parking_blind_transfer_park)(struct ast_bridge_channel *parker, const char *context, const char *exten);
/*!
* \brief Perform a direct park on a channel in a bridge.
*
* \param parkee The channel in the bridge to be parked.
* \param parkee_uuid The UUID of the channel being packed.
* \param parker_uuid The UUID of the channel performing the park.
* \param app_data Data to pass to the Park application
*
* \note This must be called within the context of the \ref AstBridging Bridging API.
* External entities should not call this method directly, but should instead use
* the direct call parking method or the blind transfer method.
*
* \retval 0 on success
* \retval non-zero on error
*/
int (* parking_park_bridge_channel)(struct ast_bridge_channel *parkee, const char *parkee_uuid, const char *parker_uuid, const char *app_data);
};
/*!
* \brief uninstall a callback for handling blind transfers to a parking extension
* \since 12
* \brief Obtain the current parking provider
*
* \retval NULL if no provider exists
* \retval an ao2 ref counted object of the existing provider's function table
*/
void ast_uninstall_park_blind_xfer_func(void);
struct ast_parking_bridge_feature_fn_table *ast_parking_get_bridge_features(void);
/*!
* \brief use the installed park blind xfer func
* \since 12
* \brief Register a parking provider
*
* \param parker Bridge channel initiating the park
* \param park_exten Exten to blind transfer part to.
* \param fn_table The \ref ast_parking_bridge_feature_fn_table to register
*
* \retval 0 on success
* \retval -1 on failure
*/
int ast_park_blind_xfer(struct ast_bridge_channel *parker, struct ast_exten *park_exten);
typedef void (*ast_bridge_channel_park_fn)(struct ast_bridge_channel *parkee, const char *parkee_uuid,
const char *parker_uuid, const char *app_data);
/*!
* \brief Install a function for ast_bridge_channel_park
* \since 12
*
* \param bridge_channel_park_func function callback to use for ast_bridge_channel_park
* \retval -1 on error
*/
void ast_install_bridge_channel_park_func(ast_bridge_channel_park_fn bridge_channel_park_func);
int ast_parking_register_bridge_features(struct ast_parking_bridge_feature_fn_table *fn_table);
/*!
* \brief Uninstall the ast_bridge_channel_park function callback
* \since 12
*/
void ast_uninstall_bridge_channel_park_func(void);
/*!
* \brief Determines whether a certain extension is a park application extension or not.
* \since 12
* \brief Unregister the current parking provider
*
* \param exten_str string representation of the extension sought
* \param chan channel the extension is sought for
* \param context context the extension is sought from
* \param The module name of the provider to unregister
*
* \retval pointer to the extension if the extension is a park extension
* \retval NULL if the extension was not a park extension
* \retval 0 if the parking provider \c module_name was unregsistered
* \retval -1 on error
*/
struct ast_exten *ast_get_parking_exten(const char *exten_str, struct ast_channel *chan, const char *context);
int ast_parking_unregister_bridge_features(const char *module_name);

@ -3781,32 +3781,31 @@ static struct ast_channel *get_transferee(struct ao2_container *channels, struct
return transferee;
}
enum try_parking_result {
PARKING_SUCCESS,
PARKING_FAILURE,
PARKING_NOT_APPLICABLE,
};
static enum try_parking_result try_parking(struct ast_channel *transferer, const char *exten, const char *context)
static enum ast_transfer_result try_parking(struct ast_channel *transferer, const char *context, const char *exten)
{
RAII_VAR(struct ast_bridge_channel *, transferer_bridge_channel, NULL, ao2_cleanup);
struct ast_exten *parking_exten;
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
if (!parking_provider) {
return AST_BRIDGE_TRANSFER_FAIL;
}
ast_channel_lock(transferer);
transferer_bridge_channel = ast_channel_get_bridge_channel(transferer);
ast_channel_unlock(transferer);
if (!transferer_bridge_channel) {
return PARKING_FAILURE;
return AST_BRIDGE_TRANSFER_FAIL;
}
parking_exten = ast_get_parking_exten(exten, NULL, context);
if (parking_exten) {
return ast_park_blind_xfer(transferer_bridge_channel, parking_exten) == 0 ?
PARKING_SUCCESS : PARKING_FAILURE;
if (parking_provider->parking_blind_transfer_park(transferer_bridge_channel,
context, exten)) {
return AST_BRIDGE_TRANSFER_FAIL;
}
return PARKING_NOT_APPLICABLE;
return AST_BRIDGE_TRANSFER_SUCCESS;
}
/*!
@ -3883,7 +3882,6 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
RAII_VAR(struct ast_channel *, transferee, NULL, ast_channel_cleanup);
int do_bridge_transfer;
int transfer_prohibited;
enum try_parking_result parking_result;
enum ast_transfer_result transfer_result;
bridge = acquire_bridge(transferer);
@ -3902,17 +3900,9 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
/* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold(bridge_channel);
parking_result = try_parking(transferer, exten, context);
switch (parking_result) {
case PARKING_SUCCESS:
transfer_result = AST_BRIDGE_TRANSFER_SUCCESS;
goto publish;
case PARKING_FAILURE:
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
transfer_result = try_parking(transferer, context, exten);
if (transfer_result == AST_BRIDGE_TRANSFER_SUCCESS) {
goto publish;
case PARKING_NOT_APPLICABLE:
default:
break;
}
{

@ -757,9 +757,22 @@ struct bridge_park {
*/
static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload)
{
ast_bridge_channel_park(bridge_channel, payload->parkee_uuid,
RAII_VAR(struct ast_parking_bridge_feature_fn_table *, parking_provider,
ast_parking_get_bridge_features(),
ao2_cleanup);
if (!parking_provider) {
ast_log(AST_LOG_WARNING, "Unable to park %s: No parking provider loaded!\n",
ast_channel_name(bridge_channel->chan));
return;
}
if (parking_provider->parking_park_bridge_channel(bridge_channel, payload->parkee_uuid,
&payload->parkee_uuid[payload->parker_uuid_offset],
payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL);
payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL)) {
ast_log(AST_LOG_WARNING, "Error occurred while parking %s\n",
ast_channel_name(bridge_channel->chan));
}
}
/*!

File diff suppressed because it is too large Load Diff

@ -41,11 +41,8 @@ STASIS_MESSAGE_TYPE_DEFN(ast_parked_call_type);
/*! \brief Topic for parking lots */
static struct stasis_topic *parking_topic;
/*! \brief Function Callback for handling blind transfers to park applications */
static ast_park_blind_xfer_fn ast_park_blind_xfer_func = NULL;
/*! \brief Function Callback for handling a bridge channel trying to park itself */
static ast_bridge_channel_park_fn ast_bridge_channel_park_func = NULL;
/*! \brief The container for the parking provider */
static AO2_GLOBAL_OBJ_STATIC(parking_provider);
static void parking_stasis_cleanup(void)
{
@ -127,67 +124,58 @@ struct ast_parked_call_payload *ast_parked_call_payload_create(enum ast_parked_c
return payload;
}
void ast_install_park_blind_xfer_func(ast_park_blind_xfer_fn park_blind_xfer_func)
{
ast_park_blind_xfer_func = park_blind_xfer_func;
}
void ast_install_bridge_channel_park_func(ast_bridge_channel_park_fn bridge_channel_park_func)
struct ast_parking_bridge_feature_fn_table *ast_parking_get_bridge_features(void)
{
ast_bridge_channel_park_func = bridge_channel_park_func;
return (struct ast_parking_bridge_feature_fn_table*)ao2_global_obj_ref(parking_provider);
}
void ast_uninstall_park_blind_xfer_func(void)
{
ast_park_blind_xfer_func = NULL;
}
/*! \brief A wrapper around the fn_table to ao2-ify it */
struct parking_provider_wrapper {
struct ast_parking_bridge_feature_fn_table fn_table;
};
void ast_uninstall_bridge_channel_park_func(void)
int ast_parking_register_bridge_features(struct ast_parking_bridge_feature_fn_table *fn_table)
{
ast_bridge_channel_park_func = NULL;
}
RAII_VAR(struct parking_provider_wrapper *, wrapper,
ao2_global_obj_ref(parking_provider), ao2_cleanup);
int ast_park_blind_xfer(struct ast_bridge_channel *parker, struct ast_exten *park_exten)
{
static int warned = 0;
if (fn_table->module_version != PARKING_MODULE_VERSION) {
ast_log(AST_LOG_WARNING, "Parking module provided incorrect parking module "
"version: %d (expected: %d)\n", fn_table->module_version, PARKING_MODULE_VERSION);
return -1;
}
if (ast_park_blind_xfer_func) {
return ast_park_blind_xfer_func(parker, park_exten);
if (wrapper) {
ast_log(AST_LOG_WARNING, "Parking provider already registered by %s!\n",
wrapper->fn_table.module_name);
return -1;
}
if (warned++ % 10 == 0) {
ast_verb(3, "%s attempted to blind transfer to a parking extension, but no parking blind transfer function is loaded.\n",
ast_channel_name(parker->chan));
wrapper = ao2_alloc(sizeof(*wrapper), NULL);
if (!wrapper) {
return -1;
}
wrapper->fn_table = *fn_table;
return -1;
ao2_global_obj_replace(parking_provider, wrapper);
return 0;
}
struct ast_exten *ast_get_parking_exten(const char *exten_str, struct ast_channel *chan, const char *context)
int ast_parking_unregister_bridge_features(const char *module_name)
{
struct ast_exten *exten;
struct pbx_find_info q = { .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_str, context);
exten = pbx_find_extension(chan, NULL, &q, context, exten_str, 1, NULL, NULL,
E_MATCH);
if (!exten) {
return NULL;
}
RAII_VAR(struct parking_provider_wrapper *, wrapper,
ao2_global_obj_ref(parking_provider), ao2_cleanup);
app_at_exten = ast_get_extension_app(exten);
if (!app_at_exten || strcasecmp(PARK_APPLICATION, app_at_exten)) {
return NULL;
if (!wrapper) {
ast_log(AST_LOG_WARNING, "No parking provider to unregister\n");
return -1;
}
return exten;
}
void ast_bridge_channel_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data)
{
/* Run installable function */
if (ast_bridge_channel_park_func) {
return ast_bridge_channel_park_func(bridge_channel, parkee_uuid, parker_uuid, app_data);
if (strcmp(wrapper->fn_table.module_name, module_name)) {
ast_log(AST_LOG_WARNING, "%s has not registered the parking provider\n", module_name);
return -1;
}
ao2_global_obj_replace_unref(parking_provider, NULL);
return 0;
}

@ -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;
}

@ -1140,19 +1140,18 @@ static void link_configured_disable_marked_lots(void)
static int load_module(void)
{
if (aco_info_init(&cfg_info)) {
goto error;
}
parking_lot_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX,
AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
parking_lot_sort_fn,
NULL);
if (!parking_lot_container) {
goto error;
}
if (aco_info_init(&cfg_info)) {
goto error;
}
/* Global options */
aco_option_register(&cfg_info, "parkeddynamic", ACO_EXACT, global_options, "no", OPT_BOOL_T, 1, FLDSET(struct parking_global_config, parkeddynamic));

Loading…
Cancel
Save