[asterisk-commits] dlee: branch dlee/record r390821 - in /team/dlee/record: ./ bridges/ channels...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Jun 6 21:51:34 CDT 2013


Author: dlee
Date: Thu Jun  6 21:51:30 2013
New Revision: 390821

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=390821
Log:
Merged revisions 390771-390804 from http://svn.asterisk.org/svn/asterisk/trunk

Modified:
    team/dlee/record/   (props changed)
    team/dlee/record/CHANGES
    team/dlee/record/UPGRADE.txt
    team/dlee/record/bridges/bridge_builtin_features.c
    team/dlee/record/channels/chan_dahdi.c
    team/dlee/record/channels/chan_misdn.c
    team/dlee/record/channels/sig_analog.c
    team/dlee/record/channels/sig_pri.c
    team/dlee/record/channels/sig_pri.h
    team/dlee/record/configs/chan_dahdi.conf.sample
    team/dlee/record/configs/iax.conf.sample
    team/dlee/record/configs/sip.conf.sample
    team/dlee/record/configs/skinny.conf.sample
    team/dlee/record/include/asterisk/bridging.h
    team/dlee/record/include/asterisk/bridging_features.h
    team/dlee/record/main/bridging.c
    team/dlee/record/main/channel.c
    team/dlee/record/main/features.c
    team/dlee/record/main/features_config.c

Propchange: team/dlee/record/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Thu Jun  6 21:51:30 2013
@@ -1,1 +1,1 @@
-/trunk:1-390754
+/trunk:1-390820

Modified: team/dlee/record/CHANGES
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/CHANGES?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/CHANGES (original)
+++ team/dlee/record/CHANGES Thu Jun  6 21:51:30 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: team/dlee/record/UPGRADE.txt
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/UPGRADE.txt?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/UPGRADE.txt (original)
+++ team/dlee/record/UPGRADE.txt Thu Jun  6 21:51:30 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: team/dlee/record/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/bridges/bridge_builtin_features.c?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/bridges/bridge_builtin_features.c (original)
+++ team/dlee/record/bridges/bridge_builtin_features.c Thu Jun  6 21:51:30 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: team/dlee/record/channels/chan_dahdi.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/channels/chan_dahdi.c?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/channels/chan_dahdi.c (original)
+++ team/dlee/record/channels/chan_dahdi.c Thu Jun  6 21:51:30 2013
@@ -131,6 +131,7 @@
 #include "asterisk/ccss.h"
 #include "asterisk/data.h"
 #include "asterisk/features_config.h"
