mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-18 10:44:28 +00:00
Make DTMF attended transfer support feature-complete.
This greatly modifies the operation of DTMF attended transfers so that the full range of options from features.conf applies. In addition, a new option has been added that allows for a transferer to switch between bridges during a transfer before completing the transfer. (closes issue ASTERISK-21543) reported by Matt Jordan Review: https://reviewboard.asterisk.org/r/2654 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@395151 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
@@ -63,6 +63,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
#include "asterisk/core_local.h"
|
||||
#include "asterisk/core_unreal.h"
|
||||
#include "asterisk/features_config.h"
|
||||
#include "asterisk/bridging_internal.h"
|
||||
|
||||
/*! All bridges container. */
|
||||
static struct ao2_container *bridges;
|
||||
@@ -77,7 +78,6 @@ static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
|
||||
|
||||
static void cleanup_video_mode(struct ast_bridge *bridge);
|
||||
static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
|
||||
static void bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags remove_flags);
|
||||
|
||||
/*! Default DTMF keys for built in features */
|
||||
static char builtin_features_dtmf[AST_BRIDGE_BUILTIN_END][MAXIMUM_DTMF_FEATURE_STRING];
|
||||
@@ -463,19 +463,7 @@ void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channe
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Helper function to find a bridge channel given a channel.
|
||||
*
|
||||
* \param bridge What to search
|
||||
* \param chan What to search for.
|
||||
*
|
||||
* \note On entry, bridge is already locked.
|
||||
*
|
||||
* \retval bridge_channel if channel is in the bridge.
|
||||
* \retval NULL if not in bridge.
|
||||
*/
|
||||
static struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan)
|
||||
struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan)
|
||||
{
|
||||
struct ast_bridge_channel *bridge_channel;
|
||||
|
||||
@@ -757,7 +745,7 @@ static int bridge_channel_push(struct ast_bridge_channel *bridge_channel)
|
||||
|| ast_bridge_channel_establish_roles(bridge_channel)) {
|
||||
ast_debug(1, "Bridge %s: pushing %p(%s) into bridge failed\n",
|
||||
bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
|
||||
bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
||||
ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
||||
return -1;
|
||||
}
|
||||
bridge_channel->in_bridge = 1;
|
||||
@@ -1719,7 +1707,7 @@ static int bridge_base_push(struct ast_bridge *self, struct ast_bridge_channel *
|
||||
*/
|
||||
static void bridge_base_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
||||
ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -4164,24 +4152,7 @@ static void bridge_channel_change_bridge(struct ast_bridge_channel *bridge_chann
|
||||
ao2_ref(old_bridge, -1);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Do the merge of two bridges.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param dst_bridge Destination bridge of merge.
|
||||
* \param src_bridge Source bridge of merge.
|
||||
* \param kick_me Array of channels to kick from the bridges.
|
||||
* \param num_kick Number of channels in the kick_me array.
|
||||
*
|
||||
* \return Nothing
|
||||
*
|
||||
* \note The two bridges are assumed already locked.
|
||||
*
|
||||
* This moves the channels in src_bridge into the bridge pointed
|
||||
* to by dst_bridge.
|
||||
*/
|
||||
static void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick,
|
||||
void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick,
|
||||
unsigned int optimized)
|
||||
{
|
||||
struct ast_bridge_channel *bridge_channel;
|
||||
@@ -4440,21 +4411,7 @@ int ast_bridge_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg
|
||||
return res;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Move a bridge channel from one bridge to another.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param dst_bridge Destination bridge of bridge channel move.
|
||||
* \param bridge_channel Channel moving from one bridge to another.
|
||||
* \param attempt_recovery TRUE if failure attempts to push channel back into original bridge.
|
||||
*
|
||||
* \note The dst_bridge and bridge_channel->bridge are assumed already locked.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on failure.
|
||||
*/
|
||||
static int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery,
|
||||
int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery,
|
||||
unsigned int optimized)
|
||||
{
|
||||
struct ast_bridge *orig_bridge;
|
||||
@@ -5646,17 +5603,7 @@ static void hooks_remove_heap(struct ast_heap *hooks, enum ast_bridge_hook_remov
|
||||
ast_heap_unlock(hooks);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Remove marked bridge channel feature hooks.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param features Bridge features structure
|
||||
* \param remove_flags Determinator for whether hook is removed.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags remove_flags)
|
||||
void ast_bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags remove_flags)
|
||||
{
|
||||
hooks_remove_container(features->dtmf_hooks, remove_flags);
|
||||
hooks_remove_container(features->hangup_hooks, remove_flags);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -374,6 +374,26 @@ int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *
|
||||
return setup_bridge_role_option(role, option, value);
|
||||
}
|
||||
|
||||
int ast_channel_has_role(struct ast_channel *channel, const char *role_name)
|
||||
{
|
||||
return get_role_from_channel(channel, role_name) ? 1 : 0;
|
||||
}
|
||||
|
||||
const char *ast_channel_get_role_option(struct ast_channel *channel, const char *role_name, const char *option)
|
||||
{
|
||||
struct bridge_role *role;
|
||||
struct bridge_role_option *role_option;
|
||||
|
||||
role = get_role_from_channel(channel, role_name);
|
||||
if (!role) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
role_option = get_role_option(role, option);
|
||||
|
||||
return role_option ? role_option->value : NULL;
|
||||
}
|
||||
|
||||
int ast_bridge_channel_has_role(struct ast_bridge_channel *bridge_channel, const char *role_name)
|
||||
{
|
||||
if (!bridge_channel->bridge_roles) {
|
||||
|
@@ -1433,9 +1433,10 @@ static void cel_attended_transfer_cb(
|
||||
switch (xfer->dest_type) {
|
||||
case AST_ATTENDED_TRANSFER_DEST_FAIL:
|
||||
return;
|
||||
/* handle these two the same */
|
||||
/* handle these three the same */
|
||||
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}",
|
||||
"bridge1_id", bridge1->uniqueid,
|
||||
"channel2_name", channel2->name,
|
||||
|
@@ -3212,37 +3212,37 @@ static int setup_bridge_features_builtin(struct ast_bridge_features *features, s
|
||||
if (!builtin_feature_get_exten(chan, "blindxfer", dtmf, sizeof(dtmf))
|
||||
&& !ast_strlen_zero(dtmf)) {
|
||||
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_BLINDTRANSFER, dtmf,
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
|
||||
}
|
||||
if (!builtin_feature_get_exten(chan, "atxfer", dtmf, sizeof(dtmf)) &&
|
||||
!ast_strlen_zero(dtmf)) {
|
||||
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, dtmf,
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
|
||||
}
|
||||
}
|
||||
if (ast_test_flag(flags, AST_FEATURE_DISCONNECT) &&
|
||||
!builtin_feature_get_exten(chan, "disconnect", dtmf, sizeof(dtmf)) &&
|
||||
!ast_strlen_zero(dtmf)) {
|
||||
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_HANGUP, dtmf,
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
|
||||
}
|
||||
if (ast_test_flag(flags, AST_FEATURE_PARKCALL) &&
|
||||
!builtin_feature_get_exten(chan, "parkcall", dtmf, sizeof(dtmf)) &&
|
||||
!ast_strlen_zero(dtmf)) {
|
||||
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_PARKCALL, dtmf,
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
|
||||
}
|
||||
if (ast_test_flag(flags, AST_FEATURE_AUTOMON) &&
|
||||
!builtin_feature_get_exten(chan, "automon", dtmf, sizeof(dtmf)) &&
|
||||
!ast_strlen_zero(dtmf)) {
|
||||
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_AUTOMON, dtmf,
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
|
||||
}
|
||||
if (ast_test_flag(flags, AST_FEATURE_AUTOMIXMON) &&
|
||||
!builtin_feature_get_exten(chan, "automixmon", dtmf, sizeof(dtmf)) &&
|
||||
!ast_strlen_zero(dtmf)) {
|
||||
res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_AUTOMIXMON, dtmf,
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
|
||||
NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
|
||||
}
|
||||
|
||||
return res ? -1 : 0;
|
||||
|
@@ -106,6 +106,16 @@
|
||||
the transferees, and the transfer target all being in a single bridge together.</para>
|
||||
</description>
|
||||
</configOption>
|
||||
<configOption name="atxferswap" default="*4">
|
||||
<synopsis>Digits to dial to toggle who the transferrer is currently bridged to during an attended transfer</synopsis>
|
||||
<description>
|
||||
<para>This option is only available to the transferrer during an attended
|
||||
transfer operation. Pressing this DTMF sequence will result in the transferrer swapping
|
||||
which party he is bridged with. For instance, if the transferrer is currently bridged with
|
||||
the transfer target, then pressing this DTMF sequence will cause the transferrer to be
|
||||
bridged with the transferees.</para>
|
||||
</description>
|
||||
</configOption>
|
||||
<configOption name="pickupexten" default="*8">
|
||||
<synopsis>Digits used for picking up ringing calls</synopsis>
|
||||
<description>
|
||||
@@ -355,6 +365,7 @@
|
||||
#define DEFAULT_ATXFER_ABORT "*1"
|
||||
#define DEFAULT_ATXFER_COMPLETE "*2"
|
||||
#define DEFAULT_ATXFER_THREEWAY "*3"
|
||||
#define DEFAULT_ATXFER_SWAP "*4"
|
||||
|
||||
/*! Default pickup options */
|
||||
#define DEFAULT_PICKUPEXTEN "*8"
|
||||
@@ -846,6 +857,8 @@ static int xfer_set(struct ast_features_xfer_config *xfer, const char *name,
|
||||
ast_string_field_set(xfer, atxfercomplete, value);
|
||||
} else if (!strcasecmp(name, "atxferthreeway")) {
|
||||
ast_string_field_set(xfer, atxferthreeway, value);
|
||||
} else if (!strcasecmp(name, "atxferswap")) {
|
||||
ast_string_field_set(xfer, atxferswap, value);
|
||||
} else {
|
||||
/* Unrecognized option */
|
||||
res = -1;
|
||||
@@ -879,6 +892,8 @@ static int xfer_get(struct ast_features_xfer_config *xfer, const char *field,
|
||||
ast_copy_string(buf, xfer->atxfercomplete, len);
|
||||
} else if (!strcasecmp(field, "atxferthreeway")) {
|
||||
ast_copy_string(buf, xfer->atxferthreeway, len);
|
||||
} else if (!strcasecmp(field, "atxferswap")) {
|
||||
ast_copy_string(buf, xfer->atxferswap, len);
|
||||
} else {
|
||||
/* Unrecognized option */
|
||||
res = -1;
|
||||
@@ -1629,6 +1644,8 @@ static int load_config(void)
|
||||
DEFAULT_ATXFER_COMPLETE, xfer_handler, 0);
|
||||
aco_option_register_custom(&cfg_info, "atxferthreeway", ACO_EXACT, global_options,
|
||||
DEFAULT_ATXFER_THREEWAY, xfer_handler, 0);
|
||||
aco_option_register_custom(&cfg_info, "atxferswap", ACO_EXACT, global_options,
|
||||
DEFAULT_ATXFER_SWAP, xfer_handler, 0);
|
||||
|
||||
aco_option_register_custom(&cfg_info, "pickupexten", ACO_EXACT, global_options,
|
||||
DEFAULT_PICKUPEXTEN, pickup_handler, 0);
|
||||
|
@@ -221,12 +221,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
<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="Threeway"><para>The transfer was accomplished by placing all parties into a threeway call.</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>
|
||||
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Bridge</literal> or <literal>Threeway</literal></para></note>
|
||||
</parameter>
|
||||
<parameter name="DestApp">
|
||||
<para>Indicates the application that is running when the transfer completes</para>
|
||||
@@ -333,6 +334,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
<parameter name="LocalTwoUniqueid">
|
||||
<note><para>This header is only present when <replaceable>DestType</replaceable> is <literal>Link</literal></para></note>
|
||||
</parameter>
|
||||
<parameter name="DestTransfererChannel">
|
||||
<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>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>The headers in this event attempt to describe all the major details of the attended transfer. The two transferer channels
|
||||
@@ -835,6 +840,11 @@ static struct ast_manager_event_blob *attended_transfer_to_ami(struct stasis_mes
|
||||
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_THREEWAY:
|
||||
ast_str_append(&variable_data, 0, "DestType: Threeway\r\n");
|
||||
ast_str_append(&variable_data, 0, "DestBridgeUniqueid: %s\r\n", transfer_msg->dest.threeway.bridge_snapshot->uniqueid);
|
||||
ast_str_append(&variable_data, 0, "DestTransfererChannel: %s\r\n", transfer_msg->dest.threeway.channel_snapshot->name);
|
||||
break;
|
||||
case AST_ATTENDED_TRANSFER_DEST_FAIL:
|
||||
ast_str_append(&variable_data, 0, "DestType: Fail\r\n");
|
||||
break;
|
||||
@@ -941,6 +951,39 @@ void ast_bridge_publish_attended_transfer_bridge_merge(int is_external, enum ast
|
||||
stasis_publish(ast_bridge_topic_all(), msg);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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_THREEWAY;
|
||||
if (final_pair->channel == transferee->channel) {
|
||||
transfer_msg->dest.threeway.channel_snapshot = transfer_msg->to_transferee.channel_snapshot;
|
||||
} else {
|
||||
transfer_msg->dest.threeway.channel_snapshot = transfer_msg->to_transfer_target.channel_snapshot;
|
||||
}
|
||||
|
||||
if (final_pair->bridge == transferee->bridge) {
|
||||
transfer_msg->dest.threeway.bridge_snapshot = transfer_msg->to_transferee.bridge_snapshot;
|
||||
} else {
|
||||
transfer_msg->dest.threeway.bridge_snapshot = transfer_msg->to_transfer_target.bridge_snapshot;
|
||||
}
|
||||
|
||||
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)
|
||||
|
Reference in New Issue
Block a user