[svn-commits] kmoore: trunk r420338 - in /trunk: ./ apps/ include/asterisk/ main/ res/ res/...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Aug 7 10:30:25 CDT 2014


Author: kmoore
Date: Thu Aug  7 10:30:19 2014
New Revision: 420338

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=420338
Log:
Stasis: Convey transfer information to applications

This fixes a class of issues where Stasis applications were not made
aware that their channels were being manipulated or replaced by
external entitiessuch as transfers, AMI commands, or dialplan
applications such as Bridge(). Inconsistent information such as
StasisEnd events with unknown channels as a result of masquerades has
also been corrected. To accomplish these fixes, several new fields
were added to blind and attended transfer messages as well as
StasisStart and BridgeAttendedTransfer Stasis events.

ASTERISK-23941 #close
Review: https://reviewboard.asterisk.org/r/3865/
Review: https://reviewboard.asterisk.org/r/3857/
Review: https://reviewboard.asterisk.org/r/3852/
Review: https://reviewboard.asterisk.org/r/3816/
Review: https://reviewboard.asterisk.org/r/3731/
Review: https://reviewboard.asterisk.org/r/3729/
Review: https://reviewboard.asterisk.org/r/3728/
........

Merged revisions 420325 from http://svn.asterisk.org/svn/asterisk/branches/12

Modified:
    trunk/   (props changed)
    trunk/apps/app_queue.c
    trunk/include/asterisk/bridge_features.h
    trunk/include/asterisk/datastore.h
    trunk/include/asterisk/stasis_app.h
    trunk/include/asterisk/stasis_bridges.h
    trunk/main/bridge.c
    trunk/main/bridge_basic.c
    trunk/main/cel.c
    trunk/main/channel.c
    trunk/main/stasis_bridges.c
    trunk/res/ari/ari_model_validators.c
    trunk/res/ari/ari_model_validators.h
    trunk/res/res_stasis.c
    trunk/res/stasis/app.c
    trunk/res/stasis/app.h
    trunk/res/stasis/command.c
    trunk/res/stasis/command.h
    trunk/res/stasis/control.c
    trunk/res/stasis/control.h
    trunk/res/stasis/stasis_bridge.c
    trunk/rest-api/api-docs/events.json
    trunk/tests/test_cel.c

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-12-merged' - no diff available.

Modified: trunk/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/trunk/apps/app_queue.c?view=diff&rev=420338&r1=420337&r2=420338
==============================================================================
--- trunk/apps/app_queue.c (original)
+++ trunk/apps/app_queue.c Thu Aug  7 10:30:19 2014
@@ -5551,6 +5551,7 @@
 		ast_str_set(&transfer_str, 0, "BRIDGE|%s", atxfer_msg->dest.bridge);
 		break;
 	case AST_ATTENDED_TRANSFER_DEST_APP:
+	case AST_ATTENDED_TRANSFER_DEST_LOCAL_APP:
 		ast_str_set(&transfer_str, 0, "APP|%s", atxfer_msg->dest.app);
 		break;
 	case AST_ATTENDED_TRANSFER_DEST_LINK:
@@ -5619,10 +5620,7 @@
 		struct stasis_message *msg)
 {
 	struct queue_stasis_data *queue_data = userdata;
-	struct ast_bridge_blob *blind_blob = stasis_message_data(msg);
-	struct ast_json *result_blob;
-	struct ast_json *exten_blob;
-	struct ast_json *context_blob;
+	struct ast_blind_transfer_message *transfer_msg = stasis_message_data(msg);
 	const char *exten;
 	const char *context;
 	RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
@@ -5632,19 +5630,14 @@
 		return;
 	}
 
-	result_blob = ast_json_object_get(blind_blob->blob, "result");
-	if (!result_blob) {
+	if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
 		return;
 	}
 
-	if (ast_json_integer_get(result_blob) != AST_BRIDGE_TRANSFER_SUCCESS) {
-		return;
-	}
-
 	ao2_lock(queue_data);
 
 	if (ast_strlen_zero(queue_data->bridge_uniqueid) ||
-			strcmp(queue_data->bridge_uniqueid, blind_blob->bridge->uniqueid)) {
+			strcmp(queue_data->bridge_uniqueid, transfer_msg->to_transferee.bridge_snapshot->uniqueid)) {
 		ao2_unlock(queue_data);
 		return;
 	}
@@ -5654,10 +5647,8 @@
 
 	ao2_unlock(queue_data);
 