+#include "asterisk/bridging.h"
 
 /*** DOCUMENTATION
 	<application name="DAHDISendKeypadFacility" language="en_US">
@@ -7841,55 +7842,49 @@
 
 static void *analog_ss_thread(void *data);
 
+/*!
+ * \internal
+ * \brief Attempt to transfer 3-way call.
+ *
+ * \param p DAHDI private structure.
+ *
+ * \note On entry these locks are held: real-call, private, 3-way call.
+ * \note On exit these locks are held: real-call, private.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
 static int attempt_transfer(struct dahdi_pvt *p)
 {
-	/* In order to transfer, we need at least one of the channels to
-	   actually be in a call bridge.  We can't conference two applications
-	   together (but then, why would we want to?) */
-	if (ast_bridged_channel(p->subs[SUB_REAL].owner)) {
-		/* The three-way person we're about to transfer to could still be in MOH, so
-		   stop it now */
-		ast_queue_unhold(p->subs[SUB_THREEWAY].owner);
-		if (ast_channel_state(p->subs[SUB_REAL].owner) == AST_STATE_RINGING) {
-			ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_RINGING);
-		}
-		if (ast_channel_state(p->subs[SUB_THREEWAY].owner) == AST_STATE_RING) {
-			tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE);
-		}
-		 if (ast_channel_masquerade(p->subs[SUB_THREEWAY].owner, ast_bridged_channel(p->subs[SUB_REAL].owner))) {
-			ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-					ast_channel_name(ast_bridged_channel(p->subs[SUB_REAL].owner)), ast_channel_name(p->subs[SUB_THREEWAY].owner));
-			return -1;
-		}
-		/* Orphan the channel after releasing the lock */
-		ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-		unalloc_sub(p, SUB_THREEWAY);
-	} else if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
-		ast_queue_unhold(p->subs[SUB_REAL].owner);
-		if (ast_channel_state(p->subs[SUB_THREEWAY].owner) == AST_STATE_RINGING) {
-			ast_queue_control(p->subs[SUB_THREEWAY].owner, AST_CONTROL_RINGING);
-		}
-		if (ast_channel_state(p->subs[SUB_REAL].owner) == AST_STATE_RING) {
-			tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE);
-		}
-		if (ast_channel_masquerade(p->subs[SUB_REAL].owner, ast_bridged_channel(p->subs[SUB_THREEWAY].owner))) {
-			ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-					ast_channel_name(ast_bridged_channel(p->subs[SUB_THREEWAY].owner)), ast_channel_name(p->subs[SUB_REAL].owner));
-			return -1;
-		}
-		/* Three-way is now the REAL */
-		swap_subs(p, SUB_THREEWAY, SUB_REAL);
-		ast_channel_unlock(p->subs[SUB_REAL].owner);
-		unalloc_sub(p, SUB_THREEWAY);
-		/* Tell the caller not to hangup */
-		return 1;
-	} else {
-		ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
-			ast_channel_name(p->subs[SUB_REAL].owner), ast_channel_name(p->subs[SUB_THREEWAY].owner));
-		ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
-		return -1;
-	}
-	return 0;
+	struct ast_channel *owner_real;
+	struct ast_channel *owner_3way;
+	enum ast_transfer_result xfer_res;
+	int res = 0;
+
+	owner_real = ast_channel_ref(p->subs[SUB_REAL].owner);
+	owner_3way = ast_channel_ref(p->subs[SUB_THREEWAY].owner);
+
+	ast_verb(3, "TRANSFERRING %s to %s\n",
+		ast_channel_name(owner_3way), ast_channel_name(owner_real));
+
+	ast_channel_unlock(owner_real);
+	ast_channel_unlock(owner_3way);
+	ast_mutex_unlock(&p->lock);
+
+	xfer_res = ast_bridge_transfer_attended(owner_3way, owner_real);
+	if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) {
+		ast_softhangup(owner_3way, AST_SOFTHANGUP_DEV);
+		res = -1;
+	}
+
+	/* Must leave with these locked. */
+	ast_channel_lock(owner_real);
+	ast_mutex_lock(&p->lock);
+
+	ast_channel_unref(owner_real);
+	ast_channel_unref(owner_3way);
+
+	return res;
 }
 
 static int check_for_conference(struct dahdi_pvt *p)
@@ -8399,17 +8394,13 @@
 								p->owner = NULL;
 								/* Ring the phone */
 								dahdi_ring_phone(p);
-							} else {
-								if ((res = attempt_transfer(p)) < 0) {
-									ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
-									if (p->subs[SUB_THREEWAY].owner)
-										ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-								} else if (res) {
-									/* Don't actually hang up at this point */
-									if (p->subs[SUB_THREEWAY].owner)
-										ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-									break;
-								}
+							} else if (!attempt_transfer(p)) {
+								/*
+								 * Transfer successful.  Don't actually hang up at this point.
+								 * Let our channel legs of the calls die off as the transfer
+								 * percolates through the core.
+								 */
+								break;
 							}
 						} else {
 							ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);

Modified: team/dlee/record/channels/chan_misdn.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/channels/chan_misdn.c?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/channels/chan_misdn.c (original)
+++ team/dlee/record/channels/chan_misdn.c Thu Jun  6 21:51:30 2013
@@ -103,6 +103,7 @@
 #include "asterisk/format.h"
 #include "asterisk/format_cap.h"
 #include "asterisk/features_config.h"
+#include "asterisk/bridging.h"
 
 #include "chan_misdn_config.h"
 #include "isdn_lib.h"
