mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-03 20:38:59 +00:00 
			
		
		
		
	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
This commit is contained in:
		@@ -587,6 +587,43 @@ static void send_conf_stasis(struct confbridge_conference *conference, struct as
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 @@ static int push_announcer(struct confbridge_conference *conference)
 | 
			
		||||
	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
 | 
			
		||||
 *
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user