-	exten_blob = ast_json_object_get(blind_blob->blob, "exten");
-	exten = exten_blob ? ast_json_string_get(exten_blob) : "<unknown>";
-	context_blob = ast_json_object_get(blind_blob->blob, "context");
-	context = context_blob ? ast_json_string_get(context_blob) : "<unknown>";
+	exten = transfer_msg->exten;
+	context = transfer_msg->context;
 
 	ast_debug(3, "Detected blind transfer in queue %s\n", queue_data->queue->name);
 	ast_queue_log(queue_data->queue->name, caller_snapshot->uniqueid, queue_data->member->membername,

Modified: trunk/include/asterisk/bridge_features.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/bridge_features.h?view=diff&rev=420338&r1=420337&r2=420338
==============================================================================
--- trunk/include/asterisk/bridge_features.h (original)
+++ trunk/include/asterisk/bridge_features.h Thu Aug  7 10:30:19 2014
@@ -157,6 +157,25 @@
  */
 typedef int (*ast_bridge_talking_indicate_callback)(struct ast_bridge_channel *bridge_channel, void *hook_pvt, int talking);
 
+/*!
+ * \brief Move indicator callback
+ *
+ * \details
+ * This callback can be registered with the bridge channel in order
+ * to be notified when the bridge channel is being moved from one
+ * bridge to another.
+ *
+ * \param bridge_channel The channel executing the feature
+ * \param hook_pvt Private data passed in when the hook was created
+ * \param src The bridge from which the bridge channel is moving
+ * \param dst The bridge into which the bridge channel is moving
+ *
+ * \retval 0 Keep the callback hook.
+ * \retval -1 Remove the callback hook.
+ */
+typedef int (*ast_bridge_move_indicate_callback)(struct ast_bridge_channel *bridge_channel,
+		void *hook_pvt, struct ast_bridge *src, struct ast_bridge *dst);
+
 enum ast_bridge_hook_remove_flags {
 	/*! The hook is removed when the channel is pulled from the bridge. */
 	AST_BRIDGE_HOOK_REMOVE_ON_PULL = (1 << 0),
@@ -173,6 +192,7 @@
 	AST_BRIDGE_HOOK_TYPE_JOIN,
 	AST_BRIDGE_HOOK_TYPE_LEAVE,
 	AST_BRIDGE_HOOK_TYPE_TALK,
+	AST_BRIDGE_HOOK_TYPE_MOVE,
 };
 
 /*! \brief Structure that is the essence of a feature hook. */
@@ -621,6 +641,37 @@
 	enum ast_bridge_hook_remove_flags remove_flags);
 
 /*!
+ * \brief Attach a bridge channel move detection hook to a bridge features structure
+ *
+ * \param features Bridge features structure
+ * \param callback Function to execute upon activation
+ * \param hook_pvt Unique data
+ * \param destructor Optional destructor callback for hook_pvt data
+ * \param remove_flags Dictates what situations the hook should be removed.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure (The caller must cleanup any hook_pvt resources.)
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * ast_bridge_move_hook(&features, move_callback, NULL, NULL, 0);
+ * \endcode
+ *
+ * This makes the bridging core call \ref callback when a
+ * channel is moved from one bridge to another.  A
+ * pointer to useful data may be provided to the hook_pvt
+ * parameter.
+ */
+int ast_bridge_move_hook(struct ast_bridge_features *features,
+	ast_bridge_move_indicate_callback callback,
+	void *hook_pvt,
+	ast_bridge_hook_pvt_destructor destructor,
+	enum ast_bridge_hook_remove_flags remove_flags);
+
+/*!
  * \brief Enable a built in feature on a bridge features structure
  *
  * \param features Bridge features structure

Modified: trunk/include/asterisk/datastore.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/datastore.h?view=diff&rev=420338&r1=420337&r2=420338
==============================================================================
--- trunk/include/asterisk/datastore.h (original)
+++ trunk/include/asterisk/datastore.h Thu Aug  7 10:30:19 2014
@@ -34,7 +34,7 @@
 	void (*destroy)(void *data);		/*!< Destroy function */
 
 	/*!
-	 * \brief Fix up channel references
+	 * \brief Fix up channel references on the masquerading channel
 	 *
 	 * \arg data The datastore data
 	 * \arg old_chan The old channel owning the datastore
@@ -48,6 +48,20 @@
 	 * \return nothing.
 	 */
 	void (*chan_fixup)(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
+
+	/*!
+	 * \brief Fix up channel references on the channel being masqueraded into
+	 *
+	 * \arg data The datastore data
+	 * \arg old_chan The old channel owning the datastore
+	 * \arg new_chan The new channel owning the datastore
+	 *
+	 * This is the same as the above callback, except it is called for the channel
+	 * being masqueraded into instead of the channel that is masquerading.
+	 *
+	 * \return nothing.
+	 */
+	void (*chan_breakdown)(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
 };
 
 /*! \brief Structure for a data store object */

Modified: trunk/include/asterisk/stasis_app.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/stasis_app.h?view=diff&rev=420338&r1=420337&r2=420338
==============================================================================
--- trunk/include/asterisk/stasis_app.h (original)
+++ trunk/include/asterisk/stasis_app.h Thu Aug  7 10:30:19 2014
@@ -799,6 +799,28 @@
  */
 struct stasis_message_sanitizer *stasis_app_get_sanitizer(void);
 
+/*!
+ * \brief Stasis message type for a StasisEnd event
+ */
+struct stasis_message_type *ast_stasis_end_message_type(void);
+
+/*!
+ * \brief Indicate that this channel has had a StasisEnd published for it
+ *
+ * \param The channel that is exiting Stasis.
+ */
+void stasis_app_channel_set_stasis_end_published(struct ast_channel *chan);
+
+/*!
+ * \brief Has this channel had a StasisEnd published on it?
+ *
+ * \param chan The channel upon which the query rests.
+ *
+ * \retval 0 No
+ * \retval 1 Yes
+ */
+int stasis_app_channel_is_stasis_end_published(struct ast_channel *chan);
+
 /*! @} */
 
 #endif /* _ASTERISK_STASIS_APP_H */

Modified: trunk/include/asterisk/stasis_bridges.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/stasis_bridges.h?view=diff&rev=420338&r1=420337&r2=420338
==============================================================================
--- trunk/include/asterisk/stasis_bridges.h (original)
+++ trunk/include/asterisk/stasis_bridges.h Thu Aug  7 10:30:19 2014
@@ -285,6 +285,24 @@
 struct stasis_message_type *ast_blind_transfer_type(void);
 
 /*!
+ * \brief Message published during a blind transfer
+ */
+struct ast_blind_transfer_message {
+	/*! Result of the transfer */
+	enum ast_transfer_result result;
+	/*! True if the transfer was initiated by an external source (i.e. not DTMF-initiated) */
+	int is_external;
+	/*! Transferer and its bridge */
+	struct ast_bridge_channel_snapshot_pair to_transferee;
+	/*! Destination context */
+	char context[AST_MAX_CONTEXT];
+	/*! Destination extension */
+	char exten[AST_MAX_EXTENSION];
+	/*! Transferee channel. NULL if there were multiple transferee channels */
+	struct ast_channel_snapshot *transferee;
+};
+
+/*!
  * \brief Publish a blind transfer event
  *
  * \pre Bridges involved are locked. Channels involved are not locked.
@@ -294,9 +312,11 @@
  * \param to_transferee The bridge between the transferer and transferee plus the transferer channel
  * \param context The destination context for the blind transfer
  * \param exten The destination extension for the blind transfer
+ * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
  */
 void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result result,
-		struct ast_bridge_channel_pair *to_transferee, const char *context, const char *exten);
+		struct ast_bridge_channel_pair *to_transferee, const char *context, const char *exten,
+		struct ast_channel *transferee_channel);
 
 enum ast_attended_transfer_dest_type {
 	/*! The transfer failed, so there is no appropriate final state */
@@ -305,6 +325,8 @@
 	AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE,
 	/*! The transfer results in a channel or bridge running an application */
 	AST_ATTENDED_TRANSFER_DEST_APP,
+	/*! The transfer results in a channel or bridge running an application via a local channel */
+	AST_ATTENDED_TRANSFER_DEST_LOCAL_APP,
 	/*! The transfer results in both bridges remaining with a local channel linking them */
 	AST_ATTENDED_TRANSFER_DEST_LINK,
 	/*! The transfer results in a threeway call between transferer, transferee, and transfer target */
@@ -323,6 +345,12 @@
 	struct ast_bridge_channel_snapshot_pair to_transferee;
 	/*! Bridge between transferer <-> transfer target and the transferer channel in that bridge. May be NULL */
 	struct ast_bridge_channel_snapshot_pair to_transfer_target;
+	/*! Local channel connecting transferee bridge to application */
+	struct ast_channel_snapshot *replace_channel;
+	/*! Transferee channel. Will be NULL if there were multiple channels transferred. */
+	struct ast_channel_snapshot *transferee;
+	/*! Transfer target channel. Will be NULL if there were multiple channels targeted. */
+	struct ast_channel_snapshot *target;
 	/*! Indicates the final state of the transfer */
 	enum ast_attended_transfer_dest_type dest_type;
 	union {
@@ -358,9 +386,12 @@
  * \param result The result of the transfer. Will always be a type of failure.
  * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
  * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
+ * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
+ * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
  */
 void ast_bridge_publish_attended_transfer_fail(int is_external, enum ast_transfer_result result,
-		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target);
+		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
+		struct ast_channel *transferee_channel, struct ast_channel *target_channel);
 
 /*!
  * \since 12
@@ -382,10 +413,13 @@
  * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
  * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
  * \param final_bridge The bridge that the parties end up in. Will be a bridge from the transferee or target pair.
+ * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
+ * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
  */
 void ast_bridge_publish_attended_transfer_bridge_merge(int is_external, enum ast_transfer_result result,
 		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-		struct ast_bridge *final_bridge);
+		struct ast_bridge *final_bridge, struct ast_channel *transferee_channel,
+		struct ast_channel *target_channel);
 
 /*!
  * \since 12
@@ -403,10 +437,13 @@
  * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
  * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
  * \param final_pair The bridge that the parties end up in, and the transferer channel that is in this bridge.
+ * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
+ * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
  */
 void ast_bridge_publish_attended_transfer_threeway(int is_external, enum ast_transfer_result result,
 		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-		struct ast_bridge_channel_pair *final_pair);