@@ -8592,10 +8593,9 @@
 static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list *held_ch)
 {
 	int retval;
-	struct ast_channel *target;
-	struct ast_channel *transferee;
-	struct ast_party_connected_line target_colp;
-	struct ast_party_connected_line transferee_colp;
+	enum ast_transfer_result xfer_res;
+	struct ast_channel *to_target;
+	struct ast_channel *to_transferee;
 
 	switch (active_ch->state) {
 	case MISDN_PROCEEDING:
@@ -8608,59 +8608,24 @@
 	}
 
 	ast_channel_lock_both(held_ch->ast, active_ch->ast);
-
-	transferee = ast_bridged_channel(held_ch->ast);
-	if (!transferee) {
-		/*
-		 * Could not transfer.  Held channel is not bridged anymore.
-		 * Held party probably got tired of waiting and hung up.
-		 */
-		ast_channel_unlock(held_ch->ast);
-		ast_channel_unlock(active_ch->ast);
-		return -1;
-	}
-
-	target = active_ch->ast;
+	to_target = active_ch->ast;
+	to_transferee = held_ch->ast;
 	chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n",
-		ast_channel_name(held_ch->ast), ast_channel_name(target));
-
-	ast_party_connected_line_init(&target_colp);
-	ast_party_connected_line_copy(&target_colp, ast_channel_connected(target));
-
-	/* Reset any earlier private connected id representation */
-	ast_party_id_reset(&target_colp.priv);
-
-	ast_party_connected_line_init(&transferee_colp);
-	ast_party_connected_line_copy(&transferee_colp, ast_channel_connected(held_ch->ast));
-
-	/* Reset any earlier private connected id representation*/
-	ast_party_id_reset(&transferee_colp.priv);
-
+		ast_channel_name(to_transferee), ast_channel_name(to_target));
 	held_ch->hold.state = MISDN_HOLD_TRANSFER;
-
-	/*
-	 * Before starting a masquerade, all channel and pvt locks must
-	 * be unlocked.  Any recursive channel locks held before
-	 * ast_channel_transfer_masquerade() invalidates deadlock
-	 * avoidance.  Since we are unlocking both the pvt and its owner
-	 * channel it is possible for "target" and "transferee" to be
-	 * destroyed by their pbx threads.  To prevent this we must give
-	 * "target" and "transferee" a reference before any unlocking
-	 * takes place.
-	 */
-	ao2_ref(target, +1);
-	ao2_ref(transferee, +1);
-	ast_channel_unlock(held_ch->ast);
-	ast_channel_unlock(active_ch->ast);
-
-	/* Setup transfer masquerade. */
-	retval = ast_channel_transfer_masquerade(target, &target_colp, 0,
-		transferee, &transferee_colp, 1);
-
-	ast_party_connected_line_free(&target_colp);
-	ast_party_connected_line_free(&transferee_colp);
-	ao2_ref(target, -1);
-	ao2_ref(transferee, -1);
+	ast_channel_ref(to_target);
+	ast_channel_ref(to_transferee);
+	ast_channel_unlock(to_target);
+	ast_channel_unlock(to_transferee);
+
+	retval = 0;
+	xfer_res = ast_bridge_transfer_attended(to_transferee, to_target);
+	if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) {
+		retval = -1;
+	}
+
+	ast_channel_unref(to_target);
+	ast_channel_unref(to_transferee);
 	return retval;
 }
 

Modified: team/dlee/record/channels/sig_analog.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/channels/sig_analog.c?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/channels/sig_analog.c (original)
+++ team/dlee/record/channels/sig_analog.c Thu Jun  6 21:51:30 2013
@@ -44,6 +44,7 @@
 #include "asterisk/cel.h"
 #include "asterisk/causes.h"
 #include "asterisk/features_config.h"
+#include "asterisk/bridging.h"
 
 #include "sig_analog.h"
 
@@ -688,87 +689,44 @@
  * \brief Attempt to transfer 3-way call.
  *
  * \param p Analog private structure.
- * \param inthreeway TRUE if the 3-way call is conferenced.
  *
- * \note
- * On entry these locks are held: real-call, private, 3-way call.
+ * \note On entry these locks are held: real-call, private, 3-way call.
+ * \note On exit these locks are held: real-call, private.
  *
- * \retval 1 Transfer successful.  3-way call is unlocked and subchannel is unalloced.
- *         Swapped real and 3-way subchannel.
- * \retval 0 Transfer successful.  3-way call is unlocked and subchannel is unalloced.
- * \retval -1 on error.  Caller must unlock 3-way call.
+ * \retval 0 on success.
+ * \retval -1 on error.
  */
