summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES27
-rw-r--r--UPGRADE.txt14
-rw-r--r--bridges/bridge_builtin_features.c18
-rw-r--r--configs/chan_dahdi.conf.sample5
-rw-r--r--configs/iax.conf.sample3
-rw-r--r--configs/sip.conf.sample3
-rw-r--r--configs/skinny.conf.sample3
-rw-r--r--include/asterisk/bridging.h85
-rw-r--r--include/asterisk/bridging_features.h12
-rw-r--r--main/bridging.c361
-rw-r--r--main/features.c148
11 files changed, 596 insertions, 83 deletions
diff --git a/CHANGES b/CHANGES
index 15cf89ca7..4d0f801c6 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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);