+		struct ast_bridge_channel_pair *final_pair, struct ast_channel *transferee_channel,
+		struct ast_channel *target_channel);
 
 /*!
  * \since 12
@@ -423,13 +460,23 @@
  *
  * \param is_external Indicates if the transfer was initiated externally
  * \param result The result of the transfer.
- * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
- * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
- * \param dest_app The application that the channel or bridge is running upon transfer completion.
+ * \param transferee The bridge between the transferer and transferees as well as the
+ *        transferer channel from that bridge
+ * \param target The bridge between the transferer and transfer targets as well as the
+ *        transferer channel from that bridge
+ * \param replace_channel The channel that will be replacing the transferee bridge
+ *        transferer channel when a local channel is involved
+ * \param dest_app The application that the channel or bridge is running upon transfer
+ *        completion.
+ * \param transferee_channel If a single channel is being transferred, this is it.
+ *        If multiple parties are being transferred, this is NULL.
+ * \param target_channel If a single channel is being transferred to, this is it.
+ *        If multiple parties are being transferred to, this is NULL.
  */
 void ast_bridge_publish_attended_transfer_app(int is_external, enum ast_transfer_result result,
 		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-		const char *dest_app);
+		struct ast_channel *replace_channel, const char *dest_app,
+		struct ast_channel *transferee_channel, struct ast_channel *target_channel);
 
 /*!
  * \since 12
@@ -451,10 +498,13 @@
  * \param transferee The bridge between the transferer and transferees as well as the transferer channel from that bridge
  * \param target The bridge between the transferer and transfer targets as well as the transferer channel from that bridge
  * \param locals The local channels linking the bridges together.
+ * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
+ * \param target_channel If a single channel is being transferred to, this is it. If multiple parties are being transferred to, this is NULL.
  */
 void ast_bridge_publish_attended_transfer_link(int is_external, enum ast_transfer_result result,
 		struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target,
-		struct ast_channel *locals[2]);
+		struct ast_channel *locals[2], struct ast_channel *transferee_channel,
+		struct ast_channel *target_channel);
 
 /*!
  * \brief Returns the most recent snapshot for the bridge.

Modified: trunk/main/bridge.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/bridge.c?view=diff&rev=420338&r1=420337&r2=420338
==============================================================================
--- trunk/main/bridge.c (original)
+++ trunk/main/bridge.c Thu Aug  7 10:30:19 2014
@@ -1825,6 +1825,32 @@
 	ao2_ref(old_bridge, -1);
 }
 
+static void bridge_channel_moving(struct ast_bridge_channel *bridge_channel, struct ast_bridge *src, struct ast_bridge *dst)
+{
+	struct ast_bridge_features *features = bridge_channel->features;
+	struct ast_bridge_hook *hook;
+	struct ao2_iterator iter;
+
+	/* Run any moving hooks. */
+	iter = ao2_iterator_init(features->other_hooks, 0);
+	for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
+		int remove_me;
+		ast_bridge_move_indicate_callback move_cb;
+
+		if (hook->type != AST_BRIDGE_HOOK_TYPE_MOVE) {
+			continue;
+		}
+		move_cb = (ast_bridge_move_indicate_callback) hook->callback;
+		remove_me = move_cb(bridge_channel, hook->hook_pvt, src, dst);
+		if (remove_me) {
+			ast_debug(1, "Move detection hook %p is being removed from %p(%s)\n",
+				hook, bridge_channel, ast_channel_name(bridge_channel->chan));
+			ao2_unlink(features->other_hooks, hook);
+		}
+	}
+	ao2_iterator_destroy(&iter);
+}
+
 void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick,
 	unsigned int optimized)
 {
@@ -1872,6 +1898,8 @@
 			 */
 			continue;
 		}