-static int analog_attempt_transfer(struct analog_pvt *p, int inthreeway)
+static int analog_attempt_transfer(struct analog_pvt *p)
 {
 	struct ast_channel *owner_real;
 	struct ast_channel *owner_3way;
-	struct ast_channel *bridge_real;
-	struct ast_channel *bridge_3way;
-
-	owner_real = p->subs[ANALOG_SUB_REAL].owner;
-	owner_3way = p->subs[ANALOG_SUB_THREEWAY].owner;
-	bridge_real = ast_bridged_channel(owner_real);
-	bridge_3way = ast_bridged_channel(owner_3way);
-
-	/*
-	 * In order to transfer, we need at least one of the channels to
-	 * actually be in a call bridge.  We can't conference two
-	 * applications together.  Why would we want to?
-	 */
-	if (bridge_3way) {
-		ast_verb(3, "TRANSFERRING %s to %s\n", ast_channel_name(owner_3way), ast_channel_name(owner_real));
-		ast_cel_report_event(owner_3way,
-			(ast_channel_state(owner_real) == AST_STATE_RINGING
-				|| ast_channel_state(owner_3way) == AST_STATE_RINGING)
-				? AST_CEL_BLINDTRANSFER : AST_CEL_ATTENDEDTRANSFER,
-			NULL, ast_channel_linkedid(owner_3way), NULL);
-
-		/*
-		 * The three-way party we're about to transfer is on hold if he
-		 * is not in a three way conference.
-		 */
-		if (ast_channel_transfer_masquerade(owner_real, ast_channel_connected(owner_real), 0,
-			bridge_3way, ast_channel_connected(owner_3way), !inthreeway)) {
-			ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-				ast_channel_name(bridge_3way), ast_channel_name(owner_real));
-			return -1;
-		}
-
-		/* Three-way is now the REAL */
-		analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
-		ast_channel_unlock(owner_3way);
-		analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
-		/* Tell the caller not to hangup */
-		return 1;
-	} else if (bridge_real) {
-		/* Try transferring the other way. */
-		ast_verb(3, "TRANSFERRING %s to %s\n", ast_channel_name(owner_real), ast_channel_name(owner_3way));
-		ast_cel_report_event(owner_3way,
-			(ast_channel_state(owner_real) == AST_STATE_RINGING
-				|| ast_channel_state(owner_3way) == AST_STATE_RINGING)
-				? AST_CEL_BLINDTRANSFER : AST_CEL_ATTENDEDTRANSFER,
-			NULL, ast_channel_linkedid(owner_3way), NULL);
-
-		/*
-		 * The three-way party we're about to transfer is on hold if he
-		 * is not in a three way conference.
-		 */
-		if (ast_channel_transfer_masquerade(owner_3way, ast_channel_connected(owner_3way),
-			!inthreeway, bridge_real, ast_channel_connected(owner_real), 0)) {
-			ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-				ast_channel_name(bridge_real), ast_channel_name(owner_3way));
-			return -1;
-		}
-
-		/* Orphan the channel after releasing the lock */
-		ast_channel_unlock(owner_3way);
-		analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
-		return 0;
-	} else {
-		ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
-			ast_channel_name(owner_real), ast_channel_name(owner_3way));
-		return -1;
-	}
+	enum ast_transfer_result xfer_res;
+	int res = 0;
+
+	owner_real = ast_channel_ref(p->subs[ANALOG_SUB_REAL].owner);
+	owner_3way = ast_channel_ref(p->subs[ANALOG_SUB_THREEWAY].owner);
+
+	ast_verb(3, "TRANSFERRING %s to %s\n",
+		ast_channel_name(owner_3way), ast_channel_name(owner_real));
+
+	ast_channel_unlock(owner_real);
+	ast_channel_unlock(owner_3way);
+	analog_unlock_private(p);
+
+	xfer_res = ast_bridge_transfer_attended(owner_3way, owner_real);
+	if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) {
+		ast_softhangup(owner_3way, AST_SOFTHANGUP_DEV);
+		res = -1;
+	}
+
+	/* Must leave with these locked. */
+	ast_channel_lock(owner_real);
+	analog_lock_private(p);
+
+	ast_channel_unref(owner_real);
+	ast_channel_unref(owner_3way);
+
+	return res;
 }
 
 static int analog_update_conf(struct analog_pvt *p)
