]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
app_confbridge: Attended transfer event fixup
authorGeorge Joseph <gjoseph@digium.com>
Mon, 10 Jun 2019 21:58:59 +0000 (15:58 -0600)
committerGeorge Joseph <gjoseph@digium.com>
Thu, 13 Jun 2019 20:05:12 +0000 (14:05 -0600)
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

apps/app_confbridge.c
apps/confbridge/confbridge_manager.c
apps/confbridge/include/confbridge.h
include/asterisk/stasis_bridges.h
main/cdr.c
main/stasis_bridges.c

index c44375bf89df47b8832379a49df3cf2e413b19fc..21a493c3be6d4c096823d883f212075d67467bd7 100644 (file)
@@ -578,6 +578,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);
@@ -1462,6 +1499,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->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->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->data)) {
+               ast_log(LOG_ERROR, "Channel '%s' didn't have app data set\n", old_snapshot->name);
+               return;
+       }
+       confbr_name = ast_strdupa(old_snapshot->data);
+       comma = strchr(confbr_name, ',');
+       if (comma) {
+               *comma = '\0';
+       }
+
+       ast_debug(1, "Confbr: %s  Leaving: %s  Joining: %s\n", confbr_name, old_snapshot->name, new_snapshot->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->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->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->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
  *
index 9ac0d55a434a071e82d205000e4f470bb53ee64b..c6ae0af90ff1995de1be1d5843df665ae46ba3db 100644 (file)
@@ -325,6 +325,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)
 {
@@ -451,6 +471,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,
index f87a57bed9096c13413454ed9066176a4c5c5543..a29b363ca7823261d3008dddfb95ff0658cf898d 100644 (file)
@@ -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 */
@@ -649,4 +650,15 @@ struct ast_channel_tech *conf_announce_get_tech(void);
  * \retval -1 on error.
  */
 int conf_announce_channel_push(struct ast_channel *ast);
+
+/*!
+ * \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
index e2e49c5b70b3571327a00567cc304cd243d3b6b5..c7c8b96e2f0fe20d6facc65c60899c471136ee4b 100644 (file)
@@ -228,6 +228,29 @@ struct stasis_message *ast_bridge_blob_create(struct stasis_message_type *type,
        struct ast_channel *chan,
        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
index 7b6fac04a20fccfb53db144b1d0330f31f381873..1d43c11ce62aa1955c5012bd0fda82ecbda58dae 100644 (file)
@@ -1604,14 +1604,12 @@ static int base_process_party_a(struct cdr_object *cdr, struct ast_channel_snaps
 static int base_process_bridge_leave(struct cdr_object *cdr, struct ast_bridge_snapshot *bridge, struct ast_channel_snapshot *channel)
 {
        /* In general, most things shouldn't get a bridge leave */
-       ast_assert(0);
        return 1;
 }
 
 static int base_process_dial_end(struct cdr_object *cdr, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const char *dial_status)
 {
        /* In general, most things shouldn't get a dial end. */
-       ast_assert(0);
        return 0;
 }
 
index 42c8d71757b0496b752a80663c7ee077101f06f8..fe022d66b6758b276c4d2389359d5228a64262a8 100644 (file)
@@ -482,6 +482,42 @@ struct stasis_message *ast_bridge_blob_create(
        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)
 {