diff options
-rw-r--r-- | channels/chan_dahdi.c | 28 | ||||
-rw-r--r-- | channels/chan_iax2.c | 2 | ||||
-rw-r--r-- | channels/chan_mgcp.c | 20 | ||||
-rw-r--r-- | channels/chan_skinny.c | 75 | ||||
-rw-r--r-- | channels/sig_analog.c | 25 | ||||
-rw-r--r-- | include/asterisk/features.h | 44 | ||||
-rw-r--r-- | include/asterisk/parking.h | 150 | ||||
-rw-r--r-- | main/bridge.c | 40 | ||||
-rw-r--r-- | main/bridge_channel.c | 17 | ||||
-rw-r--r-- | main/features.c | 1342 | ||||
-rw-r--r-- | main/parking.c | 88 | ||||
-rw-r--r-- | res/parking/parking_bridge_features.c | 214 | ||||
-rw-r--r-- | res/res_parking.c | 9 |
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)); |