@@ -2892,10 +2850,6 @@
 						ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
 					} else if ((ast_channel_pbx(ast)) || (ast_channel_state(ast) == AST_STATE_UP)) {
 						if (p->transfer) {
-							int inthreeway;
-
-							inthreeway = p->subs[ANALOG_SUB_THREEWAY].inthreeway;
-
 							/* In any case this isn't a threeway call anymore */
 							analog_set_inthreeway(p, ANALOG_SUB_REAL, 0);
 							analog_set_inthreeway(p, ANALOG_SUB_THREEWAY, 0);
@@ -2909,16 +2863,13 @@
 								analog_set_new_owner(p, NULL);
 								/* Ring the phone */
 								analog_ring(p);
-							} else {
-								res = analog_attempt_transfer(p, inthreeway);
-								if (res < 0) {
-									/* Transfer attempt failed. */
-									ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
-									ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
-								} else if (res) {
-									/* Don't actually hang up at this point */
-									break;
-								}
+							} else if (!analog_attempt_transfer(p)) {
+								/*
+								 * Transfer successful.  Don't actually hang up at this point.
+								 * Let our channel legs of the calls die off as the transfer
+								 * percolates through the core.
+								 */
+								break;
 							}
 						} else {
 							ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);

Modified: team/dlee/record/channels/sig_pri.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/channels/sig_pri.c?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/channels/sig_pri.c (original)
+++ team/dlee/record/channels/sig_pri.c Thu Jun  6 21:51:30 2013
@@ -50,6 +50,7 @@
 #include "asterisk/transcap.h"
 #include "asterisk/features.h"
 #include "asterisk/aoc.h"
+#include "asterisk/bridging.h"
 
 #include "sig_pri.h"
 #ifndef PRI_EVENT_FACILITY
@@ -1587,6 +1588,10 @@
 		strcpy(new_chan->moh_suggested, old_chan->moh_suggested);
 		new_chan->moh_state = old_chan->moh_state;
 		old_chan->moh_state = SIG_PRI_MOH_STATE_IDLE;
+#if defined(HAVE_PRI_TRANSFER)
+		new_chan->xfer_data = old_chan->xfer_data;
+		old_chan->xfer_data = NULL;
+#endif	/* defined(HAVE_PRI_TRANSFER) */
 
 #if defined(HAVE_PRI_AOC_EVENTS)
 		new_chan->aoc_s_request_invoke_id = old_chan->aoc_s_request_invoke_id;
@@ -2396,6 +2401,8 @@
 	q931_call *call;
 	/*! Invocation ID to use when sending a reply to the transfer request. */
 	int invoke_id;
+	/*! TRUE if the transfer response has been made. */
+	int responded;
 };
 #endif	/* defined(HAVE_PRI_TRANSFER) */
 
@@ -2405,31 +2412,23 @@
  * \brief Send the transfer success/fail response message.
  * \since 1.8
  *
- * \param data Callback user data pointer
+ * \param rsp Transfer response data.
  * \param is_successful TRUE if the transfer was successful.
  *
+ * \note Assumes the rsp->pri->lock is already obtained.
+ *
  * \return Nothing
  */
