Stasis: Add information to blind transfer event
authorKinsey Moore <kmoore@digium.com>
Wed, 20 Aug 2014 13:06:33 +0000 (13:06 +0000)
committerKinsey Moore <kmoore@digium.com>
Wed, 20 Aug 2014 13:06:33 +0000 (13:06 +0000)
When a blind transfer occurs that is forced to create a local channel
pair to satisfy the transfer request, information about the local
channel pair is not published. This adds a field to describe that
channel to the blind transfer message struct so that this information
is conveyed properly to consumers of the blind transfer message.

This also fixes a bug in which Stasis() was unable to properly identify
the channel that was replacing an existing Stasis-controlled channel
due to a blind transfer.

Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/3921/
........

Merged revisions 421537 from http://svn.asterisk.org/svn/asterisk/branches/12
........

Merged revisions 421538 from http://svn.asterisk.org/svn/asterisk/branches/13

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@421539 65c4cc65-6c06-0410-ace0-fbb531ad65f3

include/asterisk/stasis_bridges.h
main/bridge.c
main/stasis_bridges.c
res/ari/ari_model_validators.c
res/ari/ari_model_validators.h
res/stasis/app.c
rest-api/api-docs/events.json
tests/test_cel.c

index a4e46cd..699e276 100644 (file)
@@ -300,6 +300,8 @@ struct ast_blind_transfer_message {
        char exten[AST_MAX_EXTENSION];
        /*! Transferee channel. NULL if there were multiple transferee channels */
        struct ast_channel_snapshot *transferee;
+       /*! The channel replacing the transferer when multiple parties are being transferred */
+       struct ast_channel_snapshot *replace_channel;
 };
 
 /*!
@@ -312,11 +314,15 @@ struct ast_blind_transfer_message {
  * \param to_transferee The bridge between the transferer and transferee plus the transferer channel
  * \param context The destination context for the blind transfer
  * \param exten The destination extension for the blind transfer
- * \param transferee_channel If a single channel is being transferred, this is it. If multiple parties are being transferred, this is NULL.
+ * \param transferee_channel If a single channel is being transferred, this is it. If
+ *                           multiple parties are being transferred, this is NULL.
+ * \param replace_channel If multiple parties are being transferred or the transfer
+ *                        cannot reach across the bridge due to bridge flags, this is
+ *                        the channel connecting their bridge to the destination.
  */
 void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result result,
                struct ast_bridge_channel_pair *to_transferee, const char *context, const char *exten,
-               struct ast_channel *transferee_channel);
+               struct ast_channel *transferee_channel, struct ast_channel *replace_channel);
 
 enum ast_attended_transfer_dest_type {
        /*! The transfer failed, so there is no appropriate final state */
index 66c495a..a3ca8a9 100644 (file)
@@ -3805,6 +3805,26 @@ struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channe
        return peer;
 }
 
+static void publish_blind_transfer_full(int is_external, enum ast_transfer_result result,
+               struct ast_channel *transferer, struct ast_bridge *bridge,
+               const char *context, const char *exten, struct ast_channel *transferee_channel,
+               struct ast_channel *replace_channel)
+{
+       struct ast_bridge_channel_pair pair;
+
+       pair.channel = transferer;
+       pair.bridge = bridge;
+
+       if (bridge) {
+               ast_bridge_lock(bridge);
+       }
+       ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten,
+               transferee_channel, replace_channel);
+       if (bridge) {
+               ast_bridge_unlock(bridge);
+       }
+}
+
 /*!
  * \internal
  * \brief Transfer an entire bridge to a specific destination.
@@ -3817,16 +3837,21 @@ struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channe
  * bridges, this method is only used for multi-party bridges since this method would
  * be less efficient for two-party bridges.
  *
+ * \param is_external Whether the transfer is externally initiated
  * \param transferer The channel performing a transfer
  * \param bridge The bridge where the transfer is being performed
  * \param exten The destination extension for the blind transfer
+ * \param transferee The party being transferred if there is only one
  * \param context The destination context for the blind transfer
  * \param hook Framehook to attach to local channel
+ *
  * \return The success or failure of the operation
  */
-static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transferer,
-               struct ast_bridge *bridge, const char *exten, const char *context,
-               transfer_channel_cb new_channel_cb, struct transfer_channel_data *user_data_wrapper)
+static enum ast_transfer_result blind_transfer_bridge(int is_external,
+               struct ast_channel *transferer, struct ast_bridge *bridge,
+               const char *exten, const char *context, struct ast_channel *transferee,
+               transfer_channel_cb new_channel_cb,
+               struct transfer_channel_data *user_data_wrapper)
 {
        struct ast_channel *local;
        char chan_name[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2];
@@ -3858,6 +3883,8 @@ static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transf
                ast_hangup(local);
                return AST_BRIDGE_TRANSFER_FAIL;
        }
