mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-05 12:16:00 +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)
|
static void send_conf_start_event(struct confbridge_conference *conference)
|
||||||
{
|
{
|
||||||
send_conf_stasis(conference, NULL, confbridge_start_type(), NULL, 0);
|
send_conf_stasis(conference, NULL, confbridge_start_type(), NULL, 0);
|
||||||
@@ -1479,6 +1516,126 @@ static int push_announcer(struct confbridge_conference *conference)
|
|||||||
return 0;
|
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
|
* \brief Join a conference bridge
|
||||||
*
|
*
|
||||||
|
@@ -621,6 +621,26 @@ static void confbridge_join_cb(void *data, struct stasis_subscription *sub,
|
|||||||
ast_free(extra_text);
|
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,
|
static void confbridge_start_record_cb(void *data, struct stasis_subscription *sub,
|
||||||
struct stasis_message *message)
|
struct stasis_message *message)
|
||||||
{
|
{
|
||||||
@@ -739,6 +759,13 @@ int manager_confbridge_init(void)
|
|||||||
manager_confbridge_shutdown();
|
manager_confbridge_shutdown();
|
||||||
return -1;
|
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,
|
if (stasis_message_router_add(bridge_state_router,
|
||||||
confbridge_leave_type(),
|
confbridge_leave_type(),
|
||||||
confbridge_leave_cb,
|
confbridge_leave_cb,
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
#include "asterisk/channel.h"
|
#include "asterisk/channel.h"
|
||||||
#include "asterisk/bridge.h"
|
#include "asterisk/bridge.h"
|
||||||
#include "asterisk/bridge_features.h"
|
#include "asterisk/bridge_features.h"
|
||||||
|
#include "asterisk/stasis_bridges.h"
|
||||||
#include "conf_state.h"
|
#include "conf_state.h"
|
||||||
|
|
||||||
/*! Maximum length of a conference bridge name */
|
/*! 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,
|
void conf_send_event_to_participants(struct confbridge_conference *conference,
|
||||||
struct ast_channel *chan, struct stasis_message *msg);
|
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
|
#endif
|
||||||
|
@@ -166,6 +166,29 @@ struct stasis_message *ast_bridge_blob_create(struct stasis_message_type *type,
|
|||||||
struct ast_channel *chan,
|
struct ast_channel *chan,
|
||||||
struct ast_json *blob);
|
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
|
* \since 12
|
||||||
* \brief Publish a bridge channel enter event
|
* \brief Publish a bridge channel enter event
|
||||||
|
@@ -543,6 +543,42 @@ struct stasis_message *ast_bridge_blob_create(
|
|||||||
return msg;
|
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,
|
void ast_bridge_publish_enter(struct ast_bridge *bridge, struct ast_channel *chan,
|
||||||
struct ast_channel *swap)
|
struct ast_channel *swap)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user