-static void sig_pri_transfer_rsp(void *data, int is_successful)
-{
-	struct xfer_rsp_data *rsp = data;
+static void sig_pri_transfer_rsp(struct xfer_rsp_data *rsp, int is_successful)
+{
+	if (rsp->responded) {
+		return;
+	}
+	rsp->responded = 1;
 
 	pri_transfer_rsp(rsp->pri->pri, rsp->call, rsp->invoke_id, is_successful);
 }
 #endif	/* defined(HAVE_PRI_TRANSFER) */
-
-#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
-/*!
- * \brief Protocol callback to indicate if transfer will happen.
- * \since 1.8
- *
- * \param data Callback user data pointer
- * \param is_successful TRUE if the transfer will happen.
- *
- * \return Nothing
- */
-typedef void (*xfer_rsp_callback)(void *data, int is_successful);
-#endif	/* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
 
 #if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
 /*!
@@ -2442,15 +2441,14 @@
  * \param call_1_held TRUE if call_1_pri is on hold.
  * \param call_2_pri Second call involved in the transfer. (target; usually active/ringing)
  * \param call_2_held TRUE if call_2_pri is on hold.
- * \param rsp_callback Protocol callback to indicate if transfer will happen. NULL if not used.
- * \param data Callback user data pointer
+ * \param xfer_data Transfer response data if non-NULL.
  *
  * \note Assumes the pri->lock is already obtained.
  *
  * \retval 0 on success.
  * \retval -1 on error.
  */
-static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_pri, int call_1_held, q931_call *call_2_pri, int call_2_held, xfer_rsp_callback rsp_callback, void *data)
+static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_pri, int call_1_held, q931_call *call_2_pri, int call_2_held, struct xfer_rsp_data *xfer_data)
 {
 	struct attempt_xfer_call {
 		q931_call *pri;
@@ -2459,10 +2457,9 @@
 		int chanpos;
 	};
 	int retval;
-	struct ast_channel *transferee;
+	enum ast_transfer_result xfer_res;
 	struct attempt_xfer_call *call_1;
 	struct attempt_xfer_call *call_2;
-	struct attempt_xfer_call *swap_call;
 	struct attempt_xfer_call c1;
 	struct attempt_xfer_call c2;
 
@@ -2478,118 +2475,104 @@
 	call_2->chanpos = pri_find_principle_by_call(pri, call_2->pri);
 	if (call_1->chanpos < 0 || call_2->chanpos < 0) {
 		/* Calls not found in span control. */
-		if (rsp_callback) {
+#if defined(HAVE_PRI_TRANSFER)
+		if (xfer_data) {
 			/* Transfer failed. */
-			rsp_callback(data, 0);
-		}
+			sig_pri_transfer_rsp(xfer_data, 0);
+		}
+#endif	/* defined(HAVE_PRI_TRANSFER) */
 		return -1;
 	}
 
-	/* Attempt to make transferee and target consistent. */
-	if (!call_1->held && call_2->held) {
-		/*
-		 * Swap call_1 and call_2 to make call_1 the transferee(held call)
-		 * and call_2 the target(active call).
-		 */
-		swap_call = call_1;
-		call_1 = call_2;
-		call_2 = swap_call;
-	}
-
-	/* Deadlock avoidance is attempted. */
+	/* Get call_1 owner. */
 	sig_pri_lock_private(pri->pvts[call_1->chanpos]);
 	sig_pri_lock_owner(pri, call_1->chanpos);
+	call_1->ast = pri->pvts[call_1->chanpos]->owner;
+	if (call_1->ast) {
+		ast_channel_ref(call_1->ast);
+		ast_channel_unlock(call_1->ast);
+	}
+	sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
+
+	/* Get call_2 owner. */
 	sig_pri_lock_private(pri->pvts[call_2->chanpos]);
 	sig_pri_lock_owner(pri, call_2->chanpos);
-
-	call_1->ast = pri->pvts[call_1->chanpos]->owner;
 	call_2->ast = pri->pvts[call_2->chanpos]->owner;
+	if (call_2->ast) {
+		ast_channel_ref(call_2->ast);
+		ast_channel_unlock(call_2->ast);
+	}
+	sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
+
 	if (!call_1->ast || !call_2->ast) {
 		/* At least one owner is not present. */
 		if (call_1->ast) {
-			ast_channel_unlock(call_1->ast);
+			ast_channel_unref(call_1->ast);
 		}
 		if (call_2->ast) {
-			ast_channel_unlock(call_2->ast);
-		}
+			ast_channel_unref(call_2->ast);
+		}
+#if defined(HAVE_PRI_TRANSFER)
+		if (xfer_data) {
+			/* Transfer failed. */
+			sig_pri_transfer_rsp(xfer_data, 0);
+		}
+#endif	/* defined(HAVE_PRI_TRANSFER) */
+		return -1;
+	}
+
+	ast_verb(3, "TRANSFERRING %s to %s\n",
+		ast_channel_name(call_1->ast), ast_channel_name(call_2->ast));
+
+#if defined(HAVE_PRI_TRANSFER)
+	if (xfer_data) {
+		/*
+		 * Add traps on the transferer channels in case threading causes
+		 * them to hangup before ast_bridge_transfer_attended() returns
+		 * and we can get the pri->lock back.
+		 */
+		sig_pri_lock_private(pri->pvts[call_1->chanpos]);
+		pri->pvts[call_1->chanpos]->xfer_data = xfer_data;
 		sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
+		sig_pri_lock_private(pri->pvts[call_2->chanpos]);
+		pri->pvts[call_2->chanpos]->xfer_data = xfer_data;
 		sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
-		if (rsp_callback) {
-			/* Transfer failed. */
-			rsp_callback(data, 0);
-		}
-		return -1;
-	}
-
-	for (;;) {
-		transferee = ast_bridged_channel(call_1->ast);
-		if (transferee) {
-			break;
-		}
-
-		/* Try masquerading the other way. */
-		swap_call = call_1;
-		call_1 = call_2;
-		call_2 = swap_call;
-
-		transferee = ast_bridged_channel(call_1->ast);
-		if (transferee) {
-			break;
-		}
-
-		/* Could not transfer.  Neither call is bridged. */
-		ast_channel_unlock(call_1->ast);
-		ast_channel_unlock(call_2->ast);
-		sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
-		sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
-
-		if (rsp_callback) {
-			/* Transfer failed. */
-			rsp_callback(data, 0);
-		}
-		return -1;
-	}
-
-	ast_verb(3, "TRANSFERRING %s to %s\n", ast_channel_name(call_1->ast), ast_channel_name(call_2->ast));
-
-	/*
-	 * Setup transfer masquerade.
-	 *
-	 * Note:  There is an extremely nasty deadlock avoidance issue
-	 * with ast_channel_transfer_masquerade().  Deadlock may be possible if
-	 * the channels involved are proxies (chan_agent channels) and
-	 * it is called with locks.  Unfortunately, there is no simple
-	 * or even merely difficult way to guarantee deadlock avoidance
-	 * and still be able to send an ECT success response without the
-	 * possibility of the bridged channel hanging up on us.
-	 */
+	}
+#endif	/* defined(HAVE_PRI_TRANSFER) */
+
 	ast_mutex_unlock(&pri->lock);