+
+		bridge_channel_moving(bridge_channel, bridge_channel->bridge, dst_bridge);
 
 		/* Point to new bridge.*/
 		bridge_channel_change_bridge(bridge_channel, dst_bridge);
@@ -2122,6 +2150,8 @@
 	ao2_ref(orig_bridge, +1);/* Keep a ref in case the push fails. */
 	bridge_channel_change_bridge(bridge_channel, dst_bridge);
 
+	bridge_channel_moving(bridge_channel, orig_bridge, dst_bridge);
+
 	if (bridge_channel_internal_push(bridge_channel)) {
 		/* Try to put the channel back into the original bridge. */
 		ast_bridge_features_remove(bridge_channel->features,
@@ -3089,6 +3119,18 @@
 		AST_BRIDGE_HOOK_TYPE_TALK);
 }
 
+int ast_bridge_move_hook(struct ast_bridge_features *features,
+	ast_bridge_move_indicate_callback callback,
+	void *hook_pvt,
+	ast_bridge_hook_pvt_destructor destructor,
+	enum ast_bridge_hook_remove_flags remove_flags)
+{
+	ast_bridge_hook_callback hook_cb = (ast_bridge_hook_callback) callback;
+
+	return bridge_other_hook(features, hook_cb, hook_pvt, destructor, remove_flags,
+		AST_BRIDGE_HOOK_TYPE_MOVE);
+}
+
 int ast_bridge_interval_hook(struct ast_bridge_features *features,
 	enum ast_bridge_hook_timer_option flags,
 	unsigned int interval,
@@ -3828,14 +3870,55 @@
 	struct ast_bridge_channel_pair to_transferee;
 	/* The bridge between the transferer and transfer target, and the transferer channel in this bridge */
 	struct ast_bridge_channel_pair to_transfer_target;
+	/* The Local;1 that will replace the transferee bridge transferer channel */
+	struct ast_channel *replace_channel;
+	/* The transferee channel. NULL if there is no transferee channel or if multiple parties are transferred */
+	struct ast_channel *transferee_channel;
+	/* The transfer target channel. NULL if there is no transfer target channel or if multiple parties are transferred */
+	struct ast_channel *target_channel;
 };
