Add stasis publications for blind and attended transfers.

This creates stasis messages that are sent during a blind or
attended transfer. The stasis messages also are converted to
AMI events.

Review: https://reviewboard.asterisk.org/r/2619

(closes issue ASTERISK-21337)
Reported by Matt Jordan



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393182 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Mark Michelson
2013-06-28 18:42:24 +00:00
parent ca61a05506
commit 6d624eb008
11 changed files with 1103 additions and 44 deletions

View File

@@ -41,6 +41,321 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#define SNAPSHOT_CHANNELS_BUCKETS 13
/*** DOCUMENTATION
<managerEvent language="en_US" name="BlindTransfer">
<managerEventInstance class="EVENT_FLAG_CALL">
<synopsis>Raised when a blind transfer is complete.</synopsis>
<syntax>
<parameter name="Result">
<para>Indicates if the transfer was successful or if it failed.</para>
<enumlist>
<enum name="Fail"><para>An internal error occurred.</para></enum>
<enum name="Invalid"><para>Invalid configuration for transfer (e.g. Not bridged)</para></enum>
<enum name="Not Permitted"><para>Bridge does not permit transfers</para></enum>
<enum name="Success"><para>Transfer completed successfully</para></enum>
</enumlist>
<note><para>A result of <literal>Success</literal> does not necessarily mean that a target was succesfully
contacted. It means that a party was succesfully placed into the dialplan at the expected location.</para></note>
</parameter>
<parameter name="TransfererChannel">
<para>The name of the channel that performed the transfer</para>
</parameter>
<parameter name="TransfererChannelStateDesc">
<enumlist>
<enum name="Down"/>
<enum name="Rsrvd"/>
<enum name="OffHook"/>
<enum name="Dialing"/>
<enum name="Ring"/>
<enum name="Ringing"/>
<enum name="Up"/>
<enum name="Busy"/>
<enum name="Dialing Offhook"/>
<enum name="Pre-ring"/>
<enum name="Unknown"/>
</enumlist>
</parameter>
<parameter name="TransfererCallerIDNum">
</parameter>
<parameter name="TransfererCallerIDName">
</parameter>
<parameter name="TransfererConnectedLineNum">
</parameter>
<parameter name="TransfererConnectedLineName">
</parameter>
<parameter name="TransfererAccountCode">
</parameter>
<parameter name="TransfererContext">
</parameter>
<parameter name="TransfererExten">
</parameter>
<parameter name="TransfererPriority">
</parameter>
<parameter name="TransfererUniqueid">
</parameter>
<parameter name="BridgeUniqueid">
<para>The ID of the bridge where the Transferer performed the transfer</para>
</parameter>
<parameter name="BridgeType">
<para>The type of the bridge where the Transferer performed the transfer</para>
</parameter>
<parameter name="IsExternal">
<para>Indicates if the transfer was performed outside of Asterisk. For instance,
a channel protocol native transfer is external. A DTMF transfer is internal.</para>
<enumlist>
<enum name="Yes" />
<enum name="No" />
</enumlist>
</parameter>
<parameter name="Context">
<para>Destination context for the blind transfer.</para>
</parameter>
<parameter name="Extension">
<para>Destination extension for the blind transfer.</para>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="AttendedTransfer">
<managerEventInstance class="EVENT_FLAG_CALL">
<synopsis>Raised when an attended transfer is complete.</synopsis>
<syntax>
<xi:include xpointer="xpointer(docs/managerEvent[@name='BlindTransfer']/managerEventInstance/syntax/parameter[@name='Result'])" />
<parameter name="OrigTransfererChannel">
<para>The original transferer channel that performed the attended transfer.</para>
</parameter>
<parameter name="OrigTransfererChannelState">
<para>A numeric code for the channel's current state, related to DestChannelStateDesc</para>
</parameter>
<parameter name="OrigTransfererChannelStateDesc">
<enumlist>
<enum name="Down"/>
<enum name="Rsrvd"/>
<enum name="OffHook"/>
<enum name="Dialing"/>
<enum name="Ring"/>
<enum name="Ringing"/>
<enum name="Up"/>
<enum name="Busy"/>
<enum name="Dialing Offhook"/>
<enum name="Pre-ring"/>
<enum name="Unknown"/>
</enumlist>
</parameter>
<parameter name="OrigTransfererCallerIDNum">
</parameter>
<parameter name="OrigTransfererCallerIDName">
</parameter>
<parameter name="OrigTransfererConnectedLineNum">
</parameter>
<parameter name="OrigTransfererConnectedLineName">
</parameter>
<parameter name="OrigTransfererAccountCode">
</parameter>
<parameter name="OrigTransfererContext">
</parameter>
<parameter name="OrigTransfererExten">
</parameter>
<parameter name="OrigTransfererPriority">
</parameter>
<parameter name="OrigTransfererUniqueid">
</parameter>
<parameter name="BridgeUniqueidOrig">
<para>The ID of the bridge where the Transferer performed the transfer</para>
<note><para>This header will not be present if the original transferer was not in a bridge.</para></note>
</parameter>
<parameter name="BridgeTypeOrig">
<para>The type of the bridge where the Transferer performed the transfer</para>
<note><para>This header will not be present if the original transferer was not in a bridge.</para></note>
</parameter>
<parameter name="SecondTransfererChannel">
<para>The second transferer channel involved in the attended transfer.</para>
</parameter>
<parameter name="SecondTransfererChannelState">
<para>A numeric code for the channel's current state, related to SecondTransfererChannelStateDesc</para>
</parameter>
<parameter name="SecondTransfererChannelStateDesc">
<enumlist>
<enum name="Down"/>
<enum name="Rsrvd"/>
<enum name="OffHook"/>
<enum name="Dialing"/>
<enum name="Ring"/>
<enum name="Ringing"/>
<enum name="Up"/>
<enum name="Busy"/>
<enum name="Dialing Offhook"/>
<enum name="Pre-ring"/>
<enum name="Unknown"/>
</enumlist>
</parameter>
<parameter name="SecondTransfererCallerIDNum">
</parameter>
<parameter name="SecondTransfererCallerIDName">
</parameter>
<parameter name="SecondTransfererConnectedLineNum">
</parameter>
<parameter name="SecondTransfererConnectedLineName">
</parameter>
<parameter name="SecondTransfererAccountCode">
</parameter>
<parameter name="SecondTransfererContext">
</parameter>
<parameter name="SecondTransfererExten">
</parameter>
<parameter name="SecondTransfererPriority">
</parameter>
<parameter name="SecondTransfererUniqueid">
</parameter>
<parameter name="BridgeUniqueidSecond">
<para>The unique ID of the bridge that the second transferer channel was in, or <literal>None</literal> if the second transferer channel was not bridged</para>
<note><para>This header will not be present if the second transferer was not in a bridge.</para></note>
</parameter>
<parameter name="BridgeTypeSecond">
<para>The type of the bridge where the Transferer performed the transfer</para>
<note><para>This header will not be present if the second transferer was not in a bridge.</para></note>
</parameter>
<parameter name="DestType">
<para>Indicates the method by which the attended transfer completed.</para>
<enumlist>
<enum name="Bridge"><para>The transfer was accomplished by merging two bridges into one.</para></enum>
<enum name="App"><para>The transfer was accomplished by having a channel or bridge run a dialplan application.</para></enum>
<enum name="Link"><para>The transfer was accomplished by linking two bridges together using a local channel pair.</para></enum>
<enum name="Fail"><para>The transfer failed.</para></enum>
</enumlist>
</parameter>
<parameter name="DestBridgeUniqueid">
<para>Indicates the surviving bridge when bridges were merged to complete the transfer</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Bridge</literal></para></note>
</parameter>
<parameter name="DestApp">
<para>Indicates the application that is running when the transfer completes</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>App</literal></para></note>
</parameter>
<parameter name="LocalOneChannel">
<para>The local channel that is bridged with the original bridge when forming a link between bridges</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneChannelState">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneChannelStateDesc">
<enumlist>
<enum name="Down"/>
<enum name="Rsrvd"/>
<enum name="OffHook"/>
<enum name="Dialing"/>
<enum name="Ring"/>
<enum name="Ringing"/>
<enum name="Up"/>
<enum name="Busy"/>
<enum name="Dialing Offhook"/>
<enum name="Pre-ring"/>
<enum name="Unknown"/>
</enumlist>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneCallerIDNum">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneCallerIDName">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneConnectedLineNum">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneConnectedLineName">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneAccountCode">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneContext">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneExten">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOnePriority">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalOneUniqueid">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoChannel">
<para>The local channel that is bridged with the second bridge when forming a link between bridges</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoChannelState">
<para>A numeric code for the channel's current state, related to LocalTwoChannelStateDesc</para>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoChannelStateDesc">
<enumlist>
<enum name="Down"/>
<enum name="Rsrvd"/>
<enum name="OffHook"/>
<enum name="Dialing"/>
<enum name="Ring"/>
<enum name="Ringing"/>
<enum name="Up"/>
<enum name="Busy"/>
<enum name="Dialing Offhook"/>
<enum name="Pre-ring"/>
<enum name="Unknown"/>
</enumlist>
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoCallerIDNum">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoCallerIDName">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoConnectedLineNum">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoConnectedLineName">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoAccountCode">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoContext">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoExten">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoPriority">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
<parameter name="LocalTwoUniqueid">
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
</parameter>
</syntax>
<description>
<para>The headers in this event attempt to describe all the major details of the attended transfer. The two transferer channels
and the two bridges are determined based on their chronological establishment. So consider that Alice calls Bob, and then Alice
transfers the call to Voicemail. The transferer and bridge headers would be arranged as follows:</para>
<para> <replaceable>OrigTransfererChannel</replaceable>: Alice's channel in the bridge with Bob.</para>
<para> <replaceable>BridgeUniqueidOrig</replaceable>: The bridge between Alice and Bob.</para>
<para> <replaceable>SecondTransfererChannel</replaceable>: Alice's channel that called Voicemail.</para>
<para> <replaceable>BridgeUniqueidSecond</replaceable>: Not present, since a call to Voicemail has no bridge.</para>
<para>Now consider if the order were reversed; instead of having Alice call Bob and transfer him to Voicemail, Alice instead
calls her Voicemail and transfers that to Bob. The transferer and bridge headers would be arranged as follows:</para>
<para> <replaceable>OrigTransfererChannel</replaceable>: Alice's channel that called Voicemail.</para>
<para> <replaceable>BridgeUniqueidOrig</replaceable>: Not present, since a call to Voicemail has no bridge.</para>
<para> <replaceable>SecondTransfererChannel</replaceable>: Alice's channel in the bridge with Bob.</para>
<para> <replaceable>BridgeUniqueidSecond</replaceable>: The bridge between Alice and Bob.</para>
</description>
</managerEventInstance>
</managerEvent>
***/
static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_message *message);
static struct ast_manager_event_blob *blind_transfer_to_ami(struct stasis_message *message);
/*!
* @{ \brief Define bridge message types.
*/
@@ -48,6 +363,8 @@ STASIS_MESSAGE_TYPE_DEFN(ast_bridge_snapshot_type);
STASIS_MESSAGE_TYPE_DEFN(ast_bridge_merge_message_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_entered_bridge_type);
STASIS_MESSAGE_TYPE_DEFN(ast_channel_left_bridge_type);
STASIS_MESSAGE_TYPE_DEFN(ast_blind_transfer_type, .to_ami = blind_transfer_to_ami);
STASIS_MESSAGE_TYPE_DEFN(ast_attended_transfer_type, .to_ami = attended_transfer_to_ami);
/*! @} */
/*! \brief Aggregate topic for bridge messages */
@@ -352,6 +669,330 @@ struct ast_json *ast_bridge_snapshot_to_json(const struct ast_bridge_snapshot *s
return ast_json_ref(json_bridge);
}
/*!
* \internal
* \brief Allocate the fields of an \ref ast_bridge_channel_snapshot_pair.
*
* \param pair A bridge and channel to get snapshots of
* \param[out] snapshot_pair An allocated snapshot pair.
* \retval 0 Success
* \retval non-zero Failure
*/
static int bridge_channel_snapshot_pair_init(struct ast_bridge_channel_pair *pair, struct ast_bridge_channel_snapshot_pair *snapshot_pair)
{
if (pair->bridge) {
snapshot_pair->bridge_snapshot = ast_bridge_snapshot_create(pair->bridge);
if (!snapshot_pair->bridge_snapshot) {
return -1;
}
}
snapshot_pair->channel_snapshot = ast_channel_snapshot_create(pair->channel);
if (!snapshot_pair->channel_snapshot) {
return -1;
}
return 0;
}
/*!
* \internal
* \brief Free the fields of an \ref ast_bridge_channel_snapshot_pair.
*
* \param pair The snapshot pair whose fields are to be cleaned up
*/
static void bridge_channel_snapshot_pair_cleanup(struct ast_bridge_channel_snapshot_pair *pair)
{
ao2_cleanup(pair->bridge_snapshot);
ao2_cleanup(pair->channel_snapshot);
}
static const char *result_strs[] = {
[AST_BRIDGE_TRANSFER_FAIL] = "Fail",
[AST_BRIDGE_TRANSFER_INVALID] = "Invalid",
[AST_BRIDGE_TRANSFER_NOT_PERMITTED] = "Not Permitted",
[AST_BRIDGE_TRANSFER_SUCCESS] = "Success",
};
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 *, 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;
if (!blob) {
return NULL;
}
channel_state = ast_manager_build_channel_state_string_prefix(blob->channel, "Transferer");
bridge_state = ast_manager_build_bridge_state_string(blob->bridge, "");
if (!channel_state || !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"));
return ast_manager_event_blob_create(EVENT_FLAG_CALL, "BlindTransfer",
"Result: %s\r\n"
"%s"
"%s"
"IsExternal: %s\r\n"
"Context: %s\r\n"
"Extension: %s\r\n",
result_strs[result],
ast_str_buffer(channel_state),
ast_str_buffer(bridge_state),
is_external ? "Yes" : "No",
context,
exten);
}
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)
{
RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
json_object = ast_json_pack("{s: s, s: s, s: i, s: i}",
"context", context, "exten", exten, "result", result, "is_external", is_external);
if (!json_object) {
ast_log(LOG_NOTICE, "Failed to create json bridge blob\n");
return;
}
msg = ast_bridge_blob_create(ast_blind_transfer_type(),
transferer->bridge, transferer->channel, json_object);
if (!msg) {
ast_log(LOG_NOTICE, "Failed to create blob msg\n");
return;
}
stasis_publish(ast_bridge_topic_all(), msg);
}
static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_message *msg)
{
RAII_VAR(struct ast_str *, variable_data, ast_str_create(64), ast_free_ptr);
RAII_VAR(struct ast_str *, transferer1_state, NULL, ast_free_ptr);
RAII_VAR(struct ast_str *, bridge1_state, NULL, ast_free_ptr);
RAII_VAR(struct ast_str *, transferer2_state, NULL, ast_free_ptr);
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);
struct ast_attended_transfer_message *transfer_msg = stasis_message_data(msg);
if (!variable_data) {
return NULL;
}
transferer1_state = ast_manager_build_channel_state_string_prefix(transfer_msg->to_transferee.channel_snapshot, "OrigTransferer");
transferer2_state = ast_manager_build_channel_state_string_prefix(transfer_msg->to_transfer_target.channel_snapshot, "SecondTransferer");
if (!transferer1_state || !transferer2_state) {
return NULL;
}
if (transfer_msg->to_transferee.bridge_snapshot) {
bridge1_state = ast_manager_build_bridge_state_string(transfer_msg->to_transferee.bridge_snapshot, "Orig");
if (!bridge1_state) {
return NULL;
}
}
if (transfer_msg->to_transfer_target.bridge_snapshot) {
bridge2_state = ast_manager_build_bridge_state_string(transfer_msg->to_transfer_target.bridge_snapshot, "Second");
if (!bridge2_state) {
return NULL;
}
}
switch (transfer_msg->dest_type) {
case AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE:
ast_str_append(&variable_data, 0, "DestType: Bridge\r\n");
ast_str_append(&variable_data, 0, "DestBridgeUniqueid: %s\r\n", transfer_msg->dest.bridge);
break;
case AST_ATTENDED_TRANSFER_DEST_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;
case AST_ATTENDED_TRANSFER_DEST_LINK:
local1_state = ast_manager_build_channel_state_string_prefix(transfer_msg->dest.links[0], "LocalOne");
local2_state = ast_manager_build_channel_state_string_prefix(transfer_msg->dest.links[1], "LocalTwo");
if (!local1_state || !local2_state) {
return NULL;
}
ast_str_append(&variable_data, 0, "DestType: Link\r\n");
ast_str_append(&variable_data, 0, "%s", ast_str_buffer(local1_state));
ast_str_append(&variable_data, 0, "%s", ast_str_buffer(local2_state));
break;
case AST_ATTENDED_TRANSFER_DEST_FAIL:
ast_str_append(&variable_data, 0, "DestType: Fail\r\n");
break;
}
return ast_manager_event_blob_create(EVENT_FLAG_CALL, "AttendedTransfer",
"Result: %s\r\n"
"%s"
"%s"
"%s"
"%s"
"IsExternal: %s\r\n"
"%s\r\n",
result_strs[transfer_msg->result],
ast_str_buffer(transferer1_state),
bridge1_state ? ast_str_buffer(bridge1_state) : "",
ast_str_buffer(transferer2_state),
bridge2_state ? ast_str_buffer(bridge2_state) : "",
transfer_msg->is_external ? "Yes" : "No",
ast_str_buffer(variable_data));
}
static void attended_transfer_dtor(void *obj)
{
struct ast_attended_transfer_message *msg = obj;
int i;
bridge_channel_snapshot_pair_cleanup(&msg->to_transferee);
bridge_channel_snapshot_pair_cleanup(&msg->to_transfer_target);
if (msg->dest_type != AST_ATTENDED_TRANSFER_DEST_LINK) {
return;
}
for (i = 0; i < ARRAY_LEN(msg->dest.links); ++i) {
ao2_cleanup(msg->dest.links[i]);
}
}
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)
{
RAII_VAR(struct ast_attended_transfer_message *, msg, NULL, ao2_cleanup);
msg = ao2_alloc(sizeof(*msg), attended_transfer_dtor);
if (!msg) {
return NULL;
}
if (bridge_channel_snapshot_pair_init(transferee, &msg->to_transferee) ||
bridge_channel_snapshot_pair_init(target, &msg->to_transfer_target)) {
return NULL;
}
msg->is_external = is_external;
msg->result = result;
ao2_ref(msg, +1);
return msg;
}
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)
{
RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
transfer_msg = attended_transfer_message_create(is_external, result, transferee, target);
if (!transfer_msg) {
return;
}
transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_FAIL;
msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
if (!msg) {
return;
}
stasis_publish(ast_bridge_topic_all(), msg);
}
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)
{
RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
transfer_msg = attended_transfer_message_create(is_external, result, transferee, target);
if (!transfer_msg) {
return;
}
transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_BRIDGE_MERGE;
ast_copy_string(transfer_msg->dest.bridge, final_bridge->uniqueid,
sizeof(transfer_msg->dest.bridge));
msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
if (!msg) {
return;
}
stasis_publish(ast_bridge_topic_all(), msg);
}
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)
{
RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
transfer_msg = attended_transfer_message_create(is_external, result, transferee, target);
if (!transfer_msg) {
return;
}
transfer_msg->dest_type = 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);
if (!msg) {
return;
}
stasis_publish(ast_bridge_topic_all(), msg);
}
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])
{
RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
int i;
transfer_msg = attended_transfer_message_create(is_external, result, transferee, target);
if (!transfer_msg) {
return;
}
transfer_msg->dest_type = AST_ATTENDED_TRANSFER_DEST_LINK;
for (i = 0; i < 2; ++i) {
transfer_msg->dest.links[i] = ast_channel_snapshot_create(locals[i]);
if (!transfer_msg->dest.links[i]) {
return;
}
}
msg = stasis_message_create(ast_attended_transfer_type(), transfer_msg);
if (!msg) {
return;
}
stasis_publish(ast_bridge_topic_all(), msg);
}
struct ast_bridge_snapshot *ast_bridge_snapshot_get_latest(const char *uniqueid)
{
RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
@@ -387,6 +1028,8 @@ static void stasis_bridging_cleanup(void)
STASIS_MESSAGE_TYPE_CLEANUP(ast_bridge_merge_message_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_entered_bridge_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_channel_left_bridge_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_blind_transfer_type);
STASIS_MESSAGE_TYPE_CLEANUP(ast_attended_transfer_type);
}
/*! \brief snapshot ID getter for caching topic */
@@ -408,9 +1051,12 @@ int ast_stasis_bridging_init(void)
STASIS_MESSAGE_TYPE_INIT(ast_bridge_merge_message_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_entered_bridge_type);
STASIS_MESSAGE_TYPE_INIT(ast_channel_left_bridge_type);
STASIS_MESSAGE_TYPE_INIT(ast_blind_transfer_type);
STASIS_MESSAGE_TYPE_INIT(ast_attended_transfer_type);
bridge_topic_all = stasis_topic_create("ast_bridge_topic_all");
bridge_topic_all_cached = stasis_caching_topic_create(bridge_topic_all, bridge_snapshot_get_id);
bridge_topic_pool = stasis_topic_pool_create(bridge_topic_all);
return !bridge_topic_all
|| !bridge_topic_all_cached
|| !bridge_topic_pool ? -1 : 0;