-	retval = ast_channel_transfer_masquerade(
-		call_2->ast,
-		ast_channel_connected(call_2->ast),
-		call_2->held,
-		transferee,
-		ast_channel_connected(call_1->ast),
-		call_1->held);
-
-	/* Reacquire the pri->lock to hold off completion of the transfer masquerade. */
+	xfer_res = ast_bridge_transfer_attended(call_1->ast, call_2->ast);
 	ast_mutex_lock(&pri->lock);
-
-	ast_channel_unlock(call_1->ast);
-	ast_channel_unlock(call_2->ast);
-	sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
-	sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
-
-	if (rsp_callback) {
+	retval = (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) ? -1 : 0;
+
+#if defined(HAVE_PRI_TRANSFER)
+	if (xfer_data) {
+		int rsp_chanpos;
+
 		/*
-		 * Report transfer status.
+		 * Remove the transferrer channel traps.
 		 *
-		 * Must do the callback before the masquerade completes to ensure
-		 * that the protocol message goes out before the call leg is
-		 * disconnected.
+		 * We must refind chanpos because we released pri->lock.
 		 */
-		rsp_callback(data, retval ? 0 : 1);
-	}
+		rsp_chanpos = pri_find_principle_by_call(pri, call_1->pri);
+		if (0 <= rsp_chanpos) {
+			sig_pri_lock_private(pri->pvts[rsp_chanpos]);
+			pri->pvts[rsp_chanpos]->xfer_data = NULL;
+			sig_pri_unlock_private(pri->pvts[rsp_chanpos]);
+		}
+		rsp_chanpos = pri_find_principle_by_call(pri, call_2->pri);
+		if (0 <= rsp_chanpos) {
+			sig_pri_lock_private(pri->pvts[rsp_chanpos]);
+			pri->pvts[rsp_chanpos]->xfer_data = NULL;
+			sig_pri_unlock_private(pri->pvts[rsp_chanpos]);
+		}
+
+		/* Report transfer status. */
+		sig_pri_transfer_rsp(xfer_data, retval ? 0 : 1);
+	}
+#endif	/* defined(HAVE_PRI_TRANSFER) */
+	ast_channel_unref(call_1->ast);
+	ast_channel_unref(call_2->ast);
 	return retval;
 }
 #endif	/* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
@@ -4457,10 +4440,11 @@
 			xfer_rsp.pri = pri;
 			xfer_rsp.call = call_rsp;
 			xfer_rsp.invoke_id = subcmd->u.transfer.invoke_id;
+			xfer_rsp.responded = 0;
 			sig_pri_attempt_transfer(pri,
 				subcmd->u.transfer.call_1, subcmd->u.transfer.is_call_1_held,
 				subcmd->u.transfer.call_2, subcmd->u.transfer.is_call_2_held,
-				sig_pri_transfer_rsp, &xfer_rsp);
+				&xfer_rsp);
 			sig_pri_lock_private(pri->pvts[chanpos]);
 			break;
 #endif	/* defined(HAVE_PRI_TRANSFER) */