+       publish_blind_transfer_full(is_external, AST_BRIDGE_TRANSFER_SUCCESS, transferer, bridge,
+               context, exten, transferee, local);
        return AST_BRIDGE_TRANSFER_SUCCESS;
 }
 
@@ -4214,16 +4241,8 @@ static void publish_blind_transfer(int is_external, enum ast_transfer_result res
                struct ast_channel *transferer, struct ast_bridge *bridge,
                const char *context, const char *exten, struct ast_channel *transferee_channel)
 {
-       struct ast_bridge_channel_pair pair;
-       pair.channel = transferer;
-       pair.bridge = bridge;
-       if (bridge) {
-               ast_bridge_lock(bridge);
-       }
-       ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten, transferee_channel);
-       if (bridge) {
-               ast_bridge_unlock(bridge);
-       }
+       publish_blind_transfer_full(is_external, result, transferer, bridge, context,
+               exten, transferee_channel, NULL);
 }
 
 enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
@@ -4301,8 +4320,12 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
        set_transfer_variables_all(transferer, channels, 0);
 
        if (do_bridge_transfer) {
-               transfer_result = blind_transfer_bridge(transferer, bridge, exten, context,
-                               new_channel_cb, user_data_wrapper);
+               /* if blind_transfer_bridge succeeds, it publishes its own message */
+               transfer_result = blind_transfer_bridge(is_external, transferer, bridge,
+                       exten, context, transferee, new_channel_cb, user_data_wrapper);
+               if (transfer_result == AST_BRIDGE_TRANSFER_SUCCESS)  {
+                       return transfer_result;
+               }
                goto publish;
        }
 
index 7b61ce3..16fda2d 100644 (file)
@@ -636,7 +636,10 @@ static struct ast_json *blind_transfer_to_json(struct stasis_message *msg,
        const struct stasis_message_sanitizer *sanitize)
 {
        struct ast_blind_transfer_message *transfer_msg = stasis_message_data(msg);
-       struct ast_json *json_transferer, *json_transferee = NULL, *out;
+       struct ast_json *json_transferer;
+       struct ast_json *json_transferee = NULL;
+       struct ast_json *out;
+       struct ast_json *json_replace = NULL;
        const struct timeval *tv = stasis_message_timestamp(msg);
 
        json_transferer = ast_channel_snapshot_to_json(transfer_msg->to_transferee.channel_snapshot, sanitize);
@@ -647,6 +650,16 @@ static struct ast_json *blind_transfer_to_json(struct stasis_message *msg,
        if (transfer_msg->transferee) {
                json_transferee = ast_channel_snapshot_to_json(transfer_msg->transferee, sanitize);
                if (!json_transferee) {
+                       ast_json_unref(json_transferer);
+                       return NULL;
+               }
+       }
+
+       if (transfer_msg->replace_channel) {
+               json_replace = ast_channel_snapshot_to_json(transfer_msg->replace_channel, sanitize);
+               if (!json_replace) {
+                       ast_json_unref(json_transferee);
+                       ast_json_unref(json_transferer);
                        return NULL;
                }
        }
@@ -661,11 +674,19 @@ static struct ast_json *blind_transfer_to_json(struct stasis_message *msg,
                "is_external", ast_json_boolean(transfer_msg->is_external));
 
        if (!out) {
+               ast_json_unref(json_transferee);
+               ast_json_unref(json_replace);
                return NULL;
        }
 
        if (json_transferee && ast_json_object_set(out, "transferee", json_transferee)) {
                ast_json_unref(out);
+               ast_json_unref(json_replace);
+               return NULL;
+       }
+
+       if (json_replace && ast_json_object_set(out, "replace_channel", json_replace)) {
+               ast_json_unref(out);
                return NULL;
        }
 
@@ -741,7 +762,7 @@ static void blind_transfer_dtor(void *obj)
 
 void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result result,
                struct ast_bridge_channel_pair *transferer, const char *context, const char *exten,
-               struct ast_channel *transferee_channel)
+               struct ast_channel *transferee_channel, struct ast_channel *replace_channel)
 {
        struct ast_blind_transfer_message *msg;
        struct stasis_message *stasis;
@@ -759,6 +780,9 @@ void ast_bridge_publish_blind_transfer(int is_external, enum ast_transfer_result
        if (transferee_channel) {
                msg->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee_channel));
        }
+       if (replace_channel) {
+               msg->replace_channel = ast_channel_snapshot_get_latest(ast_channel_uniqueid(replace_channel));
+       }
        msg->is_external = is_external;
        msg->result = result;
        ast_copy_string(msg->context, context, sizeof(msg->context));
index be1a244..06c3cf7 100644 (file)
@@ -2062,6 +2062,15 @@ int ast_ari_validate_bridge_blind_transfer(struct ast_json *json)
                                res = 0;
                        }
                } else
+               if (strcmp("replace_channel", ast_json_object_iter_key(iter)) == 0) {
+                       int prop_is_valid;
+                       prop_is_valid = ast_ari_validate_channel(
+                               ast_json_object_iter_value(iter));
+                       if (!prop_is_valid) {
+                               ast_log(LOG_ERROR, "ARI BridgeBlindTransfer field replace_channel failed validation\n");
+                               res = 0;
+                       }
+               } else
                if (strcmp("result", ast_json_object_iter_key(iter)) == 0) {
                        int prop_is_valid;
                        has_result = 1;
index 64dd1b0..3a0bdb9 100644 (file)
@@ -1304,6 +1304,7 @@ ari_validator ast_ari_validate_application_fn(void);
  * - context: string (required)
  * - exten: string (required)
  * - is_external: boolean (required)
+ * - replace_channel: Channel
  * - result: string (required)
  * - transferee: Channel
  * BridgeCreated
index 2459367..9440cf1 100644 (file)
@@ -696,19 +696,6 @@ static int bridge_app_subscribed_involved(struct stasis_app *app, struct ast_bri
        return subscribed;
 }
 
-static void bridge_blind_transfer_handler(void *data, struct stasis_subscription *sub,
-       struct stasis_message *message)
-{
-       struct stasis_app *app = data;
-       struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
-       struct ast_bridge_snapshot *bridge = transfer_msg->to_transferee.bridge_snapshot;
-
-       if (bridge_app_subscribed(app, transfer_msg->to_transferee.channel_snapshot->uniqueid) ||
-               (bridge && bridge_app_subscribed_involved(app, bridge))) {
-               stasis_publish(app->topic, message);
-       }
-}
-
 static void set_replacement_channel(struct ast_channel_snapshot *to_be_replaced,
                struct ast_channel_snapshot *replacing)
 {
@@ -726,6 +713,24 @@ static void set_replacement_channel(struct ast_channel_snapshot *to_be_replaced,
        ao2_cleanup(control);
 }
 
+static void bridge_blind_transfer_handler(void *data, struct stasis_subscription *sub,
+       struct stasis_message *message)
+{
+       struct stasis_app *app = data;
+       struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message);
+       struct ast_bridge_snapshot *bridge = transfer_msg->to_transferee.bridge_snapshot;
+
+       if (transfer_msg->replace_channel) {
+               set_replacement_channel(transfer_msg->to_transferee.channel_snapshot,
+                               transfer_msg->replace_channel);
+       }
+
+       if (bridge_app_subscribed(app, transfer_msg->to_transferee.channel_snapshot->uniqueid) ||
+               (bridge && bridge_app_subscribed_involved(app, bridge))) {
+               stasis_publish(app->topic, message);
+       }
+}
+
 static void bridge_attended_transfer_handler(void *data, struct stasis_subscription *sub,
        struct stasis_message *message)
 {
index 9288ac0..2623801 100644 (file)
                                        "required": true,
                                        "type": "Channel"
                                },
+                               "replace_channel": {
+                                       "description": "The channel that is replacing transferer when the transferee(s) can not be transferred directly",
+                                       "required": false,
+                                       "type": "Channel"
+                               },
                                "transferee": {
                                        "description": "The channel that is being transferred",
                                        "required": false,
index b2a1fd3..017c48b 100644 (file)
@@ -1257,7 +1257,7 @@ AST_TEST_DEFINE(test_cel_blind_transfer)
        pair.channel = chan_alice;
        ast_bridge_lock(bridge);
        ast_bridge_publish_blind_transfer(1, AST_BRIDGE_TRANSFER_SUCCESS,
-               &pair, "transfer_context", "transfer_extension", NULL);
+               &pair, "transfer_context", "transfer_extension", NULL, NULL);
        ast_bridge_unlock(bridge);
        BLINDTRANSFER_EVENT(chan_alice, bridge, "transfer_extension", "transfer_context");