Reimplement bridging and DTMF features related channel variables in the bridging...
authorRichard Mudgett <rmudgett@digium.com>
Thu, 6 Jun 2013 22:46:54 +0000 (22:46 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Thu, 6 Jun 2013 22:46:54 +0000 (22:46 +0000)
* 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.

(closes issue ASTERISK-21555)
Reported by: Matt Jordan

Review: https://reviewboard.asterisk.org/r/2582/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@390771 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
UPGRADE.txt
bridges/bridge_builtin_features.c
configs/chan_dahdi.conf.sample
configs/iax.conf.sample
configs/sip.conf.sample
configs/skinny.conf.sample
include/asterisk/bridging.h
include/asterisk/bridging_features.h
main/bridging.c
main/features.c

diff --git a/CHANGES b/CHANGES
index 15cf89c..4d0f801 100644 (file)
--- 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
 ------------------
index e2e7090..e8a3996 100644 (file)
@@ -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
index e11b280..3a081c8 100644 (file)
@@ -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:
index 26bd0cb..18ac202 100644 (file)
@@ -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
index 9b5d4bc..e26c6fd 100644 (file)
@@ -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
index a0ceabe..57692b2 100644 (file)
@@ -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
index 0a618ac..60dc873 100644 (file)
@@ -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
index b589874..9d3f5b3 100644 (file)
@@ -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
  *
index 1679f04..1ee13a4 100644 (file)
@@ -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];
 };
 
index c596387..d5d17ae 100644 (file)
@@ -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 */
index e0c6548..c71e737 100644 (file)
@@ -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);