@@ -7114,7 +7098,7 @@
 					/* We are to transfer the call instead of simply hanging up. */
 					sig_pri_unlock_private(pri->pvts[chanpos]);
 					if (!sig_pri_attempt_transfer(pri, e->hangup.call_held, 1,
-						e->hangup.call_active, 0, NULL, NULL)) {
+						e->hangup.call_active, 0, NULL)) {
 						break;
 					}
 					sig_pri_lock_private(pri->pvts[chanpos]);
@@ -7595,6 +7579,20 @@
 		}
 #endif	/* defined(SUPPORT_USERUSER) */
 
+#if defined(HAVE_PRI_TRANSFER)
+		if (p->xfer_data) {
+			/*
+			 * The transferrer call leg is disconnecting.  It must mean that
+			 * the transfer was successful and the core is disconnecting the
+			 * call legs involved.
+			 *
+			 * The transfer protocol response message must go out before the
+			 * call leg is disconnected.
+			 */
+			sig_pri_transfer_rsp(p->xfer_data, 1);
+		}
+#endif	/* defined(HAVE_PRI_TRANSFER) */
+
 #if defined(HAVE_PRI_AOC_EVENTS)
 		if (p->holding_aoce) {
 			pri_aoc_e_send(p->pri->pri, p->call, &p->aoc_e);
@@ -7623,6 +7621,9 @@
 			pri_hangup(p->pri->pri, p->call, icause);
 		}
 	}
+#if defined(HAVE_PRI_TRANSFER)
+	p->xfer_data = NULL;
+#endif	/* defined(HAVE_PRI_TRANSFER) */
 #if defined(HAVE_PRI_AOC_EVENTS)
 	p->aoc_s_request_invoke_id_valid = 0;
 	p->holding_aoce = 0;

Modified: team/dlee/record/channels/sig_pri.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/channels/sig_pri.h?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/channels/sig_pri.h (original)
+++ team/dlee/record/channels/sig_pri.h Thu Jun  6 21:51:30 2013
@@ -172,6 +172,7 @@
 };
 
 struct sig_pri_span;
+struct xfer_rsp_data;
 
 struct sig_pri_callback {
 	/* Unlock the private in the signalling private structure.  This is used for three way calling madness. */
@@ -353,6 +354,10 @@
 	enum sig_pri_call_level call_level;
 	/*! \brief Channel reset/restart state. */
 	enum sig_pri_reset_state resetting;
+#if defined(HAVE_PRI_TRANSFER)
+	/*! If non-NULL, send transfer disconnect successfull response to first call disconnecting. */
+	struct xfer_rsp_data *xfer_data;
+#endif	/* defined(HAVE_PRI_TRANSFER) */
 	int prioffset;					/*!< channel number in span */
 	int logicalspan;				/*!< logical span number within trunk group */
 	int mastertrunkgroup;			/*!< what trunk group is our master */

Modified: team/dlee/record/configs/chan_dahdi.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/configs/chan_dahdi.conf.sample?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/configs/chan_dahdi.conf.sample (original)
+++ team/dlee/record/configs/chan_dahdi.conf.sample Thu Jun  6 21:51:30 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: team/dlee/record/configs/iax.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/configs/iax.conf.sample?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/configs/iax.conf.sample (original)
+++ team/dlee/record/configs/iax.conf.sample Thu Jun  6 21:51:30 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: team/dlee/record/configs/sip.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/configs/sip.conf.sample?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/configs/sip.conf.sample (original)
+++ team/dlee/record/configs/sip.conf.sample Thu Jun  6 21:51:30 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: team/dlee/record/configs/skinny.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/configs/skinny.conf.sample?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/configs/skinny.conf.sample (original)
+++ team/dlee/record/configs/skinny.conf.sample Thu Jun  6 21:51:30 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: team/dlee/record/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/record/include/asterisk/bridging.h?view=diff&rev=390821&r1=390820&r2=390821
==============================================================================
--- team/dlee/record/include/asterisk/bridging.h (original)
+++ team/dlee/record/include/asterisk/bridging.h Thu Jun  6 21:51:30 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

[... 908 lines stripped ...]



More information about the asterisk-commits mailing list