+
+/*!
+ * \internal
+ * \brief Get the transferee channel
+ *
+ * This is only applicable to cases where a transfer is occurring on a
+ * two-party bridge. The channels container passed in is expected to only
+ * contain two channels, the transferer and the transferee. The transferer
+ * channel is passed in as a parameter to ensure we don't return it as
+ * the transferee channel.
+ *
+ * \param channels A two-channel container containing the transferer and transferee
+ * \param transferer The party that is transfering the call
+ * \return The party that is being transferred
+ */
+static struct ast_channel *get_transferee(struct ao2_container *channels, struct ast_channel *transferer)
+{
+	struct ao2_iterator channel_iter;
+	struct ast_channel *transferee;
+
+	for (channel_iter = ao2_iterator_init(channels, 0);
+			(transferee = ao2_iterator_next(&channel_iter));
+			ao2_cleanup(transferee)) {
+		if (transferee != transferer) {
+			break;
+		}
+	}
+
+	ao2_iterator_destroy(&channel_iter);
+	return transferee;
+}
+
 
 static void stasis_publish_data_cleanup(struct stasis_attended_transfer_publish_data *publication)
 {
 	ast_channel_unref(publication->to_transferee.channel);
 	ast_channel_unref(publication->to_transfer_target.channel);
+	ast_channel_cleanup(publication->transferee_channel);
+	ast_channel_cleanup(publication->target_channel);
 	ao2_cleanup(publication->to_transferee.bridge);
 	ao2_cleanup(publication->to_transfer_target.bridge);
+	ao2_cleanup(publication->replace_channel);
 }
 
 /*!
@@ -3865,6 +3948,9 @@
 		ao2_ref(to_target_bridge, +1);
 		publication->to_transfer_target.bridge = to_target_bridge;
 	}
+
+	publication->transferee_channel = ast_bridge_peer(to_transferee_bridge, to_transferee);
+	publication->target_channel = ast_bridge_peer(to_target_bridge, to_transfer_target);
 }
 
 /*
@@ -3878,7 +3964,8 @@
 		struct ast_bridge *final_bridge)
 {
 	ast_bridge_publish_attended_transfer_bridge_merge(1, AST_BRIDGE_TRANSFER_SUCCESS,
-			&publication->to_transferee, &publication->to_transfer_target, final_bridge);
+			&publication->to_transferee, &publication->to_transfer_target, final_bridge,
+			publication->transferee_channel, publication->target_channel);
 }
 
 /*
@@ -3892,7 +3979,9 @@
 		const char *app)
 {
 	ast_bridge_publish_attended_transfer_app(1, AST_BRIDGE_TRANSFER_SUCCESS,
-			&publication->to_transferee, &publication->to_transfer_target, app);
+			&publication->to_transferee, &publication->to_transfer_target,
+			publication->replace_channel, app,
+			publication->transferee_channel, publication->target_channel);
 }
 
 /*
@@ -3909,7 +3998,8 @@
 	struct ast_channel *locals[2] = { local_channel1, local_channel2 };
 
 	ast_bridge_publish_attended_transfer_link(1, AST_BRIDGE_TRANSFER_SUCCESS,
-			&publication->to_transferee, &publication->to_transfer_target, locals);
+			&publication->to_transferee, &publication->to_transfer_target, locals,
+			publication->transferee_channel, publication->target_channel);
 }
 
 /*
@@ -3923,7 +4013,8 @@
 		enum ast_transfer_result result)
 {
 	ast_bridge_publish_attended_transfer_fail(1, result, &publication->to_transferee,
-			&publication->to_transfer_target);
+			&publication->to_transfer_target, publication->transferee_channel,
+			publication->target_channel);
 }
 
 /*!
@@ -3987,9 +4078,12 @@
 		return AST_BRIDGE_TRANSFER_FAIL;
 	}
 
+	/* Get a ref for use later since this one is being stolen */
+	ao2_ref(local_chan, +1);
 	if (ast_bridge_impart(bridge1, local_chan, chan1, NULL,
 		AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
 		ast_hangup(local_chan);
+		ao2_cleanup(local_chan);
 		return AST_BRIDGE_TRANSFER_FAIL;
 	}
 
@@ -4005,40 +4099,12 @@
 		publish_attended_transfer_link(publication,
 				local_chan, local_chan2);
 	} else {
+		publication->replace_channel = ao2_bump(local_chan);
 		publish_attended_transfer_app(publication, app);
 	}
