diff options
-rw-r--r-- | CHANGES | 27 | ||||
-rw-r--r-- | UPGRADE.txt | 14 | ||||
-rw-r--r-- | bridges/bridge_builtin_features.c | 18 | ||||
-rw-r--r-- | configs/chan_dahdi.conf.sample | 5 | ||||
-rw-r--r-- | configs/iax.conf.sample | 3 | ||||
-rw-r--r-- | configs/sip.conf.sample | 3 | ||||
-rw-r--r-- | configs/skinny.conf.sample | 3 | ||||
-rw-r--r-- | include/asterisk/bridging.h | 85 | ||||
-rw-r--r-- | include/asterisk/bridging_features.h | 12 | ||||
-rw-r--r-- | main/bridging.c | 361 | ||||
-rw-r--r-- | main/features.c | 148 |
11 files changed, 596 insertions, 83 deletions
@@ -240,6 +240,31 @@ Core reason to any string. It also allows for custom strings to be read as the redirecting reason from SIP Diversion headers. + * For DTMF blind and attended transfers, the channel variable TRANSFER_CONTEXT + must be on the channel initiating the transfer to have any effect. + + * The channel variable ATTENDED_TRANSFER_COMPLETE_SOUND is no longer channel + driver specific. If the channel variable is set on the transferrer channel, + the sound will be played to the target of an attended transfer. + + * The channel variable BRIDGEPEER becomes a comma separated list of peers in + a multi-party bridge. The BRIDGEPEER value can have a maximum of 10 peers + listed. Any more peers in the bridge will not be included in the list. + BRIDGEPEER is not valid in holding bridges like parking since those channels + do not talk to each other even though they are in a bridge. + + * The channel variable BRIDGEPVTCALLID is only valid for two party bridges + and will contain a value if the BRIDGEPEER's channel driver supports it. + + * The channel variable DYNAMIC_PEERNAME is redundant with BRIDGEPEER and is + removed. The more useful DYNAMIC_WHO_ACTIVATED gives the channel name that + activated the dynamic feature. + + * The channel variables DYNAMIC_FEATURENAME and DYNAMIC_WHO_ACTIVATED are set + only on the channel executing the dynamic feature. Executing a dynamic + feature on the bridge peer in a multi-party bridge will execute it on all + peers of the activating channel. + Realtime ------------------ * Dynamic realtime tables for SIP Users can now include a 'path' field. This @@ -247,7 +272,7 @@ Realtime tables can also use the 'supportpath' field to enable Path header support. * LDAP realtime configurations for SIP Users now have the AstAccountPathSupport - objectIdentifier. This maps to the supportpath option in sip.conf. + objectIdentifier. This maps to the supportpath option in sip.conf. RTP ------------------ diff --git a/UPGRADE.txt b/UPGRADE.txt index e2e709008..e8a399652 100644 --- a/UPGRADE.txt +++ b/UPGRADE.txt @@ -38,6 +38,11 @@ CEL: - The Uniqueid field for a channel is now a stable identifier, and will not change due to transfers, parking, etc. +Core: + - The following channel variables have changed behavior which is described in + the CHANGES file: TRANSFER_CONTEXT, BRIDGEPEER, BRIDGEPVTCALLID, + ATTENDED_TRANSFER_COMPLETE_SOUND, DYNAMIC_FEATURENAME, and DYNAMIC_PEERNAME. + Queues: - Queue logging for PAUSEALL/UNPAUSEALL now only occurs if the interface this is performed on is a member of at least one queue. @@ -86,9 +91,12 @@ Dialplan: Features: - The features.conf [applicationmap] <FeatureName> ActivatedBy option is - no longer honored. The feature is activated by which channel - DYNAMIC_FEATURES includes the feature is on. Use predial to set different - values of DYNAMIC_FEATURES on the channels + no longer honored. The feature is always activated by the channel that has + DYNAMIC_FEATURES defined on it when it enters the bridge. Use predial to set + different values of DYNAMIC_FEATURES on the channels + + - Executing a dynamic feature on the bridge peer in a multi-party bridge will + execute it on all peers of the activating channel. Parking: - The arguments for the Park, ParkedCall, and ParkAndAnnounce applications have diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c index e11b280cd..3a081c83d 100644 --- a/bridges/bridge_builtin_features.c +++ b/bridges/bridge_builtin_features.c @@ -134,6 +134,9 @@ static struct ast_channel *dial_transfer(struct ast_channel *caller, const char return NULL; } + /* Who is transferring the call. */ + pbx_builtin_setvar_helper(chan, "TRANSFERERNAME", ast_channel_name(caller)); + /* Before we actually dial out let's inherit appropriate information. */ copy_caller_data(chan, caller); @@ -275,6 +278,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg struct ast_bridge_features caller_features; int xfer_failed; struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt; + const char *complete_sound; const char *context; enum atxfer_code transfer_code = ATXFER_INCOMPLETE; const char *atxfer_abort; @@ -407,6 +411,20 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg ast_bridge_destroy(attended_bridge); ast_bridge_features_cleanup(&caller_features); + /* Is there a courtesy sound to play to the peer? */ + ast_channel_lock(bridge_channel->chan); + complete_sound = pbx_builtin_getvar_helper(bridge_channel->chan, + "ATTENDED_TRANSFER_COMPLETE_SOUND"); + if (!ast_strlen_zero(complete_sound)) { + complete_sound = ast_strdupa(complete_sound); + } else { + complete_sound = NULL; + } + ast_channel_unlock(bridge_channel->chan); + if (complete_sound) { + pbx_builtin_setvar_helper(peer, "BRIDGE_PLAY_SOUND", complete_sound); + } + xfer_failed = -1; switch (transfer_code) { case ATXFER_INCOMPLETE: diff --git a/configs/chan_dahdi.conf.sample b/configs/chan_dahdi.conf.sample index 26bd0cbd7..18ac20294 100644 --- a/configs/chan_dahdi.conf.sample +++ b/configs/chan_dahdi.conf.sample @@ -863,12 +863,13 @@ pickupgroup=1 ;namedcallgroup=engineering,sales,netgroup,protgroup ;namedpickupgroup=sales -; Channel variable to be set for all calls from this channel +; Channel variables to be set for all calls from this channel ;setvar=CHANNEL=42 ;setvar=ATTENDED_TRANSFER_COMPLETE_SOUND=beep ; This channel variable will ; cause the given audio file to ; be played upon completion of - ; an attended transfer. + ; an attended transfer to the + ; target of the transfer. ; ; Specify whether the channel should be answered immediately or if the simple diff --git a/configs/iax.conf.sample b/configs/iax.conf.sample index 9b5d4bc78..e26c6fd9c 100644 --- a/configs/iax.conf.sample +++ b/configs/iax.conf.sample @@ -550,7 +550,8 @@ inkeys=freeworlddialup ;setvar=ATTENDED_TRANSFER_COMPLETE_SOUND=beep ; This channel variable will ; cause the given audio file to ; be played upon completion of - ; an attended transfer. + ; an attended transfer to the + ; target of the transfer. ;dbsecret=mysecrets/place ; Secrets can be stored in astdb, too ;transfer=no ; Disable IAX2 native transfer ;transfer=mediaonly ; When doing IAX2 native transfers, transfer only diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index a0ceabeb7..57692b2a8 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -1518,7 +1518,8 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls ;setvar=ATTENDED_TRANSFER_COMPLETE_SOUND=beep ; This channel variable will ; cause the given audio file to ; be played upon completion of - ; an attended transfer. + ; an attended transfer to the + ; target of the transfer. ;[pre14-asterisk] ;type=friend diff --git a/configs/skinny.conf.sample b/configs/skinny.conf.sample index 0a618ac5b..60dc87305 100644 --- a/configs/skinny.conf.sample +++ b/configs/skinny.conf.sample @@ -131,7 +131,8 @@ keepalive=120 ;setvar=ATTENDED_TRANSFER_COMPLETE_SOUND=beep ; This channel variable will ; cause the given audio file to ; be played upon completion of - ; an attended transfer. + ; an attended transfer to the + ; target of the transfer. ;mailbox=500 ;callwaiting=yes ;transfer=yes diff --git a/include/asterisk/bridging.h b/include/asterisk/bridging.h index b589874b0..9d3f5b3cf 100644 --- a/include/asterisk/bridging.h +++ b/include/asterisk/bridging.h @@ -219,10 +219,12 @@ enum ast_bridge_action_type { AST_BRIDGE_ACTION_TALKING_STOP, /*! Bridge channel is to play the indicated sound file. */ AST_BRIDGE_ACTION_PLAY_FILE, - /*! Bridge channel is to get parked. */ - AST_BRIDGE_ACTION_PARK, /*! Bridge channel is to run the indicated application. */ AST_BRIDGE_ACTION_RUN_APP, + /*! Bridge channel is to run the custom callback routine. */ + AST_BRIDGE_ACTION_CALLBACK, + /*! Bridge channel is to get parked. */ + AST_BRIDGE_ACTION_PARK, /*! Bridge channel is to execute a blind transfer. */ AST_BRIDGE_ACTION_BLIND_TRANSFER, /*! Bridge channel is to execute an attended transfer */ @@ -1209,22 +1211,6 @@ typedef void (*ast_bridge_custom_play_fn)(struct ast_bridge_channel *bridge_chan void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class); /*! - * \brief Have a bridge channel park a channel in the bridge - * \since 12.0.0 - * - * \param bridge_channel Bridge channel performing the parking - * \param parkee_uuid Unique id of the channel we want to park - * \param parker_uuid Unique id of the channel parking the call - * \param app_data string indicating data used for park application (NULL allowed) - * - * \note This is intended to be called by bridge hooks. - * - * \return Nothing - */ -void ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, - const char *parker_uuid, const char *app_data); - -/*! * \brief Write a bridge action play file frame into the bridge. * \since 12.0.0 * @@ -1259,6 +1245,69 @@ void ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel void ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class); /*! + * \brief Custom callback run on a bridge channel. + * + * \param bridge_channel Which channel to operate on. + * \param payload Data to pass to the callback. (NULL if none). + * \param payload_size Size of the payload if payload is non-NULL. A number otherwise. + * + * \note The payload MUST NOT have any resources that need to be freed. + * + * \return Nothing + */ +typedef void (*ast_bridge_custom_callback_fn)(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size); + +/*! + * \brief Write a bridge action custom callback frame into the bridge. + * \since 12.0.0 + * + * \param bridge_channel Which channel is putting the frame into the bridge + * \param callback Custom callback run on a bridge channel. + * \param payload Data to pass to the callback. (NULL if none). + * \param payload_size Size of the payload if payload is non-NULL. A number otherwise. + * + * \note The payload MUST NOT have any resources that need to be freed. + * + * \note This is intended to be called by bridge hooks. + * + * \return Nothing + */ +void ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size); + +/*! + * \brief Queue a bridge action custom callback frame onto the bridge channel. + * \since 12.0.0 + * + * \param bridge_channel Which channel to put the frame onto. + * \param callback Custom callback run on a bridge channel. + * \param payload Data to pass to the callback. (NULL if none). + * \param payload_size Size of the payload if payload is non-NULL. A number otherwise. + * + * \note The payload MUST NOT have any resources that need to be freed. + * + * \note This is intended to be called by bridge hooks. + * + * \return Nothing + */ +void ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size); + +/*! + * \brief Have a bridge channel park a channel in the bridge + * \since 12.0.0 + * + * \param bridge_channel Bridge channel performing the parking + * \param parkee_uuid Unique id of the channel we want to park + * \param parker_uuid Unique id of the channel parking the call + * \param app_data string indicating data used for park application (NULL allowed) + * + * \note This is intended to be called by bridge hooks. + * + * \return Nothing + */ +void ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, + const char *parker_uuid, const char *app_data); + +/*! * \brief Restore the formats of a bridge channel's channel to how they were before bridge_channel_join * \since 12.0.0 * diff --git a/include/asterisk/bridging_features.h b/include/asterisk/bridging_features.h index 1679f04a0..1ee13a46a 100644 --- a/include/asterisk/bridging_features.h +++ b/include/asterisk/bridging_features.h @@ -249,8 +249,7 @@ struct ast_bridge_features { * \brief Structure that contains configuration information for the blind transfer built in feature */ struct ast_bridge_features_blind_transfer { -/* BUGBUG the context should be figured out based upon TRANSFER_CONTEXT channel variable of A/B or current context of A/B. More appropriate for when channel moved to other bridges. */ - /*! Context to use for transfers */ + /*! Context to use for transfers (If not empty.) */ char context[AST_MAX_CONTEXT]; }; @@ -258,14 +257,13 @@ struct ast_bridge_features_blind_transfer { * \brief Structure that contains configuration information for the attended transfer built in feature */ struct ast_bridge_features_attended_transfer { -/* BUGBUG the context should be figured out based upon TRANSFER_CONTEXT channel variable of A/B or current context of A/B. More appropriate for when channel moved to other bridges. */ - /*! Context to use for transfers */ + /*! Context to use for transfers (If not empty.) */ char context[AST_MAX_CONTEXT]; - /*! DTMF string used to abort the transfer */ + /*! DTMF string used to abort the transfer (If not empty.) */ char abort[MAXIMUM_DTMF_FEATURE_STRING]; - /*! DTMF string used to turn the transfer into a three way conference */ + /*! DTMF string used to turn the transfer into a three way conference (If not empty.) */ char threeway[MAXIMUM_DTMF_FEATURE_STRING]; - /*! DTMF string used to complete the transfer */ + /*! DTMF string used to complete the transfer (If not empty.) */ char complete[MAXIMUM_DTMF_FEATURE_STRING]; }; diff --git a/main/bridging.c b/main/bridging.c index c59638736..d5d17ae9e 100644 --- a/main/bridging.c +++ b/main/bridging.c @@ -588,6 +588,9 @@ static int bridge_channel_push(struct ast_bridge_channel *bridge_channel) bridge_channel_pull(swap); } + /* Clear any BLINDTRANSFER since the transfer has completed. */ + pbx_builtin_setvar_helper(bridge_channel->chan, "BLINDTRANSFER", NULL); + bridge->reconfigured = 1; ast_bridge_publish_enter(bridge, bridge_channel->chan); return 0; @@ -922,6 +925,68 @@ void ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel bridge_channel, custom_play, playfile, moh_class); } +struct bridge_custom_callback { + /*! Call this function on the bridge channel thread. */ + ast_bridge_custom_callback_fn callback; + /*! Size of the payload if it exists. A number otherwise. */ + size_t payload_size; + /*! Nonzero if the payload exists. */ + char payload_exists; + /*! Payload to give to callback. */ + char payload[0]; +}; + +/*! + * \internal + * \brief Handle the do custom callback bridge action. + * \since 12.0.0 + * + * \param bridge_channel Which channel to run the application on. + * \param data Action frame data to run the application. + * + * \return Nothing + */ +static void bridge_channel_do_callback(struct ast_bridge_channel *bridge_channel, struct bridge_custom_callback *data) +{ + data->callback(bridge_channel, data->payload_exists ? data->payload : NULL, data->payload_size); +} + +static void payload_helper_cb(ast_bridge_channel_post_action_data post_it, + struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) +{ + struct bridge_custom_callback *cb_data; + size_t len_data = sizeof(*cb_data) + (payload ? payload_size : 0); + + /* Sanity check. */ + if (!callback) { + ast_assert(0); + return; + } + + /* Fill in custom callback frame data. */ + cb_data = alloca(len_data); + cb_data->callback = callback; + cb_data->payload_size = payload_size; + cb_data->payload_exists = payload && payload_size; + if (cb_data->payload_exists) { + memcpy(cb_data->payload, payload, payload_size);/* Safe */ + } + + post_it(bridge_channel, AST_BRIDGE_ACTION_CALLBACK, cb_data, len_data); +} + +void ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) +{ + payload_helper_cb(ast_bridge_channel_write_action_data, + bridge_channel, callback, payload, payload_size); +} + +void ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) +{ + payload_helper_cb(ast_bridge_channel_queue_action_data, + bridge_channel, callback, payload, payload_size); +} + struct bridge_park { int parker_uuid_offset; int app_data_offset; @@ -1715,6 +1780,253 @@ static int smart_bridge_operation(struct ast_bridge *bridge) /*! * \internal + * \brief Bridge channel to check if a BRIDGE_PLAY_SOUND needs to be played. + * \since 12.0.0 + * + * \param bridge_channel What to check. + * + * \return Nothing + */ +static void check_bridge_play_sound(struct ast_bridge_channel *bridge_channel) +{ + const char *play_file; + + ast_channel_lock(bridge_channel->chan); + play_file = pbx_builtin_getvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND"); + if (!ast_strlen_zero(play_file)) { + play_file = ast_strdupa(play_file); + pbx_builtin_setvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND", NULL); + } else { + play_file = NULL; + } + ast_channel_unlock(bridge_channel->chan); + + if (play_file) { + ast_bridge_channel_queue_playfile(bridge_channel, NULL, play_file, NULL); + } +} + +/*! + * \internal + * \brief Check for any BRIDGE_PLAY_SOUND channel variables in the bridge. + * \since 12.0.0 + * + * \param bridge What to operate on. + * + * \note On entry, the bridge is already locked. + * + * \return Nothing + */ +static void check_bridge_play_sounds(struct ast_bridge *bridge) +{ + struct ast_bridge_channel *bridge_channel; + + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + check_bridge_play_sound(bridge_channel); + } +} + +static void update_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid) +{ + pbx_builtin_setvar_helper(chan, "BRIDGEPEER", name); + pbx_builtin_setvar_helper(chan, "BRIDGEPVTCALLID", pvtid); +} + +/*! + * \internal + * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a 2 party bridge. + * \since 12.0.0 + * + * \param c0 Party of the first part. + * \param c1 Party of the second part. + * + * \note On entry, the bridge is already locked. + * \note The bridge is expected to have exactly two parties. + * + * \return Nothing + */ +static void set_bridge_peer_vars_2party(struct ast_channel *c0, struct ast_channel *c1) +{ + const char *c0_name; + const char *c1_name; + const char *c0_pvtid = NULL; + const char *c1_pvtid = NULL; +#define UPDATE_BRIDGE_VARS_GET(chan, name, pvtid) \ + do { \ + name = ast_strdupa(ast_channel_name(chan)); \ + if (ast_channel_tech(chan)->get_pvt_uniqueid) { \ + pvtid = ast_strdupa(ast_channel_tech(chan)->get_pvt_uniqueid(chan)); \ + } \ + } while (0) + + ast_channel_lock(c1); + UPDATE_BRIDGE_VARS_GET(c1, c1_name, c1_pvtid); + ast_channel_unlock(c1); + + ast_channel_lock(c0); + update_bridge_vars_set(c0, c1_name, c1_pvtid); + UPDATE_BRIDGE_VARS_GET(c0, c0_name, c0_pvtid); + ast_channel_unlock(c0); + + ast_channel_lock(c1); + update_bridge_vars_set(c1, c0_name, c0_pvtid); + ast_channel_unlock(c1); +} + +/*! + * \internal + * \brief Fill the BRIDGEPEER value buffer with a comma separated list of channel names. + * \since 12.0.0 + * + * \param buf Buffer to fill. The caller must guarantee the buffer is large enough. + * \param cur_idx Which index into names[] to skip. + * \param names Channel names to put in the buffer. + * \param num_names Number of names in the array. + * + * \return Nothing + */ +static void fill_bridgepeer_buf(char *buf, unsigned int cur_idx, const char *names[], unsigned int num_names) +{ + int need_separator = 0; + unsigned int idx; + const char *src; + char *pos; + + pos = buf; + for (idx = 0; idx < num_names; ++idx) { + if (idx == cur_idx) { + continue; + } + + if (need_separator) { + *pos++ = ','; + } + need_separator = 1; + + /* Copy name into buffer. */ + src = names[idx]; + while (*src) { + *pos++ = *src++; + } + } + *pos = '\0'; +} + +/*! + * \internal + * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a multi-party bridge. + * \since 12.0.0 + * + * \param bridge What to operate on. + * + * \note On entry, the bridge is already locked. + * \note The bridge is expected to have more than two parties. + * + * \return Nothing + */ +static void set_bridge_peer_vars_multiparty(struct ast_bridge *bridge) +{ +/* + * Set a maximum number of channel names for the BRIDGEPEER + * list. The plus one is for the current channel which is not + * put in the list. + */ +#define MAX_BRIDGEPEER_CHANS (10 + 1) + + unsigned int idx; + unsigned int num_names; + unsigned int len; + const char **names; + char *buf; + struct ast_bridge_channel *bridge_channel; + + /* Get first MAX_BRIDGEPEER_CHANS channel names. */ + num_names = MIN(bridge->num_channels, MAX_BRIDGEPEER_CHANS); + names = ast_alloca(num_names * sizeof(*names)); + idx = 0; + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + if (num_names <= idx) { + break; + } + ast_channel_lock(bridge_channel->chan); + names[idx++] = ast_strdupa(ast_channel_name(bridge_channel->chan)); + ast_channel_unlock(bridge_channel->chan); + } + + /* Determine maximum buf size needed. */ + len = num_names; + for (idx = 0; idx < num_names; ++idx) { + len += strlen(names[idx]); + } + buf = ast_alloca(len); + + /* Set the bridge channel variables. */ + idx = 0; + buf[0] = '\0'; + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + if (idx < num_names) { + fill_bridgepeer_buf(buf, idx, names, num_names); + } + ++idx; + + ast_channel_lock(bridge_channel->chan); + update_bridge_vars_set(bridge_channel->chan, buf, NULL); + ast_channel_unlock(bridge_channel->chan); + } +} + +/*! + * \internal + * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a holding bridge. + * \since 12.0.0 + * + * \param bridge What to operate on. + * + * \note On entry, the bridge is already locked. + * + * \return Nothing + */ +static void set_bridge_peer_vars_holding(struct ast_bridge *bridge) +{ + struct ast_bridge_channel *bridge_channel; + + AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) { + ast_channel_lock(bridge_channel->chan); + update_bridge_vars_set(bridge_channel->chan, NULL, NULL); + ast_channel_unlock(bridge_channel->chan); + } +} + +/*! + * \internal + * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in the bridge. + * \since 12.0.0 + * + * \param bridge What to operate on. + * + * \note On entry, the bridge is already locked. + * + * \return Nothing + */ +static void set_bridge_peer_vars(struct ast_bridge *bridge) +{ + if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_HOLDING) { + set_bridge_peer_vars_holding(bridge); + return; + } + if (bridge->num_channels < 2) { + return; + } + if (bridge->num_channels == 2) { + set_bridge_peer_vars_2party(AST_LIST_FIRST(&bridge->channels)->chan, + AST_LIST_LAST(&bridge->channels)->chan); + } else { + set_bridge_peer_vars_multiparty(bridge); + } +} + +/*! + * \internal * \brief Notify the bridge that it has been reconfigured. * \since 12.0.0 * @@ -1743,6 +2055,12 @@ static void bridge_reconfigured(struct ast_bridge *bridge) return; } bridge_complete_join(bridge); + + if (bridge->dissolved) { + return; + } + check_bridge_play_sounds(bridge); + set_bridge_peer_vars(bridge); } /*! @@ -2123,17 +2441,24 @@ static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_chann ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); bridge_channel_unsuspend(bridge_channel); break; - case AST_BRIDGE_ACTION_PARK: + case AST_BRIDGE_ACTION_RUN_APP: bridge_channel_suspend(bridge_channel); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_park(bridge_channel, action->data.ptr); + bridge_channel_run_app(bridge_channel, action->data.ptr); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); bridge_channel_unsuspend(bridge_channel); break; - case AST_BRIDGE_ACTION_RUN_APP: + case AST_BRIDGE_ACTION_CALLBACK: bridge_channel_suspend(bridge_channel); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); - bridge_channel_run_app(bridge_channel, action->data.ptr); + bridge_channel_do_callback(bridge_channel, action->data.ptr); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_unsuspend(bridge_channel); + break; + case AST_BRIDGE_ACTION_PARK: + bridge_channel_suspend(bridge_channel); + ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); + bridge_channel_park(bridge_channel, action->data.ptr); ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE); bridge_channel_unsuspend(bridge_channel); break; @@ -5684,8 +6009,36 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra } if (to_target_bridge_channel) { + const char *target_complete_sound; + /* Take off hold if they are on hold. */ ast_bridge_channel_write_unhold(to_target_bridge_channel); + + /* Is there a courtesy sound to play to the target? */ + ast_channel_lock(to_transfer_target); + target_complete_sound = pbx_builtin_getvar_helper(to_transfer_target, + "ATTENDED_TRANSFER_COMPLETE_SOUND"); + if (!ast_strlen_zero(target_complete_sound)) { + target_complete_sound = ast_strdupa(target_complete_sound); + } else { + target_complete_sound = NULL; + } + ast_channel_unlock(to_transfer_target); + if (!target_complete_sound) { + ast_channel_lock(to_transferee); + target_complete_sound = pbx_builtin_getvar_helper(to_transferee, + "ATTENDED_TRANSFER_COMPLETE_SOUND"); + if (!ast_strlen_zero(target_complete_sound)) { + target_complete_sound = ast_strdupa(target_complete_sound); + } else { + target_complete_sound = NULL; + } + ast_channel_unlock(to_transferee); + } + if (target_complete_sound) { + ast_bridge_channel_write_playfile(to_target_bridge_channel, NULL, + target_complete_sound, NULL); + } } /* Let's get the easy one out of the way first */ diff --git a/main/features.c b/main/features.c index e0c6548a9..c71e73710 100644 --- a/main/features.c +++ b/main/features.c @@ -3289,9 +3289,46 @@ static int setup_bridge_features_builtin(struct ast_bridge_features *features, s #endif } -struct dtmf_hook_run_app { +struct dynamic_dtmf_hook_run { + /*! Offset into app_name[] where the channel name that activated the hook starts. */ + int activated_offset; + /*! Offset into app_name[] where the dynamic feature name starts. */ + int feature_offset; + /*! Offset into app_name[] where the MOH class name starts. (zero if no MOH) */ + int moh_offset; + /*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */ + int app_args_offset; + /*! Application name to run. */ + char app_name[0]; +}; + +static void dynamic_dtmf_hook_callback(struct ast_bridge_channel *bridge_channel, + const void *payload, size_t payload_size) +{ + struct ast_channel *chan = bridge_channel->chan; + const struct dynamic_dtmf_hook_run *run_data = payload; + + pbx_builtin_setvar_helper(chan, "DYNAMIC_FEATURENAME", + &run_data->app_name[run_data->feature_offset]); + pbx_builtin_setvar_helper(chan, "DYNAMIC_WHO_ACTIVATED", + &run_data->app_name[run_data->activated_offset]); + + ast_bridge_channel_run_app(bridge_channel, run_data->app_name, + run_data->app_args_offset ? &run_data->app_name[run_data->app_args_offset] : NULL, + run_data->moh_offset ? &run_data->app_name[run_data->moh_offset] : NULL); +} + +static void dynamic_dtmf_hook_run_callback(struct ast_bridge_channel *bridge_channel, + ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size) +{ + callback(bridge_channel, payload, payload_size); +} + +struct dynamic_dtmf_hook_data { /*! Which side of bridge to run app (AST_FEATURE_FLAG_ONSELF/AST_FEATURE_FLAG_ONPEER) */ unsigned int flags; + /*! Offset into app_name[] where the dynamic feature name starts. */ + int feature_offset; /*! Offset into app_name[] where the MOH class name starts. (zero if no MOH) */ int moh_offset; /*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */ @@ -3302,7 +3339,7 @@ struct dtmf_hook_run_app { /*! * \internal - * \brief Setup bridge dynamic features. + * \brief Activated dynamic DTMF feature hook. * \since 12.0.0 * * \param bridge The bridge that the channel is part of @@ -3312,27 +3349,55 @@ struct dtmf_hook_run_app { * \retval 0 Keep the callback hook. * \retval -1 Remove the callback hook. */ -static int app_dtmf_feature_hook(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt) +static int dynamic_dtmf_hook_trip(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt) { - struct dtmf_hook_run_app *pvt = hook_pvt; - void (*run_it)(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class); + struct dynamic_dtmf_hook_data *pvt = hook_pvt; + void (*run_it)(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size); + struct dynamic_dtmf_hook_run *run_data; + const char *activated_name; + size_t len_name; + size_t len_args; + size_t len_moh; + size_t len_feature; + size_t len_activated; + size_t len_data; + + /* Determine lengths of things. */ + len_name = strlen(pvt->app_name) + 1; + len_args = pvt->app_args_offset ? strlen(&pvt->app_name[pvt->app_args_offset]) + 1 : 0; + len_moh = pvt->moh_offset ? strlen(&pvt->app_name[pvt->moh_offset]) + 1 : 0; + len_feature = strlen(&pvt->app_name[pvt->feature_offset]) + 1; + ast_channel_lock(bridge_channel->chan); + activated_name = ast_strdupa(ast_channel_name(bridge_channel->chan)); + ast_channel_unlock(bridge_channel->chan); + len_activated = strlen(activated_name) + 1; + len_data = sizeof(*run_data) + len_name + len_args + len_moh + len_feature + len_activated; + + /* Fill in dynamic feature run hook data. */ + run_data = ast_alloca(len_data); + run_data->app_args_offset = len_args ? len_name : 0; + run_data->moh_offset = len_moh ? len_name + len_args : 0; + run_data->feature_offset = len_name + len_args + len_moh; + run_data->activated_offset = len_name + len_args + len_moh + len_feature; + strcpy(run_data->app_name, pvt->app_name);/* Safe */ + if (len_args) { + strcpy(&run_data->app_name[run_data->app_args_offset], + &pvt->app_name[pvt->app_args_offset]);/* Safe */ + } + if (len_moh) { + strcpy(&run_data->app_name[run_data->moh_offset], + &pvt->app_name[pvt->moh_offset]);/* Safe */ + } + strcpy(&run_data->app_name[run_data->feature_offset], + &pvt->app_name[pvt->feature_offset]);/* Safe */ + strcpy(&run_data->app_name[run_data->activated_offset], activated_name);/* Safe */ if (ast_test_flag(pvt, AST_FEATURE_FLAG_ONPEER)) { - run_it = ast_bridge_channel_write_app; + run_it = ast_bridge_channel_write_callback; } else { - run_it = ast_bridge_channel_run_app; + run_it = dynamic_dtmf_hook_run_callback; } - -/* - * BUGBUG need to pass to run_it the triggering channel name so DYNAMIC_WHO_TRIGGERED can be set on the channel when it is run. - * - * This would replace DYNAMIC_PEERNAME which is redundant with - * BRIDGEPEER anyway. The value of DYNAMIC_WHO_TRIGGERED is - * really useful in the case of a multi-party bridge. - */ - run_it(bridge_channel, pvt->app_name, - pvt->app_args_offset ? &pvt->app_name[pvt->app_args_offset] : NULL, - pvt->moh_offset ? &pvt->app_name[pvt->moh_offset] : NULL); + run_it(bridge_channel, dynamic_dtmf_hook_callback, run_data, len_data); return 0; } @@ -3344,6 +3409,7 @@ static int app_dtmf_feature_hook(struct ast_bridge *bridge, struct ast_bridge_ch * \param features Bridge features to setup. * \param flags Which side of bridge to run app (AST_FEATURE_FLAG_ONSELF/AST_FEATURE_FLAG_ONPEER). * \param dtmf DTMF trigger sequence. + * \param feature_name Name of the dynamic feature. * \param app_name Dialplan application name to run. * \param app_args Dialplan application arguments. (Empty or NULL if no arguments) * \param moh_class MOH class to play to peer. (Empty or NULL if no MOH played) @@ -3351,32 +3417,35 @@ static int app_dtmf_feature_hook(struct ast_bridge *bridge, struct ast_bridge_ch * \retval 0 on success. * \retval -1 on error. */ -static int add_dynamic_dtmf_hook(struct ast_bridge_features *features, unsigned int flags, const char *dtmf, const char *app_name, const char *app_args, const char *moh_class) +static int dynamic_dtmf_hook_add(struct ast_bridge_features *features, unsigned int flags, const char *dtmf, const char *feature_name, const char *app_name, const char *app_args, const char *moh_class) { - struct dtmf_hook_run_app *app_data; + struct dynamic_dtmf_hook_data *hook_data; size_t len_name = strlen(app_name) + 1; size_t len_args = ast_strlen_zero(app_args) ? 0 : strlen(app_args) + 1; size_t len_moh = ast_strlen_zero(moh_class) ? 0 : strlen(moh_class) + 1; - size_t len_data = sizeof(*app_data) + len_name + len_args + len_moh; + size_t len_feature = strlen(feature_name) + 1; + size_t len_data = sizeof(*hook_data) + len_name + len_args + len_moh + len_feature; /* Fill in application run hook data. */ - app_data = ast_malloc(len_data); - if (!app_data) { + hook_data = ast_malloc(len_data); + if (!hook_data) { return -1; } - app_data->flags = flags; - app_data->app_args_offset = len_args ? len_name : 0; - app_data->moh_offset = len_moh ? len_name + len_args : 0; - strcpy(app_data->app_name, app_name);/* Safe */ + hook_data->flags = flags; + hook_data->app_args_offset = len_args ? len_name : 0; + hook_data->moh_offset = len_moh ? len_name + len_args : 0; + hook_data->feature_offset = len_name + len_args + len_moh; + strcpy(hook_data->app_name, app_name);/* Safe */ if (len_args) { - strcpy(&app_data->app_name[app_data->app_args_offset], app_args);/* Safe */ + strcpy(&hook_data->app_name[hook_data->app_args_offset], app_args);/* Safe */ } if (len_moh) { - strcpy(&app_data->app_name[app_data->moh_offset], moh_class);/* Safe */ + strcpy(&hook_data->app_name[hook_data->moh_offset], moh_class);/* Safe */ } + strcpy(&hook_data->app_name[hook_data->feature_offset], feature_name);/* Safe */ - return ast_bridge_dtmf_hook(features, dtmf, app_dtmf_feature_hook, - app_data, ast_free_ptr, AST_BRIDGE_HOOK_REMOVE_ON_PULL); + return ast_bridge_dtmf_hook(features, dtmf, dynamic_dtmf_hook_trip, hook_data, + ast_free_ptr, AST_BRIDGE_HOOK_REMOVE_ON_PULL); } static int setup_dynamic_feature(void *obj, void *arg, void *data, int flags) @@ -3385,10 +3454,9 @@ static int setup_dynamic_feature(void *obj, void *arg, void *data, int flags) struct ast_bridge_features *features = arg; int *res = data; - /* BUGBUG need to pass to add_dynamic_dtmf_hook the applicationmap name (item->name) so the DYNAMIC_FEATURENAME can be set on the channel when it is run. */ - *res |= add_dynamic_dtmf_hook(features, item->activate_on_self ? AST_FEATURE_FLAG_ONSELF : - AST_FEATURE_FLAG_ONPEER, item->dtmf, item->app, item->app_data, - item->moh_class); + *res |= dynamic_dtmf_hook_add(features, + item->activate_on_self ? AST_FEATURE_FLAG_ONSELF : AST_FEATURE_FLAG_ONPEER, + item->dtmf, item->name, item->app, item->app_data, item->moh_class); return 0; } @@ -3421,7 +3489,6 @@ static int setup_bridge_features_dynamic(struct ast_bridge_features *features, s return res; } -/* BUGBUG struct ast_call_feature needs to be made an ao2 object so the basic bridge class can own the code setting up it's DTMF hooks. */ /* BUGBUG this really should be made a private function of bridging_basic.c after struct ast_call_feature is made an ao2 object. */ int ast_bridge_channel_setup_features(struct ast_bridge_channel *bridge_channel) { @@ -3540,15 +3607,6 @@ static int pre_bridge_setup(struct ast_channel *chan, struct ast_channel *peer, { int res; -/* BUGBUG these channel vars may need to be made dynamic so they update when transfers happen. */ - pbx_builtin_setvar_helper(chan, "BRIDGEPEER", ast_channel_name(peer)); - pbx_builtin_setvar_helper(peer, "BRIDGEPEER", ast_channel_name(chan)); - -/* BUGBUG revisit how BLINDTRANSFER operates with the new bridging model. */ - /* Clear any BLINDTRANSFER since the transfer has completed. */ - pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", NULL); - pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", NULL); - set_bridge_features_on_config(config, pbx_builtin_getvar_helper(chan, "BRIDGE_FEATURES")); add_features_datastores(chan, peer, config); |