[Asterisk-code-review] app_confbridge: Attended transfer event fixup (...asterisk[master])

Friendly Automation asteriskteam at digium.com
Fri Jun 21 11:24:36 CDT 2019


Friendly Automation has submitted this change and it was merged. ( https://gerrit.asterisk.org/c/asterisk/+/11453 )

Change subject: app_confbridge:  Attended transfer event fixup
......................................................................

app_confbridge:  Attended transfer event fixup

When a channel already in a conference bridge is attended transfered
to another extension, or when an existing call is attended
transferred into a conference bridge, we now generate ConfbridgeJoin
and ConfbridgeLeave events for the entering and departing channels.

Change-Id: Id7709cfbceb26fbcb828b2d0d2a6b2fbeaf028e1
---
M apps/app_confbridge.c
M apps/confbridge/confbridge_manager.c
M apps/confbridge/include/confbridge.h
M include/asterisk/stasis_bridges.h
M main/stasis_bridges.c
5 files changed, 254 insertions(+), 0 deletions(-)

Approvals:
  Kevin Harwell: Looks good to me, but someone else must approve
  Joshua Colp: Looks good to me, but someone else must approve
  George Joseph: Looks good to me, approved
  Friendly Automation: Approved for Submit



diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index 6d141bd..7fe727e 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -587,6 +587,43 @@
 	}
 }
 
+static void send_conf_stasis_snapshots(struct confbridge_conference *conference,
+	struct ast_channel_snapshot *chan_snapshot, struct stasis_message_type *type,
+	struct ast_json *extras)
+{
+	RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
+	RAII_VAR(struct ast_bridge_snapshot *, bridge_snapshot, NULL, ao2_cleanup);
+
+	json_object = ast_json_pack("{s: s}",
+		"conference", conference->name);
+	if (!json_object) {
+		return;
+	}
+
+	if (extras) {
+		ast_json_object_update(json_object, extras);
+	}
+
+	ast_bridge_lock(conference->bridge);
+	bridge_snapshot = ast_bridge_snapshot_create(conference->bridge);
+	ast_bridge_unlock(conference->bridge);
+	if (!bridge_snapshot) {
+		return;
+	}
+
+	msg = ast_bridge_blob_create_from_snapshots(type,
+		bridge_snapshot,
+		chan_snapshot,
+		json_object);
+	if (!msg) {
+		return;
+	}
+
+	stasis_publish(ast_bridge_topic(conference->bridge), msg);
+}
+
+
 static void send_conf_start_event(struct confbridge_conference *conference)
 {
 	send_conf_stasis(conference, NULL, confbridge_start_type(), NULL, 0);
@@ -1479,6 +1516,126 @@
 	return 0;
 }
 