+
+	ao2_cleanup(local_chan);
 	return AST_BRIDGE_TRANSFER_SUCCESS;
-}
-
-/*!
- * \internal
- * \brief Get the transferee channel
- *
- * This is only applicable to cases where a transfer is occurring on a
- * two-party bridge. The channels container passed in is expected to only
- * contain two channels, the transferer and the transferee. The transferer
- * channel is passed in as a parameter to ensure we don't return it as
- * the transferee channel.
- *
- * \param channels A two-channel container containing the transferer and transferee
- * \param transferer The party that is transfering the call
- * \return The party that is being transferred
- */
-static struct ast_channel *get_transferee(struct ao2_container *channels, struct ast_channel *transferer)
-{
-	struct ao2_iterator channel_iter;
-	struct ast_channel *transferee;
-
-	for (channel_iter = ao2_iterator_init(channels, 0);
-			(transferee = ao2_iterator_next(&channel_iter));
-			ao2_cleanup(transferee)) {
-		if (transferee != transferer) {
-			break;
-		}
-	}
-
-	ao2_iterator_destroy(&channel_iter);
-	return transferee;
 }
 
 static enum ast_transfer_result try_parking(struct ast_channel *transferer,
@@ -4142,7 +4208,7 @@
 
 static void publish_blind_transfer(int is_external, enum ast_transfer_result result,
 		struct ast_channel *transferer, struct ast_bridge *bridge,
-		const char *context, const char *exten)
+		const char *context, const char *exten, struct ast_channel *transferee_channel)
 {
 	struct ast_bridge_channel_pair pair;
 	pair.channel = transferer;
@@ -4150,7 +4216,7 @@
 	if (bridge) {
 		ast_bridge_lock(bridge);
 	}
-	ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten);
+	ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten, transferee_channel);
 	if (bridge) {
 		ast_bridge_unlock(bridge);
 	}
@@ -4174,6 +4240,9 @@
 		transfer_result = AST_BRIDGE_TRANSFER_INVALID;
 		goto publish;
 	}
+
+	transferee = ast_bridge_peer(bridge, transferer);
+
 	ast_channel_lock(transferer);
 	bridge_channel = ast_channel_get_bridge_channel(transferer);
 	ast_channel_unlock(transferer);
@@ -4235,7 +4304,6 @@
 
 	/* Reaching this portion means that we're dealing with a two-party bridge */
 
-	transferee = get_transferee(channels, transferer);
 	if (!transferee) {
 		transfer_result = AST_BRIDGE_TRANSFER_FAIL;
 		goto publish;
@@ -4251,7 +4319,7 @@
 	transfer_result = AST_BRIDGE_TRANSFER_SUCCESS;
 
 publish:
-	publish_blind_transfer(is_external, transfer_result, transferer, bridge, context, exten);
+	publish_blind_transfer(is_external, transfer_result, transferer, bridge, context, exten, transferee);
 	return transfer_result;
 }
 

Modified: trunk/main/bridge_basic.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/bridge_basic.c?view=diff&rev=420338&r1=420337&r2=420338
==============================================================================
--- trunk/main/bridge_basic.c (original)
+++ trunk/main/bridge_basic.c Thu Aug  7 10:30:19 2014
@@ -1526,9 +1526,85 @@
 }
 
 /*!
+ * \brief Get a desired transfer party for a bridge the transferer is not in.
+ *
+ * \param bridge The bridge to get the party from. May be NULL.
+ * \param[out] party The lone channel in the bridge. Will be set NULL if bridge is NULL or multiple parties are present.
+ */
+static void get_transfer_party_non_transferer_bridge(struct ast_bridge *bridge,
+		struct ast_channel **party)
+{
+	if (bridge && bridge->num_channels == 1) {
+		*party = ast_channel_ref(AST_LIST_FIRST(&bridge->channels)->chan);
+	} else {
+		*party = NULL;
+	}
+}
+
+/*!
+ * \brief Get the transferee and transfer target when the transferer is in a bridge with
+ * one of the desired parties.
+ *
+ * \param transferer_bridge The bridge the transferer is in
+ * \param other_bridge The bridge the transferer is not in. May be NULL.
+ * \param transferer The transferer party
+ * \param[out] transferer_peer The party that is in the bridge with the transferer
+ * \param[out] other_party The party that is in the other_bridge
+ */
+static void get_transfer_parties_transferer_bridge(struct ast_bridge *transferer_bridge,
+		struct ast_bridge *other_bridge, struct ast_channel *transferer,
+		struct ast_channel **transferer_peer, struct ast_channel **other_party)
+{
+	*transferer_peer = ast_bridge_peer(transferer_bridge, transferer);
+	get_transfer_party_non_transferer_bridge(other_bridge, other_party);
+}
+
+/*!
+ * \brief determine transferee and transfer target for an attended transfer
+ *
+ * In builtin attended transfers, there is a single transferer channel that jumps between
+ * the two bridges involved. At the time the attended transfer occurs, the transferer could
+ * be in either bridge, so determining the parties is a bit more complex than normal.
+ *
+ * The method used here is to determine which of the two bridges the transferer is in, and
+ * grabbing the peer from that bridge. The other bridge, if it only has a single channel in it,
+ * has the other desired channel.
+ *
+ * \param transferer The channel performing the transfer
+ * \param transferee_bridge The bridge that the transferee is in
+ * \param target_bridge The bridge that the transfer target is in
+ * \param[out] transferee The transferee channel
+ * \param[out] transfer_target The transfer target channel
+ */
+static void get_transfer_parties(struct ast_channel *transferer, struct ast_bridge *transferee_bridge,
+		struct ast_bridge *target_bridge, struct ast_channel **transferee,
+		struct ast_channel **transfer_target)
+{
+	struct ast_bridge *transferer_bridge;
+
+	ast_channel_lock(transferer);
+	transferer_bridge = ast_channel_get_bridge(transferer);
+	ast_channel_unlock(transferer);
+
+	if (transferer_bridge == transferee_bridge) {
+		get_transfer_parties_transferer_bridge(transferee_bridge, target_bridge,
+				transferer, transferee, transfer_target);
+	} else if (transferer_bridge == target_bridge) {
+		get_transfer_parties_transferer_bridge(target_bridge, transferee_bridge,
+				transferer, transfer_target, transferee);
+	} else {
+		get_transfer_party_non_transferer_bridge(transferee_bridge, transferee);
+		get_transfer_party_non_transferer_bridge(target_bridge, transfer_target);
+	}
+
+	ao2_cleanup(transferer_bridge);
+}
+
+/*!
  * \brief Send a stasis publication for a successful attended transfer
  */
