mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-03 11:25:35 +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
|
||||
*
|
||||
|
@@ -621,6 +621,26 @@ static void confbridge_join_cb(void *data, struct stasis_subscription *sub,
|
||||
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)
|
||||
{
|
||||
@@ -739,6 +759,13 @@ int manager_confbridge_init(void)
|
||||
manager_confbridge_shutdown();
|
||||
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,
|
||||
|
@@ -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 @@ struct confbridge_conference *conf_find_bridge(const char *conference_name);
|
||||
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
|
||||
|
Reference in New Issue
Block a user