|
|
|
@ -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;
|
|
|
|
|