-static void publish_transfer_success(struct attended_transfer_properties *props)
+static void publish_transfer_success(struct attended_transfer_properties *props,
+		struct ast_channel *transferee_channel, struct ast_channel *target_channel)
 {
 	struct ast_bridge_channel_pair transferee = {
 		.channel = props->transferer,
@@ -1548,7 +1624,8 @@
 	}
 
 	ast_bridge_publish_attended_transfer_bridge_merge(0, AST_BRIDGE_TRANSFER_SUCCESS,
-			&transferee, &transfer_target, props->transferee_bridge);
+			&transferee, &transfer_target, props->transferee_bridge, transferee_channel,
+			target_channel);
 
 	if (transferee.bridge) {
 		ast_bridge_unlock(transferee.bridge);
@@ -1561,7 +1638,8 @@
 /*!
  * \brief Send a stasis publication for an attended transfer that ends in a threeway call
  */
-static void publish_transfer_threeway(struct attended_transfer_properties *props)
+static void publish_transfer_threeway(struct attended_transfer_properties *props,
+		struct ast_channel *transferee_channel, struct ast_channel *target_channel)
 {
 	struct ast_bridge_channel_pair transferee = {
 		.channel = props->transferer,
@@ -1585,7 +1663,8 @@
 	}
 
 	ast_bridge_publish_attended_transfer_threeway(0, AST_BRIDGE_TRANSFER_SUCCESS,
-			&transferee, &transfer_target, &threeway);
+			&transferee, &transfer_target, &threeway, transferee_channel,
+			target_channel);
 
 	if (transferee.bridge) {
 		ast_bridge_unlock(transferee.bridge);
@@ -1608,6 +1687,8 @@
 		.channel = props->transferer,
 		.bridge = props->target_bridge,
 	};
+	struct ast_channel *transferee_channel;
+	struct ast_channel *target_channel;
 
 	if (transferee.bridge && transfer_target.bridge) {
 		ast_bridge_lock_both(transferee.bridge, transfer_target.bridge);
@@ -1617,8 +1698,12 @@
 		ast_bridge_lock(transfer_target.bridge);
 	}
 
+	get_transfer_parties(props->transferer, props->transferee_bridge, props->target_bridge,
+			&transferee_channel, &target_channel);
 	ast_bridge_publish_attended_transfer_fail(0, AST_BRIDGE_TRANSFER_FAIL,
-			&transferee, &transfer_target);
+			&transferee, &transfer_target, transferee_channel, target_channel);
+	ast_channel_cleanup(transferee_channel);
+	ast_channel_cleanup(target_channel);
 
 	if (transferee.bridge) {
 		ast_bridge_unlock(transferee.bridge);
@@ -2072,11 +2157,18 @@
 
 static int threeway_enter(struct attended_transfer_properties *props)
 {
+	struct ast_channel *transferee_channel;
+	struct ast_channel *target_channel;
+
+	get_transfer_parties(props->transferer, props->transferee_bridge, props->target_bridge,
+			&transferee_channel, &target_channel);
 	bridge_merge(props->transferee_bridge, props->target_bridge, NULL, 0);
 	play_sound(props->transfer_target, props->xfersound);
 	play_sound(props->transferer, props->xfersound);
-	publish_transfer_threeway(props);
-
+	publish_transfer_threeway(props, transferee_channel, target_channel);
+
+	ast_channel_cleanup(transferee_channel);
+	ast_channel_cleanup(target_channel);
 	return 0;
 }
 
@@ -2178,17 +2270,33 @@
 
 static int complete_enter(struct attended_transfer_properties *props)
 {
+	struct ast_channel *transferee_channel;
+	struct ast_channel *target_channel;
+
+	get_transfer_parties(props->transferer, props->transferee_bridge, props->target_bridge,
+			&transferee_channel, &target_channel);
 	bridge_merge(props->transferee_bridge, props->target_bridge, &props->transferer, 1);
 	play_sound(props->transfer_target, props->xfersound);
-	publish_transfer_success(props);
+	publish_transfer_success(props, transferee_channel, target_channel);
+
+	ast_channel_cleanup(transferee_channel);
+	ast_channel_cleanup(target_channel);
 	return 0;
 }
 
 static int blond_enter(struct attended_transfer_properties *props)
 {
+	struct ast_channel *transferee_channel;
+	struct ast_channel *target_channel;
+
+	get_transfer_parties(props->transferer, props->transferee_bridge, props->target_bridge,
+			&transferee_channel, &target_channel);
 	bridge_merge(props->transferee_bridge, props->target_bridge, &props->transferer, 1);
 	ringing(props->transfer_target);
-	publish_transfer_success(props);
+	publish_transfer_success(props, transferee_channel, target_channel);
+
+	ast_channel_cleanup(transferee_channel);
+	ast_channel_cleanup(target_channel);
 	return 0;
 }
 

Modified: trunk/main/cel.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/cel.c?view=diff&rev=420338&r1=420337&r2=420338
==============================================================================
--- trunk/main/cel.c (original)
+++ trunk/main/cel.c Thu Aug  7 10:30:19 2014
@@ -1358,44 +1358,20 @@
 	void *data, struct stasis_subscription *sub,
 	struct stasis_message *message)
 {
-	struct ast_bridge_blob *obj = stasis_message_data(message);
-	struct ast_channel_snapshot *chan_snapshot = obj->channel;
-	struct ast_bridge_snapshot *bridge_snapshot = obj->bridge;
-	struct ast_json *blob = obj->blob;
-	struct ast_json *json_result = ast_json_object_get(blob, "result");
-	struct ast_json *json_exten;
-	struct ast_json *json_context;
+	struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
+	struct ast_channel_snapshot *chan_snapshot = transfer_msg->to_transferee.channel_snapshot;
+	struct ast_bridge_snapshot *bridge_snapshot = transfer_msg->to_transferee.bridge_snapshot;
 	struct ast_json *extra;
-	const char *exten;
-	const char *context;
-	enum ast_transfer_result result;
-
-	if (!json_result) {
-		return;
-	}
-
-	result = ast_json_integer_get(json_result);
-	if (result != AST_BRIDGE_TRANSFER_SUCCESS) {
-		return;
-	}
-
-	json_exten = ast_json_object_get(blob, "exten");
-	json_context = ast_json_object_get(blob, "context");
-
-	if (!json_exten || !json_context) {
-		return;
-	}
-
-	exten = ast_json_string_get(json_exten);
-	context = ast_json_string_get(json_context);
-	if (!exten || !context) {
+
+	if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
 		return;
 	}
 
 	extra = ast_json_pack("{s: s, s: s, s: s}",
-		"extension", exten,
-		"context", context,
-		"bridge_id", bridge_snapshot->uniqueid);
+		"extension", transfer_msg->exten,
+		"context", transfer_msg->context,
+		"bridge_id", bridge_snapshot->uniqueid,
+		"transferee_channel_name", transfer_msg->transferee ? transfer_msg->transferee->name: "N/A");
 	if (extra) {
 		cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, NULL, extra, NULL);
 		ast_json_unref(extra);
@@ -1431,19 +1407,24 @@
 	case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE:
 	case AST_ATTENDED_TRANSFER_DEST_LINK:
 	case AST_ATTENDED_TRANSFER_DEST_THREEWAY:
-		extra = ast_json_pack("{s: s, s: s, s: s}",
+		extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
 			"bridge1_id", bridge1->uniqueid,
 			"channel2_name", channel2->name,
-			"bridge2_id", bridge2->uniqueid);
+			"bridge2_id", bridge2->uniqueid,
+			"transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
+			"transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A");
 		if (!extra) {
 			return;
 		}
 		break;
 	case AST_ATTENDED_TRANSFER_DEST_APP:
-		extra = ast_json_pack("{s: s, s: s, s: s}",
+	case AST_ATTENDED_TRANSFER_DEST_LOCAL_APP:
+		extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}",
 			"bridge1_id", bridge1->uniqueid,
 			"channel2_name", channel2->name,
-			"app", xfer->dest.app);
+			"app", xfer->dest.app,
+			"transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A",
+			"transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A");
 		if (!extra) {
 			return;
 		}

Modified: trunk/main/channel.c

[... 1855 lines stripped ...]



More information about the svn-commits mailing list