+static void confbridge_unlock_and_unref(void *obj)
+{
+	struct confbridge_conference *conference = obj;
+
+	if (!obj) {
+		return;
+	}
+	ao2_unlock(conference);
+	ao2_ref(conference, -1);
+}
+
+void confbridge_handle_atxfer(struct ast_attended_transfer_message *msg)
+{
+	struct ast_channel_snapshot *old_snapshot;
+	struct ast_channel_snapshot *new_snapshot;
+	char *confbr_name = NULL;
+	char *comma;
+	RAII_VAR(struct confbridge_conference *, conference, NULL, confbridge_unlock_and_unref);
+	struct confbridge_user *user = NULL;
+	int found_user = 0;
+	struct ast_json *json_object;
+
+	if (msg->to_transferee.channel_snapshot
+		&& strcmp(msg->to_transferee.channel_snapshot->dialplan->appl, "ConfBridge") == 0
+		&& msg->target) {
+		/* We're transferring a bridge to an extension */
+		old_snapshot = msg->to_transferee.channel_snapshot;
+		new_snapshot = msg->target;
+	} else if (msg->to_transfer_target.channel_snapshot
+		&& strcmp(msg->to_transfer_target.channel_snapshot->dialplan->appl, "ConfBridge") == 0
+		&& msg->transferee) {
+		/* We're transferring a call to a bridge */
+		old_snapshot = msg->to_transfer_target.channel_snapshot;
+		new_snapshot = msg->transferee;
+	} else {
+		ast_log(LOG_ERROR, "Could not determine proper channels\n");
+		return;
+	}
+
+	/*
+	 * old_snapshot->data should have the original parameters passed to
+	 * the ConfBridge app:
+	 * conference[,bridge_profile[,user_profile[,menu]]]
+	 * We'll use "conference" to look up the bridge.
+	 *
+	 * We _could_ use old_snapshot->bridgeid to get the bridge but
+	 * that would involve locking the conference_bridges container
+	 * and iterating over it looking for a matching bridge.
+	 */
+	if (ast_strlen_zero(old_snapshot->dialplan->data)) {
+		ast_log(LOG_ERROR, "Channel '%s' didn't have app data set\n", old_snapshot->base->name);
+		return;
+	}
+	confbr_name = ast_strdupa(old_snapshot->dialplan->data);
+	comma = strchr(confbr_name, ',');
+	if (comma) {
+		*comma = '\0';
+	}
+
+	ast_debug(1, "Confbr: %s  Leaving: %s  Joining: %s\n", confbr_name, old_snapshot->base->name, new_snapshot->base->name);
+
+	conference = ao2_find(conference_bridges, confbr_name, OBJ_SEARCH_KEY);
+	if (!conference) {
+		ast_log(LOG_ERROR, "Conference bridge '%s' not found\n", confbr_name);
+		return;
+	}
+	ao2_lock(conference);
+
+	/*
+	 * We need to grab the user profile for the departing user in order to
+	 * properly format the join/leave messages.
+	 */
+	AST_LIST_TRAVERSE(&conference->active_list, user, list) {
+		if (strcasecmp(ast_channel_name(user->chan), old_snapshot->base->name) == 0) {
+			found_user = 1;
+			break;
+		}
+	}
+
+	/*
+	 * If we didn't find the user in the active list, try the waiting list.
+	 */
+	if (!found_user && conference->waitingusers) {
+		AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
+			if (strcasecmp(ast_channel_name(user->chan), old_snapshot->base->name) == 0) {
+				found_user = 1;
+				break;
+			}
+		}
+	}
+
+	if (!found_user) {
+		ast_log(LOG_ERROR, "Unable to find user profile for channel '%s' in bridge '%s'\n",
+			old_snapshot->base->name, confbr_name);
+		return;
+	}
+
+	/*
+	 * We're going to use the existing user profile to create the messages.
+	 */
+	json_object = ast_json_pack("{s: b}",
+		"admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
+	);
+	if (!json_object) {
+		return;
+	}
+
+	send_conf_stasis_snapshots(conference, old_snapshot, confbridge_leave_type(), json_object);
+	ast_json_unref(json_object);
+
+	json_object = ast_json_pack("{s: b, s: b}",
+		"admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN),
+		"muted", user->muted);
+	if (!json_object) {
+		return;
+	}
+	send_conf_stasis_snapshots(conference, new_snapshot, confbridge_join_type(), json_object);
+	ast_json_unref(json_object);
+}
+
 /*!
  * \brief Join a conference bridge
  *
diff --git a/apps/confbridge/confbridge_manager.c b/apps/confbridge/confbridge_manager.c
index b1819ca..06cb433 100644
--- a/apps/confbridge/confbridge_manager.c
+++ b/apps/confbridge/confbridge_manager.c
@@ -621,6 +621,26 @@
 	ast_free(extra_text);
 }
 
+static void confbridge_atxfer_cb(void *data, struct stasis_subscription *sub,
+	struct stasis_message *message)
+{
+	struct ast_attended_transfer_message *msg = stasis_message_data(message);
+
+	if (msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
+		return;
+	}
+
+	/*
+	 * This callback will get called for ALL attended transfers
+	 * so we need to make sure this transfer belongs to
+	 * a conference bridge before trying to handle it.
+	 */
+	if (msg->dest_type == AST_ATTENDED_TRANSFER_DEST_APP
+		&& strcmp(msg->dest.app, "ConfBridge") == 0) {
+		confbridge_handle_atxfer(msg);
+	}
+}
+
 static void confbridge_start_record_cb(void *data, struct stasis_subscription *sub,
 	struct stasis_message *message)
 {
@@ -740,6 +760,13 @@
 		return -1;
 	}
 	if (stasis_message_router_add(bridge_state_router,
+			ast_attended_transfer_type(),
+			confbridge_atxfer_cb,
+			NULL)) {
+		manager_confbridge_shutdown();
+		return -1;
+	}
+	if (stasis_message_router_add(bridge_state_router,
 			confbridge_leave_type(),
 			confbridge_leave_cb,
 			NULL)) {
diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h
index 237431e..f932da8 100644
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -28,6 +28,7 @@
 #include "asterisk/channel.h"
 #include "asterisk/bridge.h"
 #include "asterisk/bridge_features.h"
+#include "asterisk/stasis_bridges.h"
 #include "conf_state.h"
 
 /*! Maximum length of a conference bridge name */
@@ -714,4 +715,14 @@
 void conf_send_event_to_participants(struct confbridge_conference *conference,
 	struct ast_channel *chan, struct stasis_message *msg);
 
+/*!
+ * \brief Create join/leave events for attended transfers
+ * \since 13.28
+ * \since 16.5
+ *
+ * \param msg The attended transfer stasis message
+ *
+ */
+void confbridge_handle_atxfer(struct ast_attended_transfer_message *msg);
+
 #endif
diff --git a/include/asterisk/stasis_bridges.h b/include/asterisk/stasis_bridges.h
index 4d80955..3d6d2b2 100644
--- a/include/asterisk/stasis_bridges.h
+++ b/include/asterisk/stasis_bridges.h
@@ -167,6 +167,29 @@
 	struct ast_json *blob);
 
 /*!
+ * \since 13.28
+ * \since 16.5
+ * \brief Creates a \ref ast_bridge_blob message from snapshots.
+ *
+ * The \a blob JSON object requires a \c "type" field describing the blob. It
+ * should also be treated as immutable and not modified after it is put into the
+ * message.
+ *
+ * \pre bridge is locked.
+ * \pre No channels are locked.
+ *
+ * \param bridge_snapshot Bridge snapshot
+ * \param channel_snapshot Channel snapshot
+ * \param blob JSON object representing the data.
+ * \return \ref ast_bridge_blob message.
+ * \return \c NULL on error
+ */
+struct stasis_message *ast_bridge_blob_create_from_snapshots(struct stasis_message_type *type,
+	struct ast_bridge_snapshot *bridge_snapshot,
+	struct ast_channel_snapshot *chan_snapshot,
+	struct ast_json *blob);
+
+/*!
  * \since 12
  * \brief Publish a bridge channel enter event
  *
diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c
index 31d3eac..9238adc 100644
--- a/main/stasis_bridges.c
+++ b/main/stasis_bridges.c
@@ -543,6 +543,42 @@
 	return msg;
 }
 
+struct stasis_message *ast_bridge_blob_create_from_snapshots(
+	struct stasis_message_type *message_type,
+	struct ast_bridge_snapshot *bridge_snapshot,
+	struct ast_channel_snapshot *chan_snapshot,
+	struct ast_json *blob)
+{
+	struct ast_bridge_blob *obj;
+	struct stasis_message *msg;
+
+	if (!message_type) {
+		return NULL;
+	}
+
+	obj = ao2_alloc(sizeof(*obj), bridge_blob_dtor);
+	if (!obj) {
+		return NULL;
+	}
+
+	if (bridge_snapshot) {
+		obj->bridge = ao2_bump(bridge_snapshot);
+	}
+
+	if (chan_snapshot) {
+		obj->channel = ao2_bump(chan_snapshot);
+	}
+
+	if (blob) {
+		obj->blob = ast_json_ref(blob);
+	}
+
+	msg = stasis_message_create(message_type, obj);
+	ao2_ref(obj, -1);
+
+	return msg;
+}
+
 void ast_bridge_publish_enter(struct ast_bridge *bridge, struct ast_channel *chan,
 		struct ast_channel *swap)
 {

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/11453
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Change-Id: Id7709cfbceb26fbcb828b2d0d2a6b2fbeaf028e1
Gerrit-Change-Number: 11453
Gerrit-PatchSet: 5
Gerrit-Owner: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Friendly Automation
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20190621/27563b47/attachment-0001.html>


More information about the asterisk-code-review mailing list