[svn-commits] rmudgett: trunk r390771 - in /trunk: ./ bridges/ configs/ include/asterisk/ m...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Jun 6 17:46:58 CDT 2013


Author: rmudgett
Date: Thu Jun  6 17:46:54 2013
New Revision: 390771

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=390771
Log:
Reimplement bridging and DTMF features related channel variables in the bridging core.

* 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/

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

Modified: trunk/CHANGES
URL: http://svnview.digium.com/svn/asterisk/trunk/CHANGES?view=diff&rev=390771&r1=390770&r2=390771
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Thu Jun  6 17:46:54 2013
@@ -240,6 +240,31 @@
    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 @@
    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
 ------------------

Modified: trunk/UPGRADE.txt
URL: http://svnview.digium.com/svn/asterisk/trunk/UPGRADE.txt?view=diff&rev=390771&r1=390770&r2=390771
==============================================================================
--- trunk/UPGRADE.txt (original)
+++ trunk/UPGRADE.txt Thu Jun  6 17:46:54 2013
@@ -37,6 +37,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
@@ -86,9 +91,12 @@
 
 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

Modified: trunk/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/bridges/bridge_builtin_features.c?view=diff&rev=390771&r1=390770&r2=390771
==============================================================================
--- trunk/bridges/bridge_builtin_features.c (original)
+++ trunk/bridges/bridge_builtin_features.c Thu Jun  6 17:46:54 2013
@@ -134,6 +134,9 @@
 		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 @@
 	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 @@
 	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:

Modified: trunk/configs/chan_dahdi.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/chan_dahdi.conf.sample?view=diff&rev=390771&r1=390770&r2=390771
==============================================================================
--- trunk/configs/chan_dahdi.conf.sample (original)
+++ trunk/configs/chan_dahdi.conf.sample Thu Jun  6 17:46:54 2013
@@ -863,12 +863,13 @@
 ;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

Modified: trunk/configs/iax.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/iax.conf.sample?view=diff&rev=390771&r1=390770&r2=390771
==============================================================================
--- trunk/configs/iax.conf.sample (original)
+++ trunk/configs/iax.conf.sample Thu Jun  6 17:46:54 2013
@@ -550,7 +550,8 @@
 ;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

Modified: trunk/configs/sip.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/sip.conf.sample?view=diff&rev=390771&r1=390770&r2=390771
==============================================================================
--- trunk/configs/sip.conf.sample (original)
+++ trunk/configs/sip.conf.sample Thu Jun  6 17:46:54 2013
@@ -1518,7 +1518,8 @@
 ;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

Modified: trunk/configs/skinny.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/skinny.conf.sample?view=diff&rev=390771&r1=390770&r2=390771
==============================================================================
--- trunk/configs/skinny.conf.sample (original)
+++ trunk/configs/skinny.conf.sample Thu Jun  6 17:46:54 2013
@@ -131,7 +131,8 @@
 ;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

Modified: trunk/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/bridging.h?view=diff&rev=390771&r1=390770&r2=390771
==============================================================================
--- trunk/include/asterisk/bridging.h (original)
+++ trunk/include/asterisk/bridging.h Thu Jun  6 17:46:54 2013
@@ -219,10 +219,12 @@
 	AST_BRIDGE_ACTION_TALKING_STOP,
 	/*! Bridge channel is to play the indicated sound file. */
 	AST_BRIDGE_ACTION_PLAY_FILE,
+	/*! 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 run the indicated application. */
-	AST_BRIDGE_ACTION_RUN_APP,
 	/*! 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 @@
 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
  *
@@ -1257,6 +1243,69 @@
  * \return Nothing
  */
 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

Modified: trunk/include/asterisk/bridging_features.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/bridging_features.h?view=diff&rev=390771&r1=390770&r2=390771
==============================================================================
--- trunk/include/asterisk/bridging_features.h (original)
+++ trunk/include/asterisk/bridging_features.h Thu Jun  6 17:46:54 2013
@@ -249,8 +249,7 @@
  * \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 @@
  * \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];
 };
 

Modified: trunk/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/bridging.c?view=diff&rev=390771&r1=390770&r2=390771
==============================================================================
--- trunk/main/bridging.c (original)
+++ trunk/main/bridging.c Thu Jun  6 17:46:54 2013
@@ -588,6 +588,9 @@
 		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;
@@ -920,6 +923,68 @@
 {
 	payload_helper_playfile(ast_bridge_channel_queue_action_data,
 		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 {
@@ -1715,6 +1780,253 @@
 
 /*!
  * \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 @@
 		return;
 	}
 	bridge_complete_join(bridge);
+
+	if (bridge->dissolved) {
+		return;
+	}
+	check_bridge_play_sounds(bridge);
+	set_bridge_peer_vars(bridge);
 }
 
 /*!
@@ -2123,17 +2441,24 @@
 		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
 		bridge_channel_unsuspend(bridge_channel);
 		break;
+	case AST_BRIDGE_ACTION_RUN_APP:
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		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_CALLBACK:
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		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;
-	case AST_BRIDGE_ACTION_RUN_APP:
-		bridge_channel_suspend(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_run_app(bridge_channel, action->data.ptr);
 		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
 		bridge_channel_unsuspend(bridge_channel);
 		break;
@@ -5684,8 +6009,36 @@
 	}
 
 	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 */

Modified: trunk/main/features.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/features.c?view=diff&rev=390771&r1=390770&r2=390771
==============================================================================
--- trunk/main/features.c (original)
+++ trunk/main/features.c Thu Jun  6 17:46:54 2013
@@ -3289,9 +3289,11 @@
 #endif
 }
 
-struct dtmf_hook_run_app {
-	/*! Which side of bridge to run app (AST_FEATURE_FLAG_ONSELF/AST_FEATURE_FLAG_ONPEER) */
-	unsigned int flags;
+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) */
@@ -3300,9 +3302,44 @@
 	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) */
+	int app_args_offset;
+	/*! Application name to run. */
+	char app_name[0];
+};
+
 /*!
  * \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 @@
  * \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)
-{
-	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);
+static int dynamic_dtmf_hook_trip(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	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;
-	}
-
-/*
- * 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 = dynamic_dtmf_hook_run_callback;
+	}
+	run_it(bridge_channel, dynamic_dtmf_hook_callback, run_data, len_data);
 	return 0;
 }
 
@@ -3344,6 +3409,7 @@
  * \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 @@
  * \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)
-{
-	struct dtmf_hook_run_app *app_data;
+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 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 */
-	}
-
-	return ast_bridge_dtmf_hook(features, dtmf, app_dtmf_feature_hook,
-		app_data, ast_free_ptr, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
+		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, 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 @@
 	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 @@
 	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. */

[... 20 lines stripped ...]



More information about the svn-commits mailing list