summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--channels/chan_dahdi.c28
-rw-r--r--channels/chan_iax2.c2
-rw-r--r--channels/chan_mgcp.c20
-rw-r--r--channels/chan_skinny.c75
-rw-r--r--channels/sig_analog.c25
-rw-r--r--include/asterisk/features.h44
-rw-r--r--include/asterisk/parking.h150
-rw-r--r--main/bridge.c40
-rw-r--r--main/bridge_channel.c17
-rw-r--r--main/features.c1342
-rw-r--r--main/parking.c88
-rw-r--r--res/parking/parking_bridge_features.c214
-rw-r--r--res/res_parking.c9
13 files changed, 427 insertions, 1627 deletions
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index d18e94d14..a31c8e7bc 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -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));
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 26ca36d75..1ae5505e3 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -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;
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 79208daa0..233d2c3ff 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -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);
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index c7485470a..c97451577 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -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);
}
diff --git a/channels/sig_analog.c b/channels/sig_analog.c
index 9effae1f1..4b5ae8887 100644
--- a/channels/sig_analog.c
+++ b/channels/sig_analog.c
@@ -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);
diff --git a/include/asterisk/features.h b/include/asterisk/features.h
index b1400ee39..43edca07a 100644
--- a/include/asterisk/features.h
+++ b/include/asterisk/features.h
@@ -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);
diff --git a/include/asterisk/parking.h b/include/asterisk/parking.h
index 4a721200a..a835d104a 100644
--- a/include/asterisk/parking.h
+++ b/include/asterisk/parking.h
@@ -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);
diff --git a/main/bridge.c b/main/bridge.c
index 9e9e65c17..2e2c87bc6 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -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;
}
{
diff --git a/main/bridge_channel.c b/main/bridge_channel.c
index f887ef576..f0163ff2e 100644
--- a/main/bridge_channel.c
+++ b/main/bridge_channel.c
@@ -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));
+ }
}
/*!
diff --git a/main/features.c b/main/features.c
index 9490116b7..7162d4e55 100644
--- a/main/features.c
+++ b/main/features.c
@@ -470,20 +470,11 @@ struct ast_parkinglot {
/*! \brief The configured parking lots container. Always at least one - the default parking lot */
static struct ao2_container *parkinglots;
-/*!
- * \brief Default parking lot.
- * \note Holds a parkinglot reference.
- * \note Will not be NULL while running.
- */
-static struct ast_parkinglot *default_parkinglot;
-
/*! Force a config reload to reload regardless of config file timestamp. */
#ifdef TEST_FRAMEWORK
static int force_reload_load;
#endif
-static int parkeddynamic = 0; /*!< Enable creation of parkinglots dynamically */
-
/*!
* \brief Context for parking dialback to parker.
* \note The need for the context is a KLUDGE.
@@ -497,8 +488,6 @@ static char parking_con_dial[] = "park-dial";
/*! Ensure that features.conf reloads on one thread at a time. */
AST_MUTEX_DEFINE_STATIC(features_reload_lock);
-static int adsipark;
-
static char *registrar = "features"; /*!< Registrar for operations */
/*! PARK_APP_NAME application arguments */
@@ -512,9 +501,6 @@ AST_DEFINE_APP_ARGS_TYPE(park_app_args,
AST_APP_ARG(dummy); /*!< Place to put any remaining args string. */
);
-/* module and CLI command definitions */
-static const char *parkcall = "Park";
-
static pthread_t parking_thread;
struct ast_dial_features {
/*! Channel's feature flags. */
@@ -670,50 +656,7 @@ static int add_features_datastore(struct ast_channel *chan, const struct ast_fla
}
/* Forward declarations */
-static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot);
static void parkinglot_unref(struct ast_parkinglot *parkinglot);
-static struct ast_parkinglot *find_parkinglot(const char *name);
-static struct ast_parkinglot *create_parkinglot(const char *name);
-static struct ast_parkinglot *copy_parkinglot(const char *name, const struct ast_parkinglot *parkinglot);
-static int parkinglot_activate(struct ast_parkinglot *parkinglot);
-static int play_message_on_chan(struct ast_channel *play_to, struct ast_channel *other, const char *msg, const char *audiofile);
-
-/*!
- * \internal
- * \brief Get the parking extension if it exists.
- *
- * \param exten_str Parking extension to see if exists.
- * \param chan Channel to autoservice while looking for exten. (Could be NULL)
- * \param context Parking context to look in for exten.
- *
- * \retval exten on success.
- * \retval NULL on error or exten does not exist.
- */
-static struct ast_exten *get_parking_exten(const char *exten_str, struct ast_channel *chan, const char *context)
-{
- 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;
- }
-
- app_at_exten = ast_get_extension_app(exten);
- if (!app_at_exten || strcasecmp(parkcall, app_at_exten)) {
- return NULL;
- }
-
- return exten;
-}
-
-int ast_parking_ext_valid(const char *exten_str, struct ast_channel *chan, const char *context)
-{
- return get_parking_exten(exten_str, chan, context) ? 1 : 0;
-}
struct ast_bridge_thread_obj
{
@@ -755,46 +698,6 @@ static const struct ast_datastore_info channel_app_data_datastore = {
.destroy = ast_free_ptr,
};
-/*!
- * \brief Announce call parking by ADSI
- * \param chan .
- * \param parkingexten .
- * Create message to show for ADSI, display message.
- * \retval 0 on success.
- * \retval -1 on failure.
- */
-static int adsi_announce_park(struct ast_channel *chan, char *parkingexten)
-{
- int res;
- int justify[5] = {ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT, ADSI_JUST_CENT};
- char tmp[256];
- char *message[5] = {NULL, NULL, NULL, NULL, NULL};
-
- snprintf(tmp, sizeof(tmp), "Parked on %s", parkingexten);
- message[0] = tmp;
- res = ast_adsi_load_session(chan, NULL, 0, 1);
- if (res == -1)
- return res;
- return ast_adsi_print(chan, message, justify, 1);
-}
-
-/*!
- * \brief Find parking lot name from channel
- * \note Channel needs to be locked while the returned string is in use.
- */
-static const char *findparkinglotname(struct ast_channel *chan)
-{
- const char *name;
-
- /* The channel variable overrides everything */
- name = pbx_builtin_getvar_helper(chan, "PARKINGLOT");
- if (!name && !ast_strlen_zero(ast_channel_parkinglot(chan))) {
- /* Use the channel's parking lot. */
- name = ast_channel_parkinglot(chan);
- }
- return name;
-}
-
/*! \brief Notify metermaids that we've changed an extension */
static void notify_metermaids(const char *exten, char *context, enum ast_device_state state)
{
@@ -857,705 +760,6 @@ struct ast_park_call_args {
/*!
* \internal
- * \brief Create a dynamic parking lot.
- *
- * \param name Dynamic parking lot name to create.
- * \param chan Channel to get dynamic parking lot parameters.
- *
- * \retval parkinglot on success.
- * \retval NULL on error.
- */
-static struct ast_parkinglot *create_dynamic_parkinglot(const char *name, struct ast_channel *chan)
-{
- const char *dyn_context;
- const char *dyn_exten;
- const char *dyn_range;
- const char *template_name;
- struct ast_parkinglot *template_parkinglot = NULL;
- struct ast_parkinglot *parkinglot;
- int dyn_start;
- int dyn_end;
-
- ast_channel_lock(chan);
- template_name = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNAMIC"), ""));
- dyn_context = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNCONTEXT"), ""));
- dyn_exten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNEXTEN"), ""));
- dyn_range = ast_strdupa(S_OR(pbx_builtin_getvar_helper(chan, "PARKINGDYNPOS"), ""));
- ast_channel_unlock(chan);
-
- if (!ast_strlen_zero(template_name)) {
- template_parkinglot = find_parkinglot(template_name);
- if (!template_parkinglot) {
- ast_debug(1, "PARKINGDYNAMIC lot %s does not exist.\n",
- template_name);
- } else if (template_parkinglot->cfg.is_invalid) {
- ast_debug(1, "PARKINGDYNAMIC lot %s has invalid config.\n",
- template_name);
- parkinglot_unref(template_parkinglot);
- template_parkinglot = NULL;
- }
- }
- if (!template_parkinglot) {
- template_parkinglot = parkinglot_addref(default_parkinglot);
- ast_debug(1, "Using default parking lot for template\n");
- }
-
- parkinglot = copy_parkinglot(name, template_parkinglot);
- if (!parkinglot) {
- ast_log(LOG_ERROR, "Could not build dynamic parking lot!\n");
- } else {
- /* Configure the dynamic parking lot. */
- if (!ast_strlen_zero(dyn_context)) {
- ast_copy_string(parkinglot->cfg.parking_con, dyn_context,
- sizeof(parkinglot->cfg.parking_con));
- }
- if (!ast_strlen_zero(dyn_exten)) {
- ast_copy_string(parkinglot->cfg.parkext, dyn_exten,
- sizeof(parkinglot->cfg.parkext));
- }
- if (!ast_strlen_zero(dyn_range)) {
- if (sscanf(dyn_range, "%30d-%30d", &dyn_start, &dyn_end) != 2) {
- ast_log(LOG_WARNING,
- "Format for parking positions is a-b, where a and b are numbers\n");
- } else if (dyn_end < dyn_start || dyn_start <= 0 || dyn_end <= 0) {
- ast_log(LOG_WARNING,
- "Format for parking positions is a-b, where a <= b\n");
- } else {
- parkinglot->cfg.parking_start = dyn_start;
- parkinglot->cfg.parking_stop = dyn_end;
- }
- }
-
- /*
- * Sanity check for dynamic parking lot configuration.
- *
- * XXX It may be desirable to instead check if the dynamic
- * parking lot overlaps any existing lots like what is done for
- * a reload.
- */
- if (!strcmp(parkinglot->cfg.parking_con, template_parkinglot->cfg.parking_con)) {
- if (!strcmp(parkinglot->cfg.parkext, template_parkinglot->cfg.parkext)
- && parkinglot->cfg.parkext_exclusive) {
- ast_log(LOG_WARNING,
- "Parking lot '%s' conflicts with template parking lot '%s'!\n"
- "Change either PARKINGDYNCONTEXT or PARKINGDYNEXTEN.\n",
- parkinglot->name, template_parkinglot->name);
- }
- if ((template_parkinglot->cfg.parking_start <= parkinglot->cfg.parking_start
- && parkinglot->cfg.parking_start <= template_parkinglot->cfg.parking_stop)
- || (template_parkinglot->cfg.parking_start <= parkinglot->cfg.parking_stop
- && parkinglot->cfg.parking_stop <= template_parkinglot->cfg.parking_stop)
- || (parkinglot->cfg.parking_start < template_parkinglot->cfg.parking_start
- && template_parkinglot->cfg.parking_stop < parkinglot->cfg.parking_stop)) {
- ast_log(LOG_WARNING,
- "Parking lot '%s' parking spaces overlap template parking lot '%s'!\n"
- "Change PARKINGDYNPOS.\n",
- parkinglot->name, template_parkinglot->name);
- }
- }
-
- parkinglot_activate(parkinglot);
- ao2_link(parkinglots, parkinglot);
- }
- parkinglot_unref(template_parkinglot);
-
- return parkinglot;
-}
-
-/*!
- * \internal
- * \brief Abort parking a call that has not completed parking yet.
- *
- * \param pu Parked user item to clean up.
- *
- * \note The parking lot parkings list is locked on entry.
- *
- * \return Nothing
- */
-static void park_space_abort(struct parkeduser *pu)
-{
- struct ast_parkinglot *parkinglot;
-
- parkinglot = pu->parkinglot;
-
- /* Put back the parking space just allocated. */
- --parkinglot->next_parking_space;
-
- AST_LIST_REMOVE(&parkinglot->parkings, pu, list);
-
- AST_LIST_UNLOCK(&parkinglot->parkings);
- parkinglot_unref(parkinglot);
- ast_free(pu);
-}
-
-/*!
- * \internal
- * \brief Reserve a parking space in a parking lot for a call being parked.
- *
- * \param park_me Channel being parked.
- * \param parker Channel parking the call.
- * \param args Optional additional parking options when parking a call.
- *
- * \return Parked call descriptor or NULL if failed.
- * \note The parking lot list is locked if successful.
- */
-static struct parkeduser *park_space_reserve(struct ast_channel *park_me, struct ast_channel *parker, struct ast_park_call_args *args)
-{
- struct parkeduser *pu;
- int i;
- int parking_space = -1;
- const char *parkinglotname;
- const char *parkingexten;
- struct parkeduser *cur;
- struct ast_parkinglot *parkinglot = NULL;
-
- if (args->parkinglot) {
- parkinglot = parkinglot_addref(args->parkinglot);
- parkinglotname = parkinglot->name;
- } else {
- if (parker) {
- parkinglotname = findparkinglotname(parker);
- } else { /* parker was NULL, check park_me (ParkAndAnnounce / res_agi) */
- parkinglotname = findparkinglotname(park_me);
- }
- if (!ast_strlen_zero(parkinglotname)) {
- parkinglot = find_parkinglot(parkinglotname);
- } else {
- /* Parking lot is not specified, so use the default parking lot. */
- ast_debug(4, "This could be an indication channel driver needs updating, using default lot.\n");
- parkinglot = parkinglot_addref(default_parkinglot);
- }
- }
-
- /* Dynamically create parkinglot */
- if (!parkinglot && parkeddynamic && !ast_strlen_zero(parkinglotname)) {
- parkinglot = create_dynamic_parkinglot(parkinglotname, park_me);
- }
-
- if (!parkinglot) {
- ast_log(LOG_WARNING, "Parking lot not available to park %s.\n", ast_channel_name(park_me));
- return NULL;
- }
-
- ast_debug(1, "Parking lot: %s\n", parkinglot->name);
- if (parkinglot->disabled || parkinglot->cfg.is_invalid) {
- ast_log(LOG_WARNING, "Parking lot %s is not in a useable state.\n",
- parkinglot->name);
- parkinglot_unref(parkinglot);
- return NULL;
- }
-
- /* Allocate memory for parking data */
- if (!(pu = ast_calloc(1, sizeof(*pu)))) {
- parkinglot_unref(parkinglot);
- return NULL;
- }
-
- /* Lock parking list */
- AST_LIST_LOCK(&parkinglot->parkings);
-
- /* Check for channel variable PARKINGEXTEN */
- parkingexten = ast_strdupa(S_OR(pbx_builtin_getvar_helper(park_me, "PARKINGEXTEN"), ""));
- if (!ast_strlen_zero(parkingexten)) {
- /*!
- * \note The API forces us to specify a numeric parking slot, even
- * though the architecture would tend to support non-numeric extensions
- * (as are possible with SIP, for example). Hence, we enforce that
- * limitation here. If extout was not numeric, we could permit
- * arbitrary non-numeric extensions.
- */
- if (sscanf(parkingexten, "%30d", &parking_space) != 1 || parking_space <= 0) {
- ast_log(LOG_WARNING, "PARKINGEXTEN='%s' is not a valid parking space.\n",
- parkingexten);
- AST_LIST_UNLOCK(&parkinglot->parkings);
- parkinglot_unref(parkinglot);
- ast_free(pu);
- return NULL;
- }
-
- if (parking_space < parkinglot->cfg.parking_start
- || parkinglot->cfg.parking_stop < parking_space) {
- /*
- * Cannot allow park because parking lots are not setup for
- * spaces outside of the lot. (Things like dialplan hints don't
- * exist for outside lot space.)
- */
- ast_log(LOG_WARNING, "PARKINGEXTEN=%d is not in %s (%d-%d).\n",
- parking_space, parkinglot->name, parkinglot->cfg.parking_start,
- parkinglot->cfg.parking_stop);
- AST_LIST_UNLOCK(&parkinglot->parkings);
- parkinglot_unref(parkinglot);
- ast_free(pu);
- return NULL;
- }
-
- /* Check if requested parking space is in use. */
- AST_LIST_TRAVERSE(&parkinglot->parkings, cur, list) {
- if (cur->parkingnum == parking_space) {
- ast_log(LOG_WARNING, "PARKINGEXTEN=%d is already in use in %s\n",
- parking_space, parkinglot->name);
- AST_LIST_UNLOCK(&parkinglot->parkings);
- parkinglot_unref(parkinglot);
- ast_free(pu);
- return NULL;
- }
- }
- } else {
- /* PARKINGEXTEN is empty, so find a usable extension in the lot to park the call */
- int start; /* The first slot we look in the parkinglot. It can be randomized. */
- int start_checked = 0; /* flag raised once the first slot is checked */
-
- /* If using randomize mode, set start to random position on parking range */
- if (ast_test_flag(args, AST_PARK_OPT_RANDOMIZE)) {
- start = ast_random() % (parkinglot->cfg.parking_stop - parkinglot->cfg.parking_start + 1);
- start += parkinglot->cfg.parking_start;
- } else if (parkinglot->cfg.parkfindnext
- && parkinglot->cfg.parking_start <= parkinglot->next_parking_space
- && parkinglot->next_parking_space <= parkinglot->cfg.parking_stop) {
- /* Start looking with the next parking space in the lot. */
- start = parkinglot->next_parking_space;
- } else {
- /* Otherwise, just set it to the start position. */
- start = parkinglot->cfg.parking_start;
- }
-
- /* free parking extension linear search: O(n^2) */
- for (i = start; ; i++) {
- /* If we are past the end, wrap around to the first parking slot*/
- if (i == parkinglot->cfg.parking_stop + 1) {
- i = parkinglot->cfg.parking_start;
- }
-
- if (i == start) {
- /* At this point, if start_checked, we've exhausted all the possible slots. */
- if (start_checked) {
- break;
- } else {
- start_checked = 1;
- }
- }
-
- /* Search the list of parked calls already in use for i. If we find it, it's in use. */
- AST_LIST_TRAVERSE(&parkinglot->parkings, cur, list) {
- if (cur->parkingnum == i) {
- break;
- }
- }
- if (!cur) {
- /* We found a parking space. */
- parking_space = i;
- break;
- }
- }
- if (parking_space == -1) {
- /* We did not find a parking space. Lot is full. */
- ast_log(LOG_WARNING, "No more parking spaces in %s\n", parkinglot->name);
- AST_LIST_UNLOCK(&parkinglot->parkings);
- parkinglot_unref(parkinglot);
- ast_free(pu);
- return NULL;
- }
- }
-
- /* Prepare for next parking space search. */
- parkinglot->next_parking_space = parking_space + 1;
-
- snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", parking_space);
- pu->notquiteyet = 1;
- pu->parkingnum = parking_space;
- pu->parkinglot = parkinglot;
- AST_LIST_INSERT_TAIL(&parkinglot->parkings, pu, list);
-
- return pu;
-}
-
-/* Park a call */
-static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, struct ast_park_call_args *args)
-{
- struct parkeduser *pu = args->pu;
- const char *event_from; /*!< Channel name that is parking the call. */
- char app_data[AST_MAX_EXTENSION + AST_MAX_CONTEXT];
-
- if (pu == NULL) {
- args->pu = pu = park_space_reserve(chan, peer, args);
- if (pu == NULL) {
- return -1;
- }
- }
-
- ast_channel_appl_set(chan, "Parked Call");
- ast_channel_data_set(chan, NULL);
-
- pu->chan = chan;
-
- /* Put the parked channel on hold if we have two different channels */
- if (chan != peer) {
- if (ast_test_flag(args, AST_PARK_OPT_RINGING)) {
- pu->hold_method = AST_CONTROL_RINGING;
- ast_indicate(chan, AST_CONTROL_RINGING);
- } else {
- pu->hold_method = AST_CONTROL_HOLD;
- ast_indicate_data(chan, AST_CONTROL_HOLD,
- S_OR(pu->parkinglot->cfg.mohclass, NULL),
- !ast_strlen_zero(pu->parkinglot->cfg.mohclass) ? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0);
- }
- }
-
- pu->start = ast_tvnow();
- /* XXX This line was changed to not use get_parkingtime. This is just a placeholder message, because
- * likely this entire function is going away.
- */
- pu->parkingtime = args->timeout;
- if (args->extout)
- *(args->extout) = pu->parkingnum;
-
- if (peer) {
- event_from = S_OR(args->orig_chan_name, ast_channel_name(peer));
-
- /*
- * This is so ugly that it hurts, but implementing
- * get_base_channel() on local channels could have ugly side
- * effects. We could have
- * transferer<->local;1<->local;2<->parking and we need the
- * callback name to be that of transferer. Since local;1/2 have
- * the same name we can be tricky and just grab the bridged
- * channel from the other side of the local.
- */
- if (!strcasecmp(ast_channel_tech(peer)->type, "Local")) {
- struct ast_channel *tmpchan, *base_peer;
- char other_side[AST_CHANNEL_NAME];
- char *c;
-
- ast_copy_string(other_side, event_from, sizeof(other_side));
- if ((c = strrchr(other_side, ';'))) {
- *++c = '1';
- }
- if ((tmpchan = ast_channel_get_by_name(other_side))) {
- ast_channel_lock(tmpchan);
- if ((base_peer = ast_bridged_channel(tmpchan))) {
- ast_copy_string(pu->peername, ast_channel_name(base_peer), sizeof(pu->peername));
- }
- ast_channel_unlock(tmpchan);
- tmpchan = ast_channel_unref(tmpchan);
- }
- } else {
- ast_copy_string(pu->peername, event_from, sizeof(pu->peername));
- }
- } else {
- event_from = S_OR(pbx_builtin_getvar_helper(chan, "BLINDTRANSFER"),
- ast_channel_name(chan));
- }
-
- /*
- * Remember what had been dialed, so that if the parking
- * expires, we try to come back to the same place
- */
- pu->options_specified = (!ast_strlen_zero(args->return_con) || !ast_strlen_zero(args->return_ext) || args->return_pri);
-
- /*
- * If extension has options specified, they override all other
- * possibilities such as the returntoorigin flag and transferred
- * context. Information on extension options is lost here, so
- * we set a flag
- */
- ast_copy_string(pu->context,
- S_OR(args->return_con, S_OR(ast_channel_macrocontext(chan), ast_channel_context(chan))),
- sizeof(pu->context));
- ast_copy_string(pu->exten,
- S_OR(args->return_ext, S_OR(ast_channel_macroexten(chan), ast_channel_exten(chan))),
- sizeof(pu->exten));
- pu->priority = args->return_pri ? args->return_pri :
- (ast_channel_macropriority(chan) ? ast_channel_macropriority(chan) : ast_channel_priority(chan));
-
- /*
- * If parking a channel directly, don't quite yet get parking
- * running on it. All parking lot entries are put into the
- * parking lot with notquiteyet on.
- */
- if (peer != chan) {
- pu->notquiteyet = 0;
- }
-
- /* Wake up the (presumably select()ing) thread */
- pthread_kill(parking_thread, SIGURG);
- ast_verb(2, "Parked %s on %d (lot %s). Will timeout back to extension [%s] %s, %d in %u seconds\n",
- ast_channel_name(chan), pu->parkingnum, pu->parkinglot->name,
- pu->context, pu->exten, pu->priority, (pu->parkingtime / 1000));
-
- /*** DOCUMENTATION
- <managerEventInstance>
- <synopsis>Raised when a call has been parked.</synopsis>
- <syntax>
- <parameter name="Exten">
- <para>The parking lot extension.</para>
- </parameter>
- <parameter name="Parkinglot">
- <para>The name of the parking lot.</para>
- </parameter>
- <parameter name="From">
- <para>The name of the channel that parked the call.</para>
- </parameter>
- </syntax>
- <see-also>
- <ref type="application">Park</ref>
- <ref type="manager">Park</ref>
- <ref type="managerEvent">ParkedCallTimeOut</ref>
- <ref type="managerEvent">ParkedCallGiveUp</ref>
- </see-also>
- </managerEventInstance>
- ***/
- ast_manager_event(chan, EVENT_FLAG_CALL, "ParkedCall",
- "Exten: %s\r\n"
- "Channel: %s\r\n"
- "Parkinglot: %s\r\n"
- "From: %s\r\n"
- "Timeout: %ld\r\n"
- "CallerIDNum: %s\r\n"
- "CallerIDName: %s\r\n"
- "ConnectedLineNum: %s\r\n"
- "ConnectedLineName: %s\r\n"
- "Uniqueid: %s\r\n",
- pu->parkingexten, ast_channel_name(chan), pu->parkinglot->name, event_from,
- (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
- S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
- S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>"),
- S_COR(ast_channel_connected(chan)->id.number.valid, ast_channel_connected(chan)->id.number.str, "<unknown>"),
- S_COR(ast_channel_connected(chan)->id.name.valid, ast_channel_connected(chan)->id.name.str, "<unknown>"),
- ast_channel_uniqueid(chan)
- );
- ast_debug(4, "peer: %s\n", peer ? ast_channel_name(peer) : "-No peer-");
- ast_debug(4, "args->orig_chan_name: %s\n", args->orig_chan_name ? args->orig_chan_name : "-none-");
- ast_debug(4, "pu->peername: %s\n", pu->peername);
- ast_debug(4, "AMI ParkedCall Channel: %s\n", ast_channel_name(chan));
- ast_debug(4, "AMI ParkedCall From: %s\n", event_from);
-
- if (peer && adsipark && ast_adsi_available(peer)) {
- adsi_announce_park(peer, pu->parkingexten); /* Only supports parking numbers */
- ast_adsi_unload_session(peer);
- }
-
- snprintf(app_data, sizeof(app_data), "%s,%s", pu->parkingexten,
- pu->parkinglot->name);
-
- AST_LIST_UNLOCK(&pu->parkinglot->parkings);
-
- /* Only say number if it's a number and the channel hasn't been masqueraded away */
- if (peer && !ast_test_flag(args, AST_PARK_OPT_SILENCE)
- && (ast_strlen_zero(args->orig_chan_name) || !strcasecmp(ast_channel_name(peer), args->orig_chan_name))) {
- /*
- * If a channel is masqueraded into peer while playing back the
- * parking space number do not continue playing it back. This
- * is the case if an attended transfer occurs.
- */
- ast_set_flag(ast_channel_flags(peer), AST_FLAG_MASQ_NOSTREAM);
- /* Tell the peer channel the number of the parking space */
- ast_say_digits(peer, pu->parkingnum, "", ast_channel_language(peer));
- ast_clear_flag(ast_channel_flags(peer), AST_FLAG_MASQ_NOSTREAM);
- }
- if (peer == chan) { /* pu->notquiteyet = 1 */
- /* Wake up parking thread if we're really done */
- if (ast_test_flag(args, AST_PARK_OPT_RINGING)) {
- pu->hold_method = AST_CONTROL_RINGING;
- ast_indicate(chan, AST_CONTROL_RINGING);
- } else {
- pu->hold_method = AST_CONTROL_HOLD;
- ast_indicate_data(chan, AST_CONTROL_HOLD,
- S_OR(pu->parkinglot->cfg.mohclass, NULL),
- !ast_strlen_zero(pu->parkinglot->cfg.mohclass) ? strlen(pu->parkinglot->cfg.mohclass) + 1 : 0);
- }
- pu->notquiteyet = 0;
- pthread_kill(parking_thread, SIGURG);
- }
- return 0;
-}
-
-/*!
- * \brief Park call via masqueraded channel and announce parking spot on peer channel.
- *
- * \param rchan the real channel to be parked
- * \param peer the channel to have the parking read to.
- * \param args Additional parking options when parking a call.
- *
- * \retval 0 on success.
- * \retval -1 on failure.
- */
-static int masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, struct ast_park_call_args *args)
-{
- struct ast_channel *chan;
-
- /* Make a new, channel that we'll use to masquerade in the real one */
- chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, ast_channel_accountcode(rchan), ast_channel_exten(rchan),
- ast_channel_context(rchan), ast_channel_linkedid(rchan), ast_channel_amaflags(rchan), "Parked/%s", ast_channel_name(rchan));
- if (!chan) {
- ast_log(LOG_WARNING, "Unable to create parked channel\n");
- if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
- if (peer == rchan) {
- /* Only have one channel to worry about. */
- ast_stream_and_wait(peer, "pbx-parkingfailed", "");
- } else if (peer) {
- /* Have two different channels to worry about. */
- play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
- }
- }
- return -1;
- }
-
- args->pu = park_space_reserve(rchan, peer, args);
- if (!args->pu) {
- ast_hangup(chan);
- if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
- if (peer == rchan) {
- /* Only have one channel to worry about. */
- ast_stream_and_wait(peer, "pbx-parkingfailed", "");
- } else if (peer) {
- /* Have two different channels to worry about. */
- play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
- }
- }
- return -1;
- }
-
- /* Make formats okay */
- ast_format_copy(ast_channel_readformat(chan), ast_channel_readformat(rchan));
- ast_format_copy(ast_channel_writeformat(chan), ast_channel_writeformat(rchan));
-
- if (ast_channel_masquerade(chan, rchan)) {
- park_space_abort(args->pu);
- args->pu = NULL;
- ast_hangup(chan);
- if (!ast_test_flag(args, AST_PARK_OPT_SILENCE)) {
- if (peer == rchan) {
- /* Only have one channel to worry about. */
- ast_stream_and_wait(peer, "pbx-parkingfailed", "");
- } else if (peer) {
- /* Have two different channels to worry about. */
- play_message_on_chan(peer, rchan, "failure message", "pbx-parkingfailed");
- }
- }
- return -1;
- }
-
- /* Setup the extensions and such */
- set_c_e_p(chan, ast_channel_context(rchan), ast_channel_exten(rchan), ast_channel_priority(rchan));
-
- /* Setup the macro extension and such */
- ast_channel_macrocontext_set(chan, ast_channel_macrocontext(rchan));
- ast_channel_macroexten_set(chan, ast_channel_macroexten(rchan));
- ast_channel_macropriority_set(chan, ast_channel_macropriority(rchan));
-
- /* Manually do the masquerade to make sure it is complete. */
- ast_do_masquerade(chan);
-
- if (peer == rchan) {
- peer = chan;
- }
-
- /* parking space reserved, return code check unnecessary */
- park_call_full(chan, peer, args);
-
- return 0;
-}
-
-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)
-{
- int res;
- char *parse;
- const char *app_data;
- struct ast_exten *exten;
- struct park_app_args app_args;
- struct ast_park_call_args args = {
- .timeout = timeout,
- .extout = extout,
- };
-
- if (parker) {
- args.orig_chan_name = ast_strdupa(ast_channel_name(parker));
- }
- if (!park_exten || !park_context) {
- return masq_park_call(park_me, parker, &args);
- }
-
- /*
- * Determiine if the specified park extension has an exclusive
- * parking lot to use.
- */
- if (parker && parker != park_me) {
- ast_autoservice_start(park_me);
- }
- exten = get_parking_exten(park_exten, parker, park_context);
- if (exten) {
- app_data = ast_get_extension_app_data(exten);
- if (!app_data) {
- app_data = "";
- }
- parse = ast_strdupa(app_data);
- AST_STANDARD_APP_ARGS(app_args, parse);
-
- if (!ast_strlen_zero(app_args.pl_name)) {
- /* Find the specified exclusive parking lot */
- args.parkinglot = find_parkinglot(app_args.pl_name);
- if (!args.parkinglot && parkeddynamic) {
- args.parkinglot = create_dynamic_parkinglot(app_args.pl_name, park_me);
- }
- }
- }
- if (parker && parker != park_me) {
- ast_autoservice_stop(park_me);
- }
-
- res = masq_park_call(park_me, parker, &args);
- if (args.parkinglot) {
- parkinglot_unref(args.parkinglot);
- }
- return res;
-}
-
-int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int timeout, int *extout)
-{
- struct ast_park_call_args args = {
- .timeout = timeout,
- .extout = extout,
- };
-
- if (peer) {
- args.orig_chan_name = ast_strdupa(ast_channel_name(peer));
- }
- return masq_park_call(rchan, peer, &args);
-}
-
-/*!
- * \internal
- * \brief Play file to specified channel.
- *
- * \param play_to Channel to play audiofile to.
- * \param other Channel to put in autoservice while playing file.
- * \param msg Descriptive name of message type being played.
- * \param audiofile Audio file to play.
- *
- * \retval 0 on success.
- * \retval -1 on error. (Couldn't play file, a channel hung up,...)
- */
-static int play_message_on_chan(struct ast_channel *play_to, struct ast_channel *other, const char *msg, const char *audiofile)
-{
- /* Put other channel in autoservice. */
- if (ast_autoservice_start(other)) {
- return -1;
- }
- ast_autoservice_ignore(other, AST_FRAME_DTMF_BEGIN);
- ast_autoservice_ignore(other, AST_FRAME_DTMF_END);
- if (ast_stream_and_wait(play_to, audiofile, "")) {
- ast_log(LOG_WARNING, "Failed to play %s '%s'!\n", msg, audiofile);
- ast_autoservice_stop(other);
- return -1;
- }
- if (ast_autoservice_stop(other)) {
- return -1;
- }
- return 0;
-}
-
-/*!
- * \internal
* \brief Get the extension for a given builtin feature
*
* \pre expects features_lock to be readlocked
@@ -2643,46 +1847,6 @@ static void *do_parking_thread(void *ignore)
return NULL; /* Never reached */
}
-/*! \brief Find parkinglot by name */
-static struct ast_parkinglot *find_parkinglot(const char *name)
-{
- struct ast_parkinglot *parkinglot;
-
- if (ast_strlen_zero(name)) {
- return NULL;
- }
-
- parkinglot = ao2_find(parkinglots, (void *) name, 0);
- if (parkinglot) {
- ast_debug(1, "Found Parking lot: %s\n", parkinglot->name);
- }
-
- return parkinglot;
-}
-
-/*! \brief Copy parkinglot and store it with new name */
-static struct ast_parkinglot *copy_parkinglot(const char *name, const struct ast_parkinglot *parkinglot)
-{
- struct ast_parkinglot *copylot;
-
- if ((copylot = find_parkinglot(name))) { /* Parkinglot with that name already exists */
- ao2_ref(copylot, -1);
- return NULL;
- }
-
- copylot = create_parkinglot(name);
- if (!copylot) {
- return NULL;
- }
-
- ast_debug(1, "Building parking lot %s\n", name);
-
- /* Copy the source parking lot configuration. */
- copylot->cfg = parkinglot->cfg;
-
- return copylot;
-}
-
AST_APP_OPTIONS(park_call_options, BEGIN_OPTIONS
AST_APP_OPTION('r', AST_PARK_OPT_RINGING),
AST_APP_OPTION('R', AST_PARK_OPT_RANDOMIZE),
@@ -2699,49 +1863,6 @@ static void parkinglot_unref(struct ast_parkinglot *parkinglot)
ao2_ref(parkinglot, -1);
}
-static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot)
-{
- int refcount;
-
- refcount = ao2_ref(parkinglot, +1);
- ast_debug(3, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount + 1);
- return parkinglot;
-}
-
-/*! \brief Destroy a parking lot */
-static void parkinglot_destroy(void *obj)
-{
- struct ast_parkinglot *doomed = obj;
-
- /*
- * No need to destroy parked calls here because any parked call
- * holds a parking lot reference. Therefore the parkings list
- * must be empty.
- */
- ast_assert(AST_LIST_EMPTY(&doomed->parkings));
- AST_LIST_HEAD_DESTROY(&doomed->parkings);
-}
-
-/*! \brief Allocate parking lot structure */
-static struct ast_parkinglot *create_parkinglot(const char *name)
-{
- struct ast_parkinglot *newlot;
-
- if (ast_strlen_zero(name)) { /* No name specified */
- return NULL;
- }
-
- newlot = ao2_alloc(sizeof(*newlot), parkinglot_destroy);
- if (!newlot)
- return NULL;
-
- ast_copy_string(newlot->name, name, sizeof(newlot->name));
- newlot->cfg.is_invalid = 1;/* No config is set yet. */
- AST_LIST_HEAD_INIT(&newlot->parkings);
-
- return newlot;
-}
-
/*! Default configuration for default parking lot. */
static const struct parkinglot_cfg parkinglot_cfg_default_default = {
.mohclass = "default",
@@ -2764,26 +1885,6 @@ static const struct parkinglot_cfg parkinglot_cfg_default = {
.comebacktoorigin = DEFAULT_COMEBACK_TO_ORIGIN,
};
-/*!
- * \internal
- * \brief Activate the given parkinglot.
- *
- * \param parkinglot Parking lot to activate.
- *
- * \details
- * Insert into the dialplan the context, parking lot access
- * extension, and optional dialplan hints.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-static int parkinglot_activate(struct ast_parkinglot *parkinglot)
-{
- /* XXX All parking stuff is being replaced by res_parking */
- parkinglot->disabled = 1;
- return -1;
-}
-
int ast_features_reload(void)
{
struct ast_context *con;
@@ -3636,243 +2737,6 @@ done:
}
#if defined(TEST_FRAMEWORK)
-/*!
- * \internal
- * \brief Convert parking spaces map list to a comma separated string.
- *
- * \param str String buffer to fill.
- * \param spaces Parking spaces map list to convert.
- *
- * \return Nothing
- */
-static void create_spaces_str(struct ast_str **str, struct parking_dp_space_map *spaces)
-{
- const char *comma;
- struct parking_dp_spaces *cur;
-
- ast_str_reset(*str);
- comma = "";
- AST_LIST_TRAVERSE(spaces, cur, node) {
- if (cur->start == cur->stop) {
- ast_str_append(str, 0, "%s%d", comma, cur->start);
- } else {
- ast_str_append(str, 0, "%s%d-%d", comma, cur->start, cur->stop);
- }
- comma = ",";
- }
-}
-#endif /* defined(TEST_FRAMEWORK) */
-
-#if defined(TEST_FRAMEWORK)
-/*!
- * \internal
- * \brief Compare parking spaces map to what is expected.
- *
- * \param test Unit test context.
- * \param spaces Parking spaces map list to check.
- * \param expected String to compare with.
- * \param what What is being compared.
- *
- * \retval 0 successful compare.
- * \retval nonzero if failed to compare.
- */
-static int check_spaces(struct ast_test *test, struct parking_dp_space_map *spaces, const char *expected, const char *what)
-{
- int cmp;
- struct ast_str *str = ast_str_alloca(1024);
-
- create_spaces_str(&str, spaces);
- cmp = strcmp(expected, ast_str_buffer(str));
- if (cmp) {
- ast_test_status_update(test,
- "Unexpected parking space map for %s. Expect:'%s' Got:'%s'\n",
- what, expected, ast_str_buffer(str));
- }
- return cmp;
-}
-#endif /* defined(TEST_FRAMEWORK) */
-
-#if defined(TEST_FRAMEWORK)
-/*!
- * \internal
- * \brief Add a dead space to the dead spaces list.
- *
- * \param context Dead spaces list ptr pretending to be a context name ptr.
- * \param space Dead space to add to the list.
- *
- * \return Nothing
- */
-static void test_add_dead_space(const char *context, int space)
-{
- struct parking_dp_space_map *dead_spaces = (struct parking_dp_space_map *) context;
-
- usage_context_add_spaces(dead_spaces, space, space, NULL, 0);
-}
-#endif /* defined(TEST_FRAMEWORK) */
-
-#if defined(TEST_FRAMEWORK)
-struct test_map {
- const char *ramp;
- int start;
- int stop;
- const char *expect;
-};
-
-/*!
- * \internal
- * \brief Build a parking lot dialplan usage test map from a table.
- *
- * \param test Unit test context.
- * \param lot Parking lot to use to build test usage map.
- * \param table_name Name of passed in table.
- * \param table Usage information to put in the usage map.
- * \param num_entries Number of entries in the table.
- *
- * \retval Created context node on success.
- * \retval NULL on error.
- */
-static struct parking_dp_context *test_build_maps(struct ast_test *test,
- struct ast_parkinglot *lot, const char *table_name, const struct test_map *table,
- size_t num_entries)
-{
- struct parking_dp_context *ctx_node;
- int cur_index = 0;
- char what[40];
-
- snprintf(what, sizeof(what), "%s[%d]", table_name, cur_index);
- ast_copy_string(lot->cfg.parkext, table->ramp, sizeof(lot->cfg.parkext));
- lot->cfg.parking_start = table->start;
- lot->cfg.parking_stop = table->stop;
- ctx_node = build_dialplan_useage_context(lot);
- if (!ctx_node) {
- ast_test_status_update(test, "Failed to create parking lot context map for %s\n",
- what);
- return NULL;
- }
- if (check_spaces(test, &ctx_node->spaces, table->expect, what)) {
- destroy_dialplan_usage_context(ctx_node);
- return NULL;
- }
- while (--num_entries) {
- ++cur_index;
- ++table;
- snprintf(what, sizeof(what), "%s[%d]", table_name, cur_index);
- ast_copy_string(lot->cfg.parkext, table->ramp, sizeof(lot->cfg.parkext));
- lot->cfg.parking_start = table->start;
- lot->cfg.parking_stop = table->stop;
- if (dialplan_usage_add_parkinglot_data(ctx_node, lot, 1)) {
- ast_test_status_update(test, "Failed to add parking lot data for %s\n", what);
- destroy_dialplan_usage_context(ctx_node);
- return NULL;
- }
- if (check_spaces(test, &ctx_node->spaces, table->expect, what)) {
- destroy_dialplan_usage_context(ctx_node);
- return NULL;
- }
- }
- return ctx_node;
-}
-
-static const struct test_map test_old_ctx[] = {
- /* The following order of building ctx is important to test adding items to the lists. */
- { "702", 14, 15, "14-15" },
- { "700", 10, 11, "10-11,14-15" },
- { "701", 18, 19, "10-11,14-15,18-19" },
- { "703", 12, 13, "10-15,18-19" },
- { "704", 16, 17, "10-19" },
-
- /* Parking ramp and space conflicts are intended with these lines. */
- { "704", 9, 19, "9-19" },
- { "704", 9, 20, "9-20" },
- { "704", 8, 21, "8-21" },
-
- /* Add more spaces to ctx to test removing dead parking spaces. */
- { "705", 23, 25, "8-21,23-25" },
- { "706", 28, 31, "8-21,23-25,28-31" },
- { "707", 33, 34, "8-21,23-25,28-31,33-34" },
- { "708", 38, 40, "8-21,23-25,28-31,33-34,38-40" },
- { "709", 42, 43, "8-21,23-25,28-31,33-34,38-40,42-43" },
-};
-
-static const struct test_map test_new_ctx[] = {
- { "702", 4, 5, "4-5" },
- { "704", 24, 26, "4-5,24-26" },
- { "709", 29, 30, "4-5,24-26,29-30" },
- { "710", 32, 35, "4-5,24-26,29-30,32-35" },
- { "711", 37, 39, "4-5,24-26,29-30,32-35,37-39" },
-};
-#endif /* defined(TEST_FRAMEWORK) */
-
-#if defined(TEST_FRAMEWORK)
-/*!
- * \internal
- * \brief Test parking dialplan usage map code.
- *
- * \param test Unit test context.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-static int test_dialplan_usage_map(struct ast_test *test)
-{
- struct parking_dp_context *old_ctx;
- struct parking_dp_context *new_ctx;
- struct ast_parkinglot *lot;
- struct parking_dp_spaces *spaces;
- struct parking_dp_space_map dead_spaces = AST_LIST_HEAD_NOLOCK_INIT_VALUE;
- int res;
-
- ast_test_status_update(test, "Test parking dialplan usage map code\n");
-
- lot = create_parkinglot("test_lot");
- if (!lot) {
- return -1;
- }
- ast_copy_string(lot->cfg.parking_con, "test-ctx", sizeof(lot->cfg.parking_con));
- lot->cfg.parkext_exclusive = 1;
-
- ast_test_status_update(test,
- "Build old_ctx map\n");
- ast_log(LOG_NOTICE, "6 Ramp and space conflict warnings are expected.\n");
- old_ctx = test_build_maps(test, lot, "test_old_ctx", test_old_ctx,
- ARRAY_LEN(test_old_ctx));
- if (!old_ctx) {
- ao2_ref(lot, -1);
- return -1;
- }
-
- ast_test_status_update(test, "Build new_ctx map\n");
- new_ctx = test_build_maps(test, lot, "test_new_ctx", test_new_ctx,
- ARRAY_LEN(test_new_ctx));
- if (!new_ctx) {
- res = -1;
- goto fail_old_ctx;
- }
-
- ast_test_status_update(test, "Test removing dead parking spaces\n");
- remove_dead_spaces_usage((void *) &dead_spaces, &old_ctx->spaces,
- &new_ctx->spaces, test_add_dead_space);
- if (check_spaces(test, &dead_spaces, "8-21,23,28,31,40,42-43", "dead_spaces")) {
- res = -1;
- goto fail_dead_spaces;
- }
-
- res = 0;
-
-fail_dead_spaces:
- while ((spaces = AST_LIST_REMOVE_HEAD(&dead_spaces, node))) {
- ast_free(spaces);
- }
- destroy_dialplan_usage_context(new_ctx);
-
-fail_old_ctx:
- destroy_dialplan_usage_context(old_ctx);
- ao2_ref(lot, -1);
- return res;
-}
-#endif /* defined(TEST_FRAMEWORK) */
-
-#if defined(TEST_FRAMEWORK)
static int fake_fixup(struct ast_channel *clonechan, struct ast_channel *original)
{
return 0;
@@ -3905,209 +2769,6 @@ static struct ast_channel *create_test_channel(const struct ast_channel_tech *fa
}
#endif /* defined(TEST_FRAMEWORK) */
-#if defined(TEST_FRAMEWORK)
-static int unpark_test_channel(struct ast_channel *toremove, struct ast_park_call_args *args)
-{
- struct ast_context *con;
- struct parkeduser *pu_toremove;
- int res = 0;
-
- args->pu->notquiteyet = 1; /* go ahead and stop processing the test parking */
-
- AST_LIST_LOCK(&args->pu->parkinglot->parkings);
- AST_LIST_TRAVERSE_SAFE_BEGIN(&args->pu->parkinglot->parkings, pu_toremove, list) {
- if (pu_toremove == args->pu) {
- AST_LIST_REMOVE_CURRENT(list);
- break;
- }
- }
- AST_LIST_TRAVERSE_SAFE_END;
- AST_LIST_UNLOCK(&args->pu->parkinglot->parkings);
-
- if (!pu_toremove) {
- ast_log(LOG_WARNING, "Whoa, could not find parking test call!\n");
- return -1;
- }
-
- con = ast_context_find(args->pu->parkinglot->cfg.parking_con);
- if (con) {
- if (ast_context_remove_extension2(con, args->pu->parkingexten, 1, NULL, 0)) {
- ast_log(LOG_WARNING, "Whoa, failed to remove the parking extension!\n");
- res = -1;
- } else {
- notify_metermaids(args->pu->parkingexten,
- pu_toremove->parkinglot->cfg.parking_con, AST_DEVICE_NOT_INUSE);
- }
- } else {
- ast_log(LOG_WARNING, "Whoa, no parking context?\n");
- res = -1;
- }
-
- parkinglot_unref(pu_toremove->parkinglot);
- ast_free(pu_toremove);
- args->pu = NULL;
-
- if (!res && toremove) {
- ast_hangup(toremove);
- }
- return res;
-}
-#endif /* defined(TEST_FRAMEWORK) */
-
-#if defined(TEST_FRAMEWORK)
-AST_TEST_DEFINE(features_test)
-{
- struct ast_channel *test_channel1 = NULL;
- struct ast_channel *parked_chan = NULL;
- struct ast_parkinglot *dynlot;
- struct ast_park_call_args args = {
- .timeout = DEFAULT_PARK_TIME,
- };
-
- int res = 0;
-
- static const struct ast_channel_tech fake_tech = {
- .fixup = fake_fixup, /* silence warning from masquerade */
- };
-
- static const char unique_lot_1[] = "myuniquetestparkinglot314";
- static const char unique_lot_2[] = "myuniquetestparkinglot3141592654";
- static const char unique_context_1[] = "myuniquetestcontext314";
- static const char unique_context_2[] = "myuniquetestcontext3141592654";
- static const char parkinglot_parkext[] = "750";
- static const char parkinglot_range[] = "751-760";
-
- switch (cmd) {
- case TEST_INIT:
- info->name = "features_test";
- info->category = "/main/features/";
- info->summary = "Features unit test";
- info->description =
- "Tests whether parking respects PARKINGLOT settings";
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
-
- if (test_dialplan_usage_map(test)) {
- res = -1;
- goto exit_features_test;
- }
-
- /* changing a config option is a bad practice, but must be done in this case */
- parkeddynamic = 1;
-
- ast_test_status_update(test, "Test parking functionality with defaults\n");
- if (!(test_channel1 = create_test_channel(&fake_tech))) {
- res = -1;
- goto exit_features_test;
- }
- if (park_call_full(test_channel1, NULL, &args)) {
- res = -1;
- goto exit_features_test;
- }
- if (unpark_test_channel(test_channel1, &args)) {
- res = -1;
- goto exit_features_test;
- }
-
-
- ast_test_status_update(test, "Check that certain parking options are respected\n");
- if (!(test_channel1 = create_test_channel(&fake_tech))) {
- res = -1;
- goto exit_features_test;
- }
- pbx_builtin_setvar_helper(test_channel1, "PARKINGLOT", unique_lot_1);
- pbx_builtin_setvar_helper(test_channel1, "PARKINGDYNCONTEXT", unique_context_1);
- pbx_builtin_setvar_helper(test_channel1, "PARKINGDYNEXTEN", parkinglot_parkext);
- pbx_builtin_setvar_helper(test_channel1, "PARKINGDYNPOS", parkinglot_range);
- if (park_call_full(test_channel1, NULL, &args)) {
- res = -1;
- goto exit_features_test;
- }
- /* grab newly created parking lot for destruction in the end */
- dynlot = args.pu->parkinglot;
- if (args.pu->parkingnum != 751
- || strcmp(dynlot->name, unique_lot_1)
- || strcmp(dynlot->cfg.parking_con, unique_context_1)
- || strcmp(dynlot->cfg.parkext, parkinglot_parkext)
- || dynlot->cfg.parking_start != 751
- || dynlot->cfg.parking_stop != 760) {
- ast_test_status_update(test, "Parking settings were not respected\n");
- ast_test_status_update(test, "Dyn-name:%s\n", dynlot->name);
- ast_test_status_update(test, "Dyn-context:%s\n", dynlot->cfg.parking_con);
- ast_test_status_update(test, "Dyn-parkext:%s\n", dynlot->cfg.parkext);
- ast_test_status_update(test, "Dyn-parkpos:%d-%d\n", dynlot->cfg.parking_start,
- dynlot->cfg.parking_stop);
- ast_test_status_update(test, "Parked in space:%d\n", args.pu->parkingnum);
- if (!unpark_test_channel(test_channel1, &args)) {
- test_channel1 = NULL;
- }
- res = -1;
- goto exit_features_test;
- } else {
- ast_test_status_update(test, "Parking settings for non-masquerading park verified\n");
- }
- if (unpark_test_channel(test_channel1, &args)) {
- res = -1;
- goto exit_features_test;
- }
-
-
- ast_test_status_update(test, "Check #2 that certain parking options are respected\n");
- if (!(test_channel1 = create_test_channel(&fake_tech))) {
- res = -1;
- goto exit_features_test;
- }
- pbx_builtin_setvar_helper(test_channel1, "PARKINGLOT", unique_lot_2);
- pbx_builtin_setvar_helper(test_channel1, "PARKINGDYNCONTEXT", unique_context_2);
- pbx_builtin_setvar_helper(test_channel1, "PARKINGDYNEXTEN", parkinglot_parkext);
- pbx_builtin_setvar_helper(test_channel1, "PARKINGDYNPOS", parkinglot_range);
- if (masq_park_call(test_channel1, NULL, &args)) {
- res = -1;
- goto exit_features_test;
- }
- /* hangup zombie channel */
- ast_hangup(test_channel1);
- test_channel1 = NULL;
-
- dynlot = args.pu->parkinglot;
- if (args.pu->parkingnum != 751
- || strcmp(dynlot->name, unique_lot_2)
- || strcmp(dynlot->cfg.parking_con, unique_context_2)
- || strcmp(dynlot->cfg.parkext, parkinglot_parkext)
- || dynlot->cfg.parking_start != 751
- || dynlot->cfg.parking_stop != 760) {
- ast_test_status_update(test, "Parking settings were not respected\n");
- ast_test_status_update(test, "Dyn-name:%s\n", dynlot->name);
- ast_test_status_update(test, "Dyn-context:%s\n", dynlot->cfg.parking_con);
- ast_test_status_update(test, "Dyn-parkext:%s\n", dynlot->cfg.parkext);
- ast_test_status_update(test, "Dyn-parkpos:%d-%d\n", dynlot->cfg.parking_start,
- dynlot->cfg.parking_stop);
- ast_test_status_update(test, "Parked in space:%d\n", args.pu->parkingnum);
- res = -1;
- } else {
- ast_test_status_update(test, "Parking settings for masquerading park verified\n");
- }
-
- /* find the real channel */
- parked_chan = ast_channel_get_by_name("TestChannel1");
- if (unpark_test_channel(parked_chan, &args)) {
- ast_hangup(parked_chan);
- res = -1;
- }
-
-
-exit_features_test:
-
- ast_hangup(test_channel1);
-
- force_reload_load = 1;
- ast_features_reload();
- return res ? AST_TEST_FAIL : AST_TEST_PASS;
-}
-#endif /* defined(TEST_FRAMEWORK) */
-
/*! \internal \brief Clean up resources on Asterisk shutdown */
static void features_shutdown(void)
{
@@ -4152,9 +2813,6 @@ int ast_features_init(void)
res |= ast_manager_register_xml_core("Bridge", EVENT_FLAG_CALL, action_bridge);
res |= ast_devstate_prov_add("Park", metermaidstate);
-#if defined(TEST_FRAMEWORK)
- res |= AST_TEST_REGISTER(features_test);
-#endif /* defined(TEST_FRAMEWORK) */
if (res) {
features_shutdown();
diff --git a/main/parking.c b/main/parking.c
index 710a3c8c7..83c599ba0 100644
--- a/main/parking.c
+++ b/main/parking.c
@@ -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;
}
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;
}
diff --git a/res/res_parking.c b/res/res_parking.c
index 9a13db098..6efb79543 100644
--- a/res/res_parking.c
+++ b/res/res_parking.c
@@ -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));