diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/bridge.c | 146 | ||||
-rw-r--r-- | main/bridge_basic.c | 124 | ||||
-rw-r--r-- | main/cel.c | 53 | ||||
-rw-r--r-- | main/channel.c | 23 | ||||
-rw-r--r-- | main/stasis_bridges.c | 219 |
5 files changed, 421 insertions, 144 deletions
diff --git a/main/bridge.c b/main/bridge.c index 462676ca8..926004bc9 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -1825,6 +1825,32 @@ static void bridge_channel_change_bridge(struct ast_bridge_channel *bridge_chann ao2_ref(old_bridge, -1); } +static void bridge_channel_moving(struct ast_bridge_channel *bridge_channel, struct ast_bridge *src, struct ast_bridge *dst) +{ + struct ast_bridge_features *features = bridge_channel->features; + struct ast_bridge_hook *hook; + struct ao2_iterator iter; + + /* Run any moving hooks. */ + iter = ao2_iterator_init(features->other_hooks, 0); + for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) { + int remove_me; + ast_bridge_move_indicate_callback move_cb; + + if (hook->type != AST_BRIDGE_HOOK_TYPE_MOVE) { + continue; + } + move_cb = (ast_bridge_move_indicate_callback) hook->callback; + remove_me = move_cb(bridge_channel, hook->hook_pvt, src, dst); + if (remove_me) { + ast_debug(1, "Move detection hook %p is being removed from %p(%s)\n", + hook, bridge_channel, ast_channel_name(bridge_channel->chan)); + ao2_unlink(features->other_hooks, hook); + } + } + ao2_iterator_destroy(&iter); +} + void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick, unsigned int optimized) { @@ -1873,6 +1899,8 @@ void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg continue; } + bridge_channel_moving(bridge_channel, bridge_channel->bridge, dst_bridge); + /* Point to new bridge.*/ bridge_channel_change_bridge(bridge_channel, dst_bridge); @@ -2122,6 +2150,8 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri ao2_ref(orig_bridge, +1);/* Keep a ref in case the push fails. */ bridge_channel_change_bridge(bridge_channel, dst_bridge); + bridge_channel_moving(bridge_channel, orig_bridge, dst_bridge); + if (bridge_channel_internal_push(bridge_channel)) { /* Try to put the channel back into the original bridge. */ ast_bridge_features_remove(bridge_channel->features, @@ -3089,6 +3119,18 @@ int ast_bridge_talk_detector_hook(struct ast_bridge_features *features, AST_BRIDGE_HOOK_TYPE_TALK); } +int ast_bridge_move_hook(struct ast_bridge_features *features, + ast_bridge_move_indicate_callback callback, + void *hook_pvt, + ast_bridge_hook_pvt_destructor destructor, + enum ast_bridge_hook_remove_flags remove_flags) +{ + ast_bridge_hook_callback hook_cb = (ast_bridge_hook_callback) callback; + + return bridge_other_hook(features, hook_cb, hook_pvt, destructor, remove_flags, + AST_BRIDGE_HOOK_TYPE_MOVE); +} + int ast_bridge_interval_hook(struct ast_bridge_features *features, enum ast_bridge_hook_timer_option flags, unsigned int interval, @@ -3828,14 +3870,55 @@ struct stasis_attended_transfer_publish_data { struct ast_bridge_channel_pair to_transferee; /* The bridge between the transferer and transfer target, and the transferer channel in this bridge */ struct ast_bridge_channel_pair to_transfer_target; + /* The Local;1 that will replace the transferee bridge transferer channel */ + struct ast_channel *replace_channel; + /* The transferee channel. NULL if there is no transferee channel or if multiple parties are transferred */ + struct ast_channel *transferee_channel; + /* The transfer target channel. NULL if there is no transfer target channel or if multiple parties are transferred */ + struct ast_channel *target_channel; }; +/*! + * \internal + * \brief Get the transferee channel + * + * This is only applicable to cases where a transfer is occurring on a + * two-party bridge. The channels container passed in is expected to only + * contain two channels, the transferer and the transferee. The transferer + * channel is passed in as a parameter to ensure we don't return it as + * the transferee channel. + * + * \param channels A two-channel container containing the transferer and transferee + * \param transferer The party that is transfering the call + * \return The party that is being transferred + */ +static struct ast_channel *get_transferee(struct ao2_container *channels, struct ast_channel *transferer) +{ + struct ao2_iterator channel_iter; + struct ast_channel *transferee; + + for (channel_iter = ao2_iterator_init(channels, 0); + (transferee = ao2_iterator_next(&channel_iter)); + ao2_cleanup(transferee)) { + if (transferee != transferer) { + break; + } + } + + ao2_iterator_destroy(&channel_iter); + return transferee; +} + + static void stasis_publish_data_cleanup(struct stasis_attended_transfer_publish_data *publication) { ast_channel_unref(publication->to_transferee.channel); ast_channel_unref(publication->to_transfer_target.channel); + ast_channel_cleanup(publication->transferee_channel); + ast_channel_cleanup(publication->target_channel); ao2_cleanup(publication->to_transferee.bridge); ao2_cleanup(publication->to_transfer_target.bridge); + ao2_cleanup(publication->replace_channel); } /*! @@ -3865,6 +3948,9 @@ static void stasis_publish_data_init(struct ast_channel *to_transferee, ao2_ref(to_target_bridge, +1); publication->to_transfer_target.bridge = to_target_bridge; } + + publication->transferee_channel = ast_bridge_peer(to_transferee_bridge, to_transferee); + publication->target_channel = ast_bridge_peer(to_target_bridge, to_transfer_target); } /* @@ -3878,7 +3964,8 @@ static void publish_attended_transfer_bridge_merge(struct stasis_attended_transf struct ast_bridge *final_bridge) { ast_bridge_publish_attended_transfer_bridge_merge(1, AST_BRIDGE_TRANSFER_SUCCESS, - &publication->to_transferee, &publication->to_transfer_target, final_bridge); + &publication->to_transferee, &publication->to_transfer_target, final_bridge, + publication->transferee_channel, publication->target_channel); } /* @@ -3892,7 +3979,9 @@ static void publish_attended_transfer_app(struct stasis_attended_transfer_publis const char *app) { ast_bridge_publish_attended_transfer_app(1, AST_BRIDGE_TRANSFER_SUCCESS, - &publication->to_transferee, &publication->to_transfer_target, app); + &publication->to_transferee, &publication->to_transfer_target, + publication->replace_channel, app, + publication->transferee_channel, publication->target_channel); } /* @@ -3909,7 +3998,8 @@ static void publish_attended_transfer_link(struct stasis_attended_transfer_publi struct ast_channel *locals[2] = { local_channel1, local_channel2 }; ast_bridge_publish_attended_transfer_link(1, AST_BRIDGE_TRANSFER_SUCCESS, - &publication->to_transferee, &publication->to_transfer_target, locals); + &publication->to_transferee, &publication->to_transfer_target, locals, + publication->transferee_channel, publication->target_channel); } /* @@ -3923,7 +4013,8 @@ static void publish_attended_transfer_fail(struct stasis_attended_transfer_publi enum ast_transfer_result result) { ast_bridge_publish_attended_transfer_fail(1, result, &publication->to_transferee, - &publication->to_transfer_target); + &publication->to_transfer_target, publication->transferee_channel, + publication->target_channel); } /*! @@ -3987,9 +4078,12 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha return AST_BRIDGE_TRANSFER_FAIL; } + /* Get a ref for use later since this one is being stolen */ + ao2_ref(local_chan, +1); if (ast_bridge_impart(bridge1, local_chan, chan1, NULL, AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) { ast_hangup(local_chan); + ao2_cleanup(local_chan); return AST_BRIDGE_TRANSFER_FAIL; } @@ -4005,40 +4099,12 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha publish_attended_transfer_link(publication, local_chan, local_chan2); } else { + publication->replace_channel = ao2_bump(local_chan); publish_attended_transfer_app(publication, app); } - return AST_BRIDGE_TRANSFER_SUCCESS; -} -/*! - * \internal - * \brief Get the transferee channel - * - * This is only applicable to cases where a transfer is occurring on a - * two-party bridge. The channels container passed in is expected to only - * contain two channels, the transferer and the transferee. The transferer - * channel is passed in as a parameter to ensure we don't return it as - * the transferee channel. - * - * \param channels A two-channel container containing the transferer and transferee - * \param transferer The party that is transfering the call - * \return The party that is being transferred - */ -static struct ast_channel *get_transferee(struct ao2_container *channels, struct ast_channel *transferer) -{ - struct ao2_iterator channel_iter; - struct ast_channel *transferee; - - for (channel_iter = ao2_iterator_init(channels, 0); - (transferee = ao2_iterator_next(&channel_iter)); - ao2_cleanup(transferee)) { - if (transferee != transferer) { - break; - } - } - - ao2_iterator_destroy(&channel_iter); - return transferee; + ao2_cleanup(local_chan); + return AST_BRIDGE_TRANSFER_SUCCESS; } static enum ast_transfer_result try_parking(struct ast_channel *transferer, @@ -4142,7 +4208,7 @@ static struct ast_bridge *acquire_bridge(struct ast_channel *chan) static void publish_blind_transfer(int is_external, enum ast_transfer_result result, struct ast_channel *transferer, struct ast_bridge *bridge, - const char *context, const char *exten) + const char *context, const char *exten, struct ast_channel *transferee_channel) { struct ast_bridge_channel_pair pair; pair.channel = transferer; @@ -4150,7 +4216,7 @@ static void publish_blind_transfer(int is_external, enum ast_transfer_result res if (bridge) { ast_bridge_lock(bridge); } - ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten); + ast_bridge_publish_blind_transfer(is_external, result, &pair, context, exten, transferee_channel); if (bridge) { ast_bridge_unlock(bridge); } @@ -4174,6 +4240,9 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external, transfer_result = AST_BRIDGE_TRANSFER_INVALID; goto publish; } + + transferee = ast_bridge_peer(bridge, transferer); + ast_channel_lock(transferer); bridge_channel = ast_channel_get_bridge_channel(transferer); ast_channel_unlock(transferer); @@ -4235,7 +4304,6 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external, /* Reaching this portion means that we're dealing with a two-party bridge */ - transferee = get_transferee(channels, transferer); if (!transferee) { transfer_result = AST_BRIDGE_TRANSFER_FAIL; goto publish; @@ -4251,7 +4319,7 @@ enum ast_transfer_result ast_bridge_transfer_blind(int is_external, transfer_result = AST_BRIDGE_TRANSFER_SUCCESS; publish: - publish_blind_transfer(is_external, transfer_result, transferer, bridge, context, exten); + publish_blind_transfer(is_external, transfer_result, transferer, bridge, context, exten, transferee); return transfer_result; } diff --git a/main/bridge_basic.c b/main/bridge_basic.c index 0d95d83d2..a69750d15 100644 --- a/main/bridge_basic.c +++ b/main/bridge_basic.c @@ -1526,9 +1526,85 @@ static void stimulate_attended_transfer(struct attended_transfer_properties *pro } /*! + * \brief Get a desired transfer party for a bridge the transferer is not in. + * + * \param bridge The bridge to get the party from. May be NULL. + * \param[out] party The lone channel in the bridge. Will be set NULL if bridge is NULL or multiple parties are present. + */ +static void get_transfer_party_non_transferer_bridge(struct ast_bridge *bridge, + struct ast_channel **party) +{ + if (bridge && bridge->num_channels == 1) { + *party = ast_channel_ref(AST_LIST_FIRST(&bridge->channels)->chan); + } else { + *party = NULL; + } +} + +/*! + * \brief Get the transferee and transfer target when the transferer is in a bridge with + * one of the desired parties. + * + * \param transferer_bridge The bridge the transferer is in + * \param other_bridge The bridge the transferer is not in. May be NULL. + * \param transferer The transferer party + * \param[out] transferer_peer The party that is in the bridge with the transferer + * \param[out] other_party The party that is in the other_bridge + */ +static void get_transfer_parties_transferer_bridge(struct ast_bridge *transferer_bridge, + struct ast_bridge *other_bridge, struct ast_channel *transferer, + struct ast_channel **transferer_peer, struct ast_channel **other_party) +{ + *transferer_peer = ast_bridge_peer(transferer_bridge, transferer); + get_transfer_party_non_transferer_bridge(other_bridge, other_party); +} + +/*! + * \brief determine transferee and transfer target for an attended transfer + * + * In builtin attended transfers, there is a single transferer channel that jumps between + * the two bridges involved. At the time the attended transfer occurs, the transferer could + * be in either bridge, so determining the parties is a bit more complex than normal. + * + * The method used here is to determine which of the two bridges the transferer is in, and + * grabbing the peer from that bridge. The other bridge, if it only has a single channel in it, + * has the other desired channel. + * + * \param transferer The channel performing the transfer + * \param transferee_bridge The bridge that the transferee is in + * \param target_bridge The bridge that the transfer target is in + * \param[out] transferee The transferee channel + * \param[out] transfer_target The transfer target channel + */ +static void get_transfer_parties(struct ast_channel *transferer, struct ast_bridge *transferee_bridge, + struct ast_bridge *target_bridge, struct ast_channel **transferee, + struct ast_channel **transfer_target) +{ + struct ast_bridge *transferer_bridge; + + ast_channel_lock(transferer); + transferer_bridge = ast_channel_get_bridge(transferer); + ast_channel_unlock(transferer); + + if (transferer_bridge == transferee_bridge) { + get_transfer_parties_transferer_bridge(transferee_bridge, target_bridge, + transferer, transferee, transfer_target); + } else if (transferer_bridge == target_bridge) { + get_transfer_parties_transferer_bridge(target_bridge, transferee_bridge, + transferer, transfer_target, transferee); + } else { + get_transfer_party_non_transferer_bridge(transferee_bridge, transferee); + get_transfer_party_non_transferer_bridge(target_bridge, transfer_target); + } + + ao2_cleanup(transferer_bridge); +} + +/*! * \brief Send a stasis publication for a successful attended transfer */ -static void publish_transfer_success(struct attended_transfer_properties *props) +static void publish_transfer_success(struct attended_transfer_properties *props, + struct ast_channel *transferee_channel, struct ast_channel *target_channel) { struct ast_bridge_channel_pair transferee = { .channel = props->transferer, @@ -1548,7 +1624,8 @@ static void publish_transfer_success(struct attended_transfer_properties *props) } ast_bridge_publish_attended_transfer_bridge_merge(0, AST_BRIDGE_TRANSFER_SUCCESS, - &transferee, &transfer_target, props->transferee_bridge); + &transferee, &transfer_target, props->transferee_bridge, transferee_channel, + target_channel); if (transferee.bridge) { ast_bridge_unlock(transferee.bridge); @@ -1561,7 +1638,8 @@ static void publish_transfer_success(struct attended_transfer_properties *props) /*! * \brief Send a stasis publication for an attended transfer that ends in a threeway call */ -static void publish_transfer_threeway(struct attended_transfer_properties *props) +static void publish_transfer_threeway(struct attended_transfer_properties *props, + struct ast_channel *transferee_channel, struct ast_channel *target_channel) { struct ast_bridge_channel_pair transferee = { .channel = props->transferer, @@ -1585,7 +1663,8 @@ static void publish_transfer_threeway(struct attended_transfer_properties *props } ast_bridge_publish_attended_transfer_threeway(0, AST_BRIDGE_TRANSFER_SUCCESS, - &transferee, &transfer_target, &threeway); + &transferee, &transfer_target, &threeway, transferee_channel, + target_channel); if (transferee.bridge) { ast_bridge_unlock(transferee.bridge); @@ -1608,6 +1687,8 @@ static void publish_transfer_fail(struct attended_transfer_properties *props) .channel = props->transferer, .bridge = props->target_bridge, }; + struct ast_channel *transferee_channel; + struct ast_channel *target_channel; if (transferee.bridge && transfer_target.bridge) { ast_bridge_lock_both(transferee.bridge, transfer_target.bridge); @@ -1617,8 +1698,12 @@ static void publish_transfer_fail(struct attended_transfer_properties *props) ast_bridge_lock(transfer_target.bridge); } + get_transfer_parties(props->transferer, props->transferee_bridge, props->target_bridge, + &transferee_channel, &target_channel); ast_bridge_publish_attended_transfer_fail(0, AST_BRIDGE_TRANSFER_FAIL, - &transferee, &transfer_target); + &transferee, &transfer_target, transferee_channel, target_channel); + ast_channel_cleanup(transferee_channel); + ast_channel_cleanup(target_channel); if (transferee.bridge) { ast_bridge_unlock(transferee.bridge); @@ -2072,11 +2157,18 @@ static int resume_enter(struct attended_transfer_properties *props) static int threeway_enter(struct attended_transfer_properties *props) { + struct ast_channel *transferee_channel; + struct ast_channel *target_channel; + + get_transfer_parties(props->transferer, props->transferee_bridge, props->target_bridge, + &transferee_channel, &target_channel); bridge_merge(props->transferee_bridge, props->target_bridge, NULL, 0); play_sound(props->transfer_target, props->xfersound); play_sound(props->transferer, props->xfersound); - publish_transfer_threeway(props); + publish_transfer_threeway(props, transferee_channel, target_channel); + ast_channel_cleanup(transferee_channel); + ast_channel_cleanup(target_channel); return 0; } @@ -2178,17 +2270,33 @@ static enum attended_transfer_state double_checking_exit(struct attended_transfe static int complete_enter(struct attended_transfer_properties *props) { + struct ast_channel *transferee_channel; + struct ast_channel *target_channel; + + get_transfer_parties(props->transferer, props->transferee_bridge, props->target_bridge, + &transferee_channel, &target_channel); bridge_merge(props->transferee_bridge, props->target_bridge, &props->transferer, 1); play_sound(props->transfer_target, props->xfersound); - publish_transfer_success(props); + publish_transfer_success(props, transferee_channel, target_channel); + + ast_channel_cleanup(transferee_channel); + ast_channel_cleanup(target_channel); return 0; } static int blond_enter(struct attended_transfer_properties *props) { + struct ast_channel *transferee_channel; + struct ast_channel *target_channel; + + get_transfer_parties(props->transferer, props->transferee_bridge, props->target_bridge, + &transferee_channel, &target_channel); bridge_merge(props->transferee_bridge, props->target_bridge, &props->transferer, 1); ringing(props->transfer_target); - publish_transfer_success(props); + publish_transfer_success(props, transferee_channel, target_channel); + + ast_channel_cleanup(transferee_channel); + ast_channel_cleanup(target_channel); return 0; } diff --git a/main/cel.c b/main/cel.c index 4be13511c..bb0f75051 100644 --- a/main/cel.c +++ b/main/cel.c @@ -1358,44 +1358,20 @@ static void cel_blind_transfer_cb( void *data, struct stasis_subscription *sub, struct stasis_message *message) { - struct ast_bridge_blob *obj = stasis_message_data(message); - struct ast_channel_snapshot *chan_snapshot = obj->channel; - struct ast_bridge_snapshot *bridge_snapshot = obj->bridge; - struct ast_json *blob = obj->blob; - struct ast_json *json_result = ast_json_object_get(blob, "result"); - struct ast_json *json_exten; - struct ast_json *json_context; + struct ast_blind_transfer_message *transfer_msg = stasis_message_data(message); + struct ast_channel_snapshot *chan_snapshot = transfer_msg->to_transferee.channel_snapshot; + struct ast_bridge_snapshot *bridge_snapshot = transfer_msg->to_transferee.bridge_snapshot; struct ast_json *extra; - const char *exten; - const char *context; - enum ast_transfer_result result; - if (!json_result) { - return; - } - - result = ast_json_integer_get(json_result); - if (result != AST_BRIDGE_TRANSFER_SUCCESS) { - return; - } - - json_exten = ast_json_object_get(blob, "exten"); - json_context = ast_json_object_get(blob, "context"); - - if (!json_exten || !json_context) { - return; - } - - exten = ast_json_string_get(json_exten); - context = ast_json_string_get(json_context); - if (!exten || !context) { + if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) { return; } extra = ast_json_pack("{s: s, s: s, s: s}", - "extension", exten, - "context", context, - "bridge_id", bridge_snapshot->uniqueid); + "extension", transfer_msg->exten, + "context", transfer_msg->context, + "bridge_id", bridge_snapshot->uniqueid, + "transferee_channel_name", transfer_msg->transferee ? transfer_msg->transferee->name: "N/A"); if (extra) { cel_report_event(chan_snapshot, AST_CEL_BLINDTRANSFER, NULL, extra, NULL); ast_json_unref(extra); @@ -1431,19 +1407,24 @@ static void cel_attended_transfer_cb( case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE: case AST_ATTENDED_TRANSFER_DEST_LINK: case AST_ATTENDED_TRANSFER_DEST_THREEWAY: - extra = ast_json_pack("{s: s, s: s, s: s}", + extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}", "bridge1_id", bridge1->uniqueid, "channel2_name", channel2->name, - "bridge2_id", bridge2->uniqueid); + "bridge2_id", bridge2->uniqueid, + "transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A", + "transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A"); if (!extra) { return; } break; case AST_ATTENDED_TRANSFER_DEST_APP: - extra = ast_json_pack("{s: s, s: s, s: s}", + case AST_ATTENDED_TRANSFER_DEST_LOCAL_APP: + extra = ast_json_pack("{s: s, s: s, s: s, s: s, s: s}", "bridge1_id", bridge1->uniqueid, "channel2_name", channel2->name, - "app", xfer->dest.app); + "app", xfer->dest.app, + "transferee_channel_name", xfer->transferee ? xfer->transferee->name : "N/A", + "transfer_target_channel_name", xfer->target ? xfer->target->name : "N/A"); if (!extra) { return; } diff --git a/main/channel.c b/main/channel.c index 23799d9be..50b9e8726 100644 --- a/main/channel.c +++ b/main/channel.c @@ -6596,17 +6596,36 @@ static void channel_do_masquerade(struct ast_channel *original, struct ast_chann *ast_channel_hangup_handlers(original) = *ast_channel_hangup_handlers(clonechan); *ast_channel_hangup_handlers(clonechan) = exchange.handlers; - /* Move data stores over */ + /* Call fixup handlers for the clone chan */ if (AST_LIST_FIRST(ast_channel_datastores(clonechan))) { struct ast_datastore *ds; /* We use a safe traversal here because some fixup routines actually * remove the datastore from the list and free them. */ AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_datastores(clonechan), ds, entry) { - if (ds->info->chan_fixup) + if (ds->info->chan_fixup) { ds->info->chan_fixup(ds->data, clonechan, original); + } + } + AST_LIST_TRAVERSE_SAFE_END; + } + + /* Call breakdown handlers for the original chan */ + if (AST_LIST_FIRST(ast_channel_datastores(original))) { + struct ast_datastore *ds; + /* We use a safe traversal here because some breakdown routines may + * remove the datastore from the list and free them. + */ + AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_datastores(original), ds, entry) { + if (ds->info->chan_breakdown) { + ds->info->chan_breakdown(ds->data, clonechan, original); + } } AST_LIST_TRAVERSE_SAFE_END; + } + + /* Move data stores over */ + if (AST_LIST_FIRST(ast_channel_datastores(clonechan))) { AST_LIST_APPEND_LIST(ast_channel_datastores(original), ast_channel_datastores(clonechan), entry); } diff --git a/main/stasis_bridges.c b/main/stasis_bridges.c index 56f7605f7..c94d2ea10 100644 --- a/main/stasis_bridges.c +++ b/main/stasis_bridges.c @@ -62,6 +62,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") contacted. It means that a party was succesfully placed into the dialplan at the expected location.</para></note> </parameter> <channel_snapshot prefix="Transferer"/> + <channel_snapshot prefix="Transferee"/> <bridge_snapshot/> <parameter name="IsExternal"> <para>Indicates if the transfer was performed outside of Asterisk. For instance, @@ -113,6 +114,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <para>The name of the surviving transferer channel when a transfer results in a threeway call</para> <note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Threeway</literal></para></note> </parameter> + <channel_snapshot prefix="Transferee" /> </syntax> <description> <para>The headers in this event attempt to describe all the major details of the attended transfer. The two transferer channels @@ -633,30 +635,41 @@ static const char *result_strs[] = { static struct ast_json *blind_transfer_to_json(struct stasis_message *msg, const struct stasis_message_sanitizer *sanitize) { - struct ast_bridge_blob *blob = stasis_message_data(msg); - struct ast_json *json_channel, *out; + struct ast_blind_transfer_message *transfer_msg = stasis_message_data(msg); + struct ast_json *json_transferer, *json_transferee, *out; const struct timeval *tv = stasis_message_timestamp(msg); - json_channel = ast_channel_snapshot_to_json(blob->channel, sanitize); - if (!json_channel) { + json_transferer = ast_channel_snapshot_to_json(transfer_msg->to_transferee.channel_snapshot, sanitize); + if (!json_transferer) { return NULL; } - out = ast_json_pack("{s: s, s: o, s: o, s: O, s: O, s: s, s: o}", + if (transfer_msg->transferee) { + json_transferee = ast_channel_snapshot_to_json(transfer_msg->transferee, sanitize); + if (!json_transferee) { + return NULL; + } + } else { + json_transferee = ast_json_null(); + } + + out = ast_json_pack("{s: s, s: o, s: o, s: o, s: s, s: s, s: s, s: o}", "type", "BridgeBlindTransfer", "timestamp", ast_json_timeval(*tv, NULL), - "channel", json_channel, - "exten", ast_json_object_get(blob->blob, "exten"), - "context", ast_json_object_get(blob->blob, "context"), - "result", result_strs[ast_json_integer_get(ast_json_object_get(blob->blob, "result"))], - "is_external", ast_json_boolean(ast_json_integer_get(ast_json_object_get(blob->blob, "is_external")))); + "transferer", json_transferer, + "transferee", json_transferee, + "exten", transfer_msg->exten, + "context", transfer_msg->context, + "result", result_strs[transfer_msg->result], + "is_external", ast_json_boolean(transfer_msg->is_external)); if (!out) { return NULL; } - if (blob->bridge) { - struct ast_json *json_bridge = ast_bridge_snapshot_to_json(blob->bridge, sanitize); + if (transfer_msg->to_transferee.bridge_snapshot) { + struct ast_json *json_bridge = ast_bridge_snapshot_to_json( + transfer_msg->to_transferee.bridge_snapshot, sanitize); if (!json_bridge || ast_json_object_set(out, "bridge", json_bridge)) { ast_json_unref(out); @@ -669,73 +682,96 @@ static struct ast_json *blind_transfer_to_json(struct stasis_message *msg, static struct ast_manager_event_blob *blind_transfer_to_ami(struct stasis_message *msg) { - RAII_VAR(struct ast_str *, channel_state, NULL, ast_free_ptr); + RAII_VAR(struct ast_str *, transferer_state, NULL, ast_free_ptr); RAII_VAR(struct ast_str *, bridge_state, NULL, ast_free_ptr); - struct ast_bridge_blob *blob = stasis_message_data(msg); - const char *exten; - const char *context; - enum ast_transfer_result result; - int is_external; + RAII_VAR(struct ast_str *, transferee_state, NULL, ast_free_ptr); + struct ast_blind_transfer_message *transfer_msg = stasis_message_data(msg); - if (!blob) { + if (!transfer_msg) { return NULL; } - channel_state = ast_manager_build_channel_state_string_prefix(blob->channel, "Transferer"); - if (!channel_state) { + transferer_state = ast_manager_build_channel_state_string_prefix( + transfer_msg->to_transferee.channel_snapshot, "Transferer"); + if (!transferer_state) { return NULL; } - if (blob->bridge) { - bridge_state = ast_manager_build_bridge_state_string(blob->bridge); + if (transfer_msg->to_transferee.bridge_snapshot) { + bridge_state = ast_manager_build_bridge_state_string(transfer_msg->to_transferee.bridge_snapshot); if (!bridge_state) { return NULL; } } - exten = ast_json_string_get(ast_json_object_get(blob->blob, "exten")); - context = ast_json_string_get(ast_json_object_get(blob->blob, "context")); - result = ast_json_integer_get(ast_json_object_get(blob->blob, "result")); - is_external = ast_json_integer_get(ast_json_object_get(blob->blob, "is_external")); + if (transfer_msg->transferee) { + transferee_state = ast_manager_build_channel_state_string_prefix( + transfer_msg->transferee, "Transferee"); + if (!transferee_state) { + return NULL; + } + } return ast_manager_event_blob_create(EVENT_FLAG_CALL, "BlindTransfer", "Result: %s\r\n" "%s" "%s" + "%s" "IsExternal: %s\r\n" "Context: %s\r\n" "Extension: %s\r\n", - result_strs[result], - ast_str_buffer(channel_state), + result_strs[transfer_msg->result], + ast_str_buffer(transferer_state), + transferee_state ? ast_str_buffer(transferee_state) : "", bridge_state ? ast_str_buffer(bridge_state) : "", - is_external ? "Yes" : "No", - context, - exten); + transfer_msg->is_external ? "Yes" : "No", + transfer_msg->context, + transfer_msg->exten); +} + +static void blind_transfer_dtor(void *obj) +{ + struct ast_blind_transfer_message *msg = obj; + + bridge_channel_snapshot_pair_cleanup(&msg->to_transferee); + ao2_cleanup(msg->transferee); } 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_bridge_channel_pair *transferer, const char *context, const char *exten, + struct ast_channel *transferee_channel) { - RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref); - RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); + struct ast_blind_transfer_message *msg; + struct stasis_message *stasis; - json_object = ast_json_pack("{s: s, s: s, s: i, s: i}", - "context", context, "exten", exten, "result", result, "is_external", is_external); + msg = ao2_alloc(sizeof(*msg), blind_transfer_dtor); + if (!msg) { + return; + } - if (!json_object) { - ast_log(LOG_NOTICE, "Failed to create json bridge blob\n"); + if (bridge_channel_snapshot_pair_init(transferer, &msg->to_transferee)) { + ao2_cleanup(msg); return; } - msg = ast_bridge_blob_create(ast_blind_transfer_type(), - transferer->bridge, transferer->channel, json_object); + if (transferee_channel) { + msg->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee_channel)); + } + msg->is_external = is_external; + msg->result = result; + ast_copy_string(msg->context, context, sizeof(msg->context)); + ast_copy_string(msg->exten, exten, sizeof(msg->exten)); - if (!msg) { - ast_log(LOG_NOTICE, "Failed to create blob msg\n"); + stasis = stasis_message_create(ast_blind_transfer_type(), msg); + if (!stasis) { + ao2_cleanup(msg); return; } - stasis_publish(ast_bridge_topic_all(), msg); + stasis_publish(ast_bridge_topic_all(), stasis); + + ao2_cleanup(stasis); + ao2_cleanup(msg); } static struct ast_json *attended_transfer_to_json(struct stasis_message *msg, @@ -743,7 +779,7 @@ static struct ast_json *attended_transfer_to_json(struct stasis_message *msg, { struct ast_attended_transfer_message *transfer_msg = stasis_message_data(msg); RAII_VAR(struct ast_json *, out, NULL, ast_json_unref); - struct ast_json *json_transferer1, *json_transferer2, *json_bridge, *json_channel; + struct ast_json *json_transferer1, *json_transferer2, *json_bridge, *json_channel, *json_transferee, *json_target; const struct timeval *tv = stasis_message_timestamp(msg); int res = 0; @@ -758,11 +794,25 @@ static struct ast_json *attended_transfer_to_json(struct stasis_message *msg, return NULL; } - out = ast_json_pack("{s: s, s: o, s: o, s: o, s: s, s: o}", + if (transfer_msg->transferee) { + json_transferee = ast_channel_snapshot_to_json(transfer_msg->transferee, sanitize); + } else { + json_transferee = ast_json_null(); + } + + if (transfer_msg->target) { + json_target = ast_channel_snapshot_to_json(transfer_msg->target, sanitize); + } else { + json_target = ast_json_null(); + } + + out = ast_json_pack("{s: s, s: o, s: o, s: o, s: o, s: o, s: s, s: o}", "type", "BridgeAttendedTransfer", "timestamp", ast_json_timeval(*tv, NULL), "transferer_first_leg", json_transferer1, "transferer_second_leg", json_transferer2, + "transferee", json_transferee, + "transfer_target", json_target, "result", result_strs[transfer_msg->result], "is_external", ast_json_boolean(transfer_msg->is_external)); if (!out) { @@ -794,6 +844,9 @@ static struct ast_json *attended_transfer_to_json(struct stasis_message *msg, res |= ast_json_object_set(out, "destination_type", ast_json_string_create("bridge")); res |= ast_json_object_set(out, "destination_bridge", ast_json_string_create(transfer_msg->dest.bridge)); break; + case AST_ATTENDED_TRANSFER_DEST_LOCAL_APP: + res |= ast_json_object_set(out, "replace_channel", ast_channel_snapshot_to_json(transfer_msg->replace_channel, sanitize)); + /* fallthrough */ case AST_ATTENDED_TRANSFER_DEST_APP: res |= ast_json_object_set(out, "destination_type", ast_json_string_create("application")); res |= ast_json_object_set(out, "destination_application", ast_json_string_create(transfer_msg->dest.app)); @@ -851,6 +904,8 @@ static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_mes RAII_VAR(struct ast_str *, bridge2_state, NULL, ast_free_ptr); RAII_VAR(struct ast_str *, local1_state, NULL, ast_free_ptr); RAII_VAR(struct ast_str *, local2_state, NULL, ast_free_ptr); + RAII_VAR(struct ast_str *, transferee_state, NULL, ast_free_ptr); + RAII_VAR(struct ast_str *, target_state, NULL, ast_free_ptr); struct ast_attended_transfer_message *transfer_msg = stasis_message_data(msg); if (!variable_data) { @@ -862,6 +917,19 @@ static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_mes if (!transferer1_state || !transferer2_state) { return NULL; } + if (transfer_msg->transferee) { + transferee_state = ast_manager_build_channel_state_string_prefix(transfer_msg->transferee, "Transferee"); + if (!transferee_state) { + return NULL; + } + } + + if (transfer_msg->target) { + target_state = ast_manager_build_channel_state_string_prefix(transfer_msg->target, "TransferTarget"); + if (!target_state) { + return NULL; + } + } if (transfer_msg->to_transferee.bridge_snapshot) { bridge1_state = ast_manager_build_bridge_state_string_prefix( @@ -885,6 +953,7 @@ static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_mes ast_str_append(&variable_data, 0, "DestBridgeUniqueid: %s\r\n", transfer_msg->dest.bridge); break; case AST_ATTENDED_TRANSFER_DEST_APP: + case AST_ATTENDED_TRANSFER_DEST_LOCAL_APP: ast_str_append(&variable_data, 0, "DestType: App\r\n"); ast_str_append(&variable_data, 0, "DestApp: %s\r\n", transfer_msg->dest.app); break; @@ -914,6 +983,8 @@ static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_mes "%s" "%s" "%s" + "%s" + "%s" "IsExternal: %s\r\n" "%s", result_strs[transfer_msg->result], @@ -921,6 +992,8 @@ static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_mes bridge1_state ? ast_str_buffer(bridge1_state) : "", ast_str_buffer(transferer2_state), bridge2_state ? ast_str_buffer(bridge2_state) : "", + transferee_state ? ast_str_buffer(transferee_state) : "", + target_state ? ast_str_buffer(target_state) : "", transfer_msg->is_external ? "Yes" : "No", ast_str_buffer(variable_data)); } @@ -932,6 +1005,9 @@ static void attended_transfer_dtor(void *obj) bridge_channel_snapshot_pair_cleanup(&msg->to_transferee); bridge_channel_snapshot_pair_cleanup(&msg->to_transfer_target); + ao2_cleanup(msg->replace_channel); + ao2_cleanup(msg->transferee); + ao2_cleanup(msg->target); if (msg->dest_type != AST_ATTENDED_TRANSFER_DEST_LINK) { return; @@ -942,8 +1018,10 @@ static void attended_transfer_dtor(void *obj) } } -static struct ast_attended_transfer_message *attended_transfer_message_create(int is_external, enum ast_transfer_result result, - struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target) +static struct ast_attended_transfer_message *attended_transfer_message_create(int is_external, + enum ast_transfer_result result, struct ast_bridge_channel_pair *transferee, + struct ast_bridge_channel_pair *target, struct ast_channel *replace_channel, + struct ast_channel *transferee_channel, struct ast_channel *target_channel) { RAII_VAR(struct ast_attended_transfer_message *, msg, NULL, ao2_cleanup); @@ -957,6 +1035,19 @@ static struct ast_attended_transfer_message *attended_transfer_message_create(in return NULL; } + if (replace_channel) { + msg->replace_channel = ast_channel_snapshot_get_latest(ast_channel_uniqueid(replace_channel)); + if (!msg->replace_channel) { + return NULL; + } + } + + if (transferee_channel) { + msg->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee_channel)); + } + if (target_channel) { + msg->target = ast_channel_snapshot_get_latest(ast_channel_uniqueid(target_channel)); + } msg->is_external = is_external; msg->result = result; @@ -965,7 +1056,8 @@ static struct ast_attended_transfer_message *attended_transfer_message_create(in } void ast_bridge_publish_attended_transfer_fail(int is_external, enum ast_transfer_result result, - struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target) + struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target, + struct ast_channel *transferee_channel, struct ast_channel *target_channel) { RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); @@ -974,7 +1066,8 @@ void ast_bridge_publish_attended_transfer_fail(int is_external, enum ast_transfe return; } - transfer_msg = attended_transfer_message_create(is_external, result, transferee, target); + transfer_msg = attended_transfer_message_create(is_external, result, + transferee, target, NULL, transferee_channel, target_channel); if (!transfer_msg) { return; } @@ -991,7 +1084,8 @@ void ast_bridge_publish_attended_transfer_fail(int is_external, enum ast_transfe void ast_bridge_publish_attended_transfer_bridge_merge(int is_external, enum ast_transfer_result result, struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target, - struct ast_bridge *final_bridge) + struct ast_bridge *final_bridge, struct ast_channel *transferee_channel, + struct ast_channel *target_channel) { RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); @@ -1000,7 +1094,8 @@ void ast_bridge_publish_attended_transfer_bridge_merge(int is_external, enum ast return; } - transfer_msg = attended_transfer_message_create(is_external, result, transferee, target); + transfer_msg = attended_transfer_message_create(is_external, result, + transferee, target, NULL, transferee_channel, target_channel); if (!transfer_msg) { return; } @@ -1019,7 +1114,8 @@ void ast_bridge_publish_attended_transfer_bridge_merge(int is_external, enum ast void ast_bridge_publish_attended_transfer_threeway(int is_external, enum ast_transfer_result result, struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target, - struct ast_bridge_channel_pair *final_pair) + struct ast_bridge_channel_pair *final_pair, struct ast_channel *transferee_channel, + struct ast_channel *target_channel) { RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); @@ -1028,7 +1124,8 @@ void ast_bridge_publish_attended_transfer_threeway(int is_external, enum ast_tra return; } - transfer_msg = attended_transfer_message_create(is_external, result, transferee, target); + transfer_msg = attended_transfer_message_create(is_external, result, + transferee, target, NULL, transferee_channel, target_channel); if (!transfer_msg) { return; } @@ -1056,7 +1153,8 @@ void ast_bridge_publish_attended_transfer_threeway(int is_external, enum ast_tra void ast_bridge_publish_attended_transfer_app(int is_external, enum ast_transfer_result result, struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target, - const char *dest_app) + struct ast_channel *replace_channel, const char *dest_app, + struct ast_channel *transferee_channel, struct ast_channel *target_channel) { RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); @@ -1065,12 +1163,13 @@ void ast_bridge_publish_attended_transfer_app(int is_external, enum ast_transfer return; } - transfer_msg = attended_transfer_message_create(is_external, result, transferee, target); + transfer_msg = attended_transfer_message_create(is_external, result, + transferee, target, replace_channel, transferee_channel, target_channel); if (!transfer_msg) { return; } - transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_APP; + transfer_msg->dest_type = replace_channel ? AST_ATTENDED_TRANSFER_DEST_LOCAL_APP : AST_ATTENDED_TRANSFER_DEST_APP; ast_copy_string(transfer_msg->dest.app, dest_app, sizeof(transfer_msg->dest.app)); msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg); @@ -1083,7 +1182,8 @@ void ast_bridge_publish_attended_transfer_app(int is_external, enum ast_transfer void ast_bridge_publish_attended_transfer_link(int is_external, enum ast_transfer_result result, struct ast_bridge_channel_pair *transferee, struct ast_bridge_channel_pair *target, - struct ast_channel *locals[2]) + struct ast_channel *locals[2], struct ast_channel *transferee_channel, + struct ast_channel *target_channel) { RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup); RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup); @@ -1093,7 +1193,8 @@ void ast_bridge_publish_attended_transfer_link(int is_external, enum ast_transfe return; } - transfer_msg = attended_transfer_message_create(is_external, result, transferee, target); + transfer_msg = attended_transfer_message_create(is_external, result, + transferee, target, NULL, transferee_channel, target_channel); if (!transfer_msg) { return; } |