Skip to content
Snippets Groups Projects
bridge_basic.c 119 KiB
Newer Older
/*
 * Asterisk -- An open source telephony toolkit.
 *
 * Copyright (C) 2013 Digium, Inc.
 *
 * Richard Mudgett <rmudgett@digium.com>
 *
 * See http://www.asterisk.org for more information about
 * the Asterisk project. Please do not directly contact
 * any of the maintainers of this project for assistance;
 * the project provides a web site, mailing lists and IRC
 * channels for your use.
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License Version 2. See the LICENSE file
 * at the top of the source tree.
 */

/*!
 * \file
 * \brief Basic bridge class.  It is a subclass of struct ast_bridge.
 *
 * \author Richard Mudgett <rmudgett@digium.com>
 *
 * See Also:
 * \arg \ref AstCREDITS
 */


#include "asterisk.h"

ASTERISK_FILE_VERSION(__FILE__, "$Revision$")

#include "asterisk/channel.h"
#include "asterisk/utils.h"
#include "asterisk/linkedlists.h"
#include "asterisk/bridge.h"
#include "asterisk/bridge_internal.h"
#include "asterisk/bridge_basic.h"
#include "asterisk/bridge_after.h"
#include "asterisk/features_config.h"
#include "asterisk/pbx.h"
#include "asterisk/file.h"
#include "asterisk/app.h"
#include "asterisk/dial.h"
#include "asterisk/stasis_bridges.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/features.h"

#define NORMAL_FLAGS	(AST_BRIDGE_FLAG_DISSOLVE_HANGUP | AST_BRIDGE_FLAG_DISSOLVE_EMPTY \
			| AST_BRIDGE_FLAG_SMART)

#define TRANSFER_FLAGS AST_BRIDGE_FLAG_SMART

struct attended_transfer_properties;

enum bridge_basic_personality_type {
	/*! Index for "normal" basic bridge personality */
	BRIDGE_BASIC_PERSONALITY_NORMAL,
	/*! Index for attended transfer basic bridge personality */
	BRIDGE_BASIC_PERSONALITY_ATXFER,
	/*! Indicates end of enum. Must always remain the last element */
	BRIDGE_BASIC_PERSONALITY_END,
};

/*!
 * \brief Change basic bridge personality
 *
 * Changing personalities allows for the bridge to remain in use but have
 * properties such as its v_table and its flags change.
 *
 * \param bridge The bridge
 * \param type The personality to change the bridge to
 * \user_data Private data to attach to the personality.
 */
static void bridge_basic_change_personality(struct ast_bridge *bridge,
		enum bridge_basic_personality_type type, void *user_data);

/* ------------------------------------------------------------------- */

static const struct ast_datastore_info dtmf_features_info = {
	.type = "bridge-dtmf-features",
	.destroy = ast_free_ptr,
};

/*!
 * \internal
 * \since 12.0.0
 * \brief read a feature code character and set it on for the give feature_flags struct
 *
 * \param feature_flags flags being modifed
 * \param feature feature code provided - should be an uppercase letter
 *
 * \retval 0 if the feature was set successfully
 * \retval -1 failure because the requested feature code isn't handled by this function
 */
static int set_feature_flag_from_char(struct ast_flags *feature_flags, char feature)
{
	switch (feature) {
	case 'T':
		ast_set_flag(feature_flags, AST_FEATURE_REDIRECT);
		return 0;
	case 'K':
		ast_set_flag(feature_flags, AST_FEATURE_PARKCALL);
		return 0;
	case 'H':
		ast_set_flag(feature_flags, AST_FEATURE_DISCONNECT);
		return 0;
	case 'W':
		ast_set_flag(feature_flags, AST_FEATURE_AUTOMON);
		return 0;
	case 'X':
		ast_set_flag(feature_flags, AST_FEATURE_AUTOMIXMON);
		return 0;
	default:
		return -1;
	}
}

/*!
 * \internal
 * \since 12.0.0
 * \brief Write a features string to a string buffer based on the feature flags provided
 *
 * \param feature_flags pointer to the feature flags to write from.
 * \param buffer pointer to a string buffer to write the features
 * \param buffer_size size of the buffer provided (should be able to fit all feature codes)
 *
 * \retval 0 on successful write
 * \retval -1 failure due to running out of buffer space
 */
static int dtmf_features_flags_to_string(struct ast_flags *feature_flags, char *buffer, size_t buffer_size)
{
	size_t buffer_expended = 0;
	unsigned int cur_feature;
	static const struct {
		char letter;
		unsigned int flag;
	} associations[] = {
		{ 'T', AST_FEATURE_REDIRECT },
		{ 'K', AST_FEATURE_PARKCALL },
		{ 'H', AST_FEATURE_DISCONNECT },
		{ 'W', AST_FEATURE_AUTOMON },
		{ 'X', AST_FEATURE_AUTOMIXMON },
	};

	for (cur_feature = 0; cur_feature < ARRAY_LEN(associations); cur_feature++) {
		if (ast_test_flag(feature_flags, associations[cur_feature].flag)) {
			if (buffer_expended == buffer_size - 1) {
				buffer[buffer_expended] = '\0';
				return -1;
			}
			buffer[buffer_expended++] = associations[cur_feature].letter;
		}
	}

	buffer[buffer_expended] = '\0';
	return 0;
}

static int build_dtmf_features(struct ast_flags *flags, const char *features)
{
	const char *feature;

	char missing_features[strlen(features) + 1];
	size_t number_of_missing_features = 0;

	for (feature = features; *feature; feature++) {
		if (!isupper(*feature)) {
			ast_log(LOG_ERROR, "Features string '%s' rejected because it contains non-uppercase feature.\n", features);
			return -1;
		}

		if (set_feature_flag_from_char(flags, *feature)) {
			missing_features[number_of_missing_features++] = *feature;
		}
	}

	missing_features[number_of_missing_features] = '\0';

	if (number_of_missing_features) {
		ast_log(LOG_WARNING, "Features '%s' from features string '%s' can not be applied.\n", missing_features, features);
	}

	return 0;
}

int ast_bridge_features_ds_set_string(struct ast_channel *chan, const char *features)
{
	struct ast_flags flags = {0};

	if (build_dtmf_features(&flags, features)) {
		return -1;
	}

	ast_channel_lock(chan);
	if (ast_bridge_features_ds_set(chan, &flags)) {
		ast_channel_unlock(chan);
		ast_log(LOG_ERROR, "Failed to apply features datastore for '%s' to channel '%s'\n", features, ast_channel_name(chan));
		return -1;
	}
	ast_channel_unlock(chan);

	return 0;
}

int ast_bridge_features_ds_get_string(struct ast_channel *chan, char *buffer, size_t buf_size)
{
	struct ast_flags *channel_flags;
	struct ast_flags held_copy;

	ast_channel_lock(chan);
	if (!(channel_flags = ast_bridge_features_ds_get(chan))) {
		ast_channel_unlock(chan);
		return -1;
	}
	held_copy = *channel_flags;
	ast_channel_unlock(chan);

	return dtmf_features_flags_to_string(&held_copy, buffer, buf_size);
}

static int bridge_features_ds_set_full(struct ast_channel *chan, struct ast_flags *flags, int replace)
{
	struct ast_datastore *datastore;
	struct ast_flags *ds_flags;

	datastore = ast_channel_datastore_find(chan, &dtmf_features_info, NULL);
	if (datastore) {
		ds_flags = datastore->data;
		if (replace) {
			*ds_flags = *flags;
		} else {
			flags->flags = flags->flags | ds_flags->flags;
			*ds_flags = *flags;
		}
		return 0;
	}

	datastore = ast_datastore_alloc(&dtmf_features_info, NULL);
	if (!datastore) {
		return -1;
	}

	ds_flags = ast_malloc(sizeof(*ds_flags));
	if (!ds_flags) {
		ast_datastore_free(datastore);
		return -1;
	}

	*ds_flags = *flags;
	datastore->data = ds_flags;
	ast_channel_datastore_add(chan, datastore);
	return 0;
}

int ast_bridge_features_ds_set(struct ast_channel *chan, struct ast_flags *flags)
{
	return bridge_features_ds_set_full(chan, flags, 1);
}

int ast_bridge_features_ds_append(struct ast_channel *chan, struct ast_flags *flags)
{
	return bridge_features_ds_set_full(chan, flags, 0);
}

struct ast_flags *ast_bridge_features_ds_get(struct ast_channel *chan)
{
	struct ast_datastore *datastore;

	datastore = ast_channel_datastore_find(chan, &dtmf_features_info, NULL);
	if (!datastore) {
		return NULL;
	}
	return datastore->data;
}

/*!
 * \internal
 * \brief Determine if we should dissolve the bridge from a hangup.
 * \since 12.0.0
 *
 * \param bridge_channel Channel executing the feature
 * \param hook_pvt Private data passed in when the hook was created
 *
 * \retval 0 Keep the callback hook.
 * \retval -1 Remove the callback hook.
 */
static int basic_hangup_hook(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
	int bridge_count = 0;
	struct ast_bridge_channel *iter;

	ast_bridge_channel_lock_bridge(bridge_channel);
	AST_LIST_TRAVERSE(&bridge_channel->bridge->channels, iter, entry) {
		if (iter != bridge_channel && iter->state == BRIDGE_CHANNEL_STATE_WAIT) {
			++bridge_count;
		}
	}
	if (2 <= bridge_count) {
		/* Just allow this channel to leave the multi-party bridge. */
		ast_bridge_channel_leave_bridge(bridge_channel,
			BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0);
/*!
 * \brief Details for specific basic bridge personalities
 */
struct personality_details {
	/*! The v_table to use for this personality */
	struct ast_bridge_methods *v_table;
	/*! Flags to set on this type of bridge */
	unsigned int bridge_flags;
	/*! User data for this personality. If used, must be an ao2 object */
	void *pvt;
	/*! Callback to be called when changing to the personality */
	void (*on_personality_change)(struct ast_bridge *bridge);
};

/*!
 * \brief structure that organizes different personalities for basic bridges.
 */
struct bridge_basic_personality {
	/*! The current bridge personality in use */
	enum bridge_basic_personality_type current;
	/*! Array of details for the types of bridge personalities supported */
	struct personality_details details[BRIDGE_BASIC_PERSONALITY_END];
};

/*
 * \internal
 * \brief Get the extension for a given builtin feature.
 *
 * \param chan Get the feature extension for this channel.
 * \param feature_name features.conf name of feature.
 * \param buf Where to put the extension.
 * \param len Length of the given extension buffer.
 *
 * \retval 0 success
 * \retval non-zero failiure
 */
static int builtin_feature_get_exten(struct ast_channel *chan, const char *feature_name, char *buf, size_t len)
{
	SCOPED_CHANNELLOCK(lock, chan);

	return ast_get_builtin_feature(chan, feature_name, buf, len);
}

/*!
 * \internal
 * \brief Helper to add a builtin DTMF feature hook to the features struct.
 * \since 12.0.0
 *
 * \param features Bridge features to setup.
 * \param chan Get features from this channel.
 * \param flags Feature flags on the channel.
 * \param feature_flag Feature flag to test.
 * \param feature_name features.conf name of feature.
 * \param feature_bridge Bridge feature enum to get hook callback.
 *
 * \retval 0 on success.
 * \retval -1 on error.
 */
static int builtin_features_helper(struct ast_bridge_features *features, struct ast_channel *chan,
	struct ast_flags *flags, unsigned int feature_flag, const char *feature_name, enum ast_bridge_builtin_feature feature_bridge)
{
	char dtmf[AST_FEATURE_MAX_LEN];
	int res;

	res = 0;
	if (ast_test_flag(flags, feature_flag)
		&& !builtin_feature_get_exten(chan, feature_name, dtmf, sizeof(dtmf))
		&& !ast_strlen_zero(dtmf)) {
		res = ast_bridge_features_enable(features, feature_bridge, dtmf, NULL, NULL,
			AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
		if (res) {
			ast_log(LOG_ERROR, "Channel %s: Requested DTMF feature %s not available.\n",
				ast_channel_name(chan), feature_name);
		}
	}

	return res;
}

/*!
 * \internal
 * \brief Setup bridge builtin features.
 * \since 12.0.0
 *
 * \param features Bridge features to setup.
 * \param chan Get features from this channel.
 *
 * \retval 0 on success.
 * \retval -1 on error.
 */
static int setup_bridge_features_builtin(struct ast_bridge_features *features, struct ast_channel *chan)
{
	struct ast_flags *flags;
	int res;

	ast_channel_lock(chan);
	flags = ast_bridge_features_ds_get(chan);
	ast_channel_unlock(chan);
	if (!flags) {
		return 0;
	}

	res = 0;
	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_REDIRECT, "blindxfer", AST_BRIDGE_BUILTIN_BLINDTRANSFER);
	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_REDIRECT, "atxfer", AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER);
	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_DISCONNECT, "disconnect", AST_BRIDGE_BUILTIN_HANGUP);
	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_PARKCALL, "parkcall", AST_BRIDGE_BUILTIN_PARKCALL);
	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_AUTOMON, "automon", AST_BRIDGE_BUILTIN_AUTOMON);
	res |= builtin_features_helper(features, chan, flags, AST_FEATURE_AUTOMIXMON, "automixmon", AST_BRIDGE_BUILTIN_AUTOMIXMON);

	return res ? -1 : 0;
}

struct dynamic_dtmf_hook_run {
	/*! Offset into app_name[] where the channel name that activated the hook starts. */
	int activated_offset;
	/*! Offset into app_name[] where the dynamic feature name starts. */
	int feature_offset;
	/*! Offset into app_name[] where the MOH class name starts.  (zero if no MOH) */
	int moh_offset;
	/*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */
	int app_args_offset;
	/*! Application name to run. */
	char app_name[0];
};

static void dynamic_dtmf_hook_callback(struct ast_bridge_channel *bridge_channel,
	const void *payload, size_t payload_size)
{
	struct ast_channel *chan = bridge_channel->chan;
	const struct dynamic_dtmf_hook_run *run_data = payload;

	pbx_builtin_setvar_helper(chan, "DYNAMIC_FEATURENAME",
		&run_data->app_name[run_data->feature_offset]);
	pbx_builtin_setvar_helper(chan, "DYNAMIC_WHO_ACTIVATED",
		&run_data->app_name[run_data->activated_offset]);

	ast_bridge_channel_run_app(bridge_channel, run_data->app_name,
		run_data->app_args_offset ? &run_data->app_name[run_data->app_args_offset] : NULL,
		run_data->moh_offset ? &run_data->app_name[run_data->moh_offset] : NULL);
}

struct dynamic_dtmf_hook_data {
	/*! Which side of bridge to run app (AST_FEATURE_FLAG_ONSELF/AST_FEATURE_FLAG_ONPEER) */
	unsigned int flags;
	/*! Offset into app_name[] where the dynamic feature name starts. */
	int feature_offset;
	/*! Offset into app_name[] where the MOH class name starts.  (zero if no MOH) */
	int moh_offset;
	/*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */
	int app_args_offset;
	/*! Application name to run. */
	char app_name[0];
};

/*!
 * \internal
 * \brief Activated dynamic DTMF feature hook.
 * \since 12.0.0
 *
 * \param bridge_channel Channel executing the feature
 * \param hook_pvt Private data passed in when the hook was created
 *
 * \retval 0 Keep the callback hook.
 * \retval -1 Remove the callback hook.
 */
static int dynamic_dtmf_hook_trip(struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
	struct dynamic_dtmf_hook_data *pvt = hook_pvt;
	struct dynamic_dtmf_hook_run *run_data;
	const char *activated_name;
	size_t len_name;
	size_t len_args;
	size_t len_moh;
	size_t len_feature;
	size_t len_activated;
	size_t len_data;

	/* Determine lengths of things. */
	len_name = strlen(pvt->app_name) + 1;
	len_args = pvt->app_args_offset ? strlen(&pvt->app_name[pvt->app_args_offset]) + 1 : 0;
	len_moh = pvt->moh_offset ? strlen(&pvt->app_name[pvt->moh_offset]) + 1 : 0;
	len_feature = strlen(&pvt->app_name[pvt->feature_offset]) + 1;
	ast_channel_lock(bridge_channel->chan);
	activated_name = ast_strdupa(ast_channel_name(bridge_channel->chan));
	ast_channel_unlock(bridge_channel->chan);
	len_activated = strlen(activated_name) + 1;
	len_data = sizeof(*run_data) + len_name + len_args + len_moh + len_feature + len_activated;

	/* Fill in dynamic feature run hook data. */
	run_data = ast_alloca(len_data);
	run_data->app_args_offset = len_args ? len_name : 0;
	run_data->moh_offset = len_moh ? len_name + len_args : 0;
	run_data->feature_offset = len_name + len_args + len_moh;
	run_data->activated_offset = len_name + len_args + len_moh + len_feature;
	strcpy(run_data->app_name, pvt->app_name);/* Safe */
	if (len_args) {
		strcpy(&run_data->app_name[run_data->app_args_offset],
			&pvt->app_name[pvt->app_args_offset]);/* Safe */
	}
	if (len_moh) {
		strcpy(&run_data->app_name[run_data->moh_offset],
			&pvt->app_name[pvt->moh_offset]);/* Safe */
	}
	strcpy(&run_data->app_name[run_data->feature_offset],
		&pvt->app_name[pvt->feature_offset]);/* Safe */
	strcpy(&run_data->app_name[run_data->activated_offset], activated_name);/* Safe */

	if (ast_test_flag(pvt, AST_FEATURE_FLAG_ONPEER)) {
		ast_bridge_channel_write_callback(bridge_channel,
			AST_BRIDGE_CHANNEL_CB_OPTION_MEDIA,
			dynamic_dtmf_hook_callback, run_data, len_data);
	} else {
		dynamic_dtmf_hook_callback(bridge_channel, run_data, len_data);
	}
	return 0;
}

/*!
 * \internal
 * \brief Add a dynamic DTMF feature hook to the bridge features.
 * \since 12.0.0
 *
 * \param features Bridge features to setup.
 * \param flags Which side of bridge to run app (AST_FEATURE_FLAG_ONSELF/AST_FEATURE_FLAG_ONPEER).
 * \param dtmf DTMF trigger sequence.
 * \param feature_name Name of the dynamic feature.
 * \param app_name Dialplan application name to run.
 * \param app_args Dialplan application arguments. (Empty or NULL if no arguments)
 * \param moh_class MOH class to play to peer. (Empty or NULL if no MOH played)
 *
 * \retval 0 on success.
 * \retval -1 on error.
 */
static int dynamic_dtmf_hook_add(struct ast_bridge_features *features, unsigned int flags, const char *dtmf, const char *feature_name, const char *app_name, const char *app_args, const char *moh_class)
{
	struct dynamic_dtmf_hook_data *hook_data;
	size_t len_name = strlen(app_name) + 1;
	size_t len_args = ast_strlen_zero(app_args) ? 0 : strlen(app_args) + 1;
	size_t len_moh = ast_strlen_zero(moh_class) ? 0 : strlen(moh_class) + 1;
	size_t len_feature = strlen(feature_name) + 1;
	size_t len_data = sizeof(*hook_data) + len_name + len_args + len_moh + len_feature;
	int res;

	/* Fill in application run hook data. */
	hook_data = ast_malloc(len_data);
	if (!hook_data) {
		return -1;
	}
	hook_data->flags = flags;
	hook_data->app_args_offset = len_args ? len_name : 0;
	hook_data->moh_offset = len_moh ? len_name + len_args : 0;
	hook_data->feature_offset = len_name + len_args + len_moh;
	strcpy(hook_data->app_name, app_name);/* Safe */
	if (len_args) {
		strcpy(&hook_data->app_name[hook_data->app_args_offset], app_args);/* Safe */
	}
	if (len_moh) {
		strcpy(&hook_data->app_name[hook_data->moh_offset], moh_class);/* Safe */
	}
	strcpy(&hook_data->app_name[hook_data->feature_offset], feature_name);/* Safe */

	res = ast_bridge_dtmf_hook(features, dtmf, dynamic_dtmf_hook_trip, hook_data,
		ast_free_ptr,
		AST_BRIDGE_HOOK_REMOVE_ON_PULL | AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
	if (res) {
		ast_free(hook_data);
	}
	return res;
}

static int setup_dynamic_feature(void *obj, void *arg, void *data, int flags)
{
	struct ast_applicationmap_item *item = obj;
	struct ast_bridge_features *features = arg;
	int *res = data;

	*res |= dynamic_dtmf_hook_add(features,
		item->activate_on_self ? AST_FEATURE_FLAG_ONSELF : AST_FEATURE_FLAG_ONPEER,
		item->dtmf, item->name, item->app, item->app_data, item->moh_class);

	return 0;
}

/*!
 * \internal
 * \brief Setup bridge dynamic features.
 * \since 12.0.0
 *
 * \param features Bridge features to setup.
 * \param chan Get features from this channel.
 *
 * \retval 0 on success.
 * \retval -1 on error.
 */
static int setup_bridge_features_dynamic(struct ast_bridge_features *features, struct ast_channel *chan)
{
	int res = 0;

	ast_channel_lock(chan);
	applicationmap = ast_get_chan_applicationmap(chan);
	ast_channel_unlock(chan);
	if (applicationmap) {
		ao2_callback_data(applicationmap, 0, setup_dynamic_feature, features, &res);
		ao2_ref(applicationmap, -1);
	}

	return res;
}

/*!
 * \internal
 * \brief Setup DTMF feature hooks using the channel features datastore property.
 * \since 12.0.0
 *
 * \param bridge_channel What to setup DTMF features on.
 *
 * \retval 0 on success.
 * \retval -1 on error.
 */
static int bridge_basic_setup_features(struct ast_bridge_channel *bridge_channel)
{
	int res = 0;

	res |= setup_bridge_features_builtin(bridge_channel->features, bridge_channel->chan);
	res |= setup_bridge_features_dynamic(bridge_channel->features, bridge_channel->chan);

	return res;
}

static int add_normal_hooks(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
{
	return ast_bridge_hangup_hook(bridge_channel->features, basic_hangup_hook,
			NULL, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL)
		|| bridge_basic_setup_features(bridge_channel);
/*!
 * \internal
 * \brief ast_bridge basic push method.
 * \since 12.0.0
 *
 * \param self Bridge to operate upon.
 * \param bridge_channel Bridge channel to push.
 * \param swap Bridge channel to swap places with if not NULL.
 *
 * \note On entry, self is already locked.
 *
 * \retval 0 on success
 * \retval -1 on failure
 */
static int bridge_personality_normal_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
	if (add_normal_hooks(self, bridge_channel)) {
	return 0;
}

static int bridge_basic_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
{
	struct bridge_basic_personality *personality = self->personality;

	ast_assert(personality != NULL);

	if (personality->details[personality->current].v_table->push
		&& personality->details[personality->current].v_table->push(self, bridge_channel, swap)) {
	ast_bridge_channel_update_linkedids(bridge_channel, swap);
	ast_bridge_channel_update_accountcodes(bridge_channel, swap);

	return ast_bridge_base_v_table.push(self, bridge_channel, swap);
}

static void bridge_basic_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
{
	struct bridge_basic_personality *personality = self->personality;
	ast_assert(personality != NULL);

	if (personality->details[personality->current].v_table->pull) {
		personality->details[personality->current].v_table->pull(self, bridge_channel);
	}

	ast_bridge_channel_update_accountcodes(NULL, bridge_channel);

	ast_bridge_base_v_table.pull(self, bridge_channel);
}

static void bridge_basic_destroy(struct ast_bridge *self)
	struct bridge_basic_personality *personality = self->personality;
	ao2_cleanup(personality);

	ast_bridge_base_v_table.destroy(self);
/*!
 * \brief Remove appropriate hooks when basic bridge personality changes
 *
 * Hooks that have the AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE flag
 * set will be removed from all bridge channels in the bridge.
 *
 * \param bridge Basic bridge undergoing personality change
 */
static void remove_hooks_on_personality_change(struct ast_bridge *bridge)
724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321
	struct ast_bridge_channel *iter;

	AST_LIST_TRAVERSE(&bridge->channels, iter, entry) {
		SCOPED_LOCK(lock, iter, ast_bridge_channel_lock, ast_bridge_channel_unlock);
		ast_bridge_features_remove(iter->features, AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE);
	}
}

/*!
 * \brief Attended transfer superstates.
 *
 * An attended transfer's progress is facilitated by a state machine.
 * The individual states of the state machine fall into the realm of
 * one of two superstates.
 */
enum attended_transfer_superstate {
	/*!
	 * \brief Transfer superstate
	 *
	 * The attended transfer state machine begins in this superstate. The
	 * goal of this state is for a transferer channel to facilitate a
	 * transfer from a transferee to a transfer target.
	 *
	 * There are two bridges used in this superstate. The transferee bridge is
	 * the bridge that the transferer and transferee channels originally
	 * communicate in, and the target bridge is the bridge where the transfer
	 * target is being dialed.
	 *
	 * The transferer channel is capable of moving between the bridges using
	 * the DTMF swap sequence.
	 */
	SUPERSTATE_TRANSFER,
	/*!
	 * \brief Recall superstate
	 *
	 * The attended transfer state machine moves to this superstate if
	 * atxferdropcall is set to "no" and the transferer channel hangs up
	 * during a transfer. The goal in this superstate is to call back either
	 * the transfer target or transferer and rebridge with the transferee
	 * channel(s).
	 *
	 * In this superstate, there is only a single bridge used, the original
	 * transferee bridge. Rather than distinguishing between a transferer
	 * and transfer target, all outbound calls are toward a "recall_target"
	 * channel.
	 */
	SUPERSTATE_RECALL,
};

/*!
 * The states in the attended transfer state machine.
 */
enum attended_transfer_state {
	/*!
	 * \brief Calling Target state
	 *
	 * This state describes the initial state of a transfer. The transferer
	 * waits in the transfer target's bridge for the transfer target to answer.
	 *
	 * Superstate: Transfer
	 *
	 * Preconditions:
	 * 1) Transfer target is RINGING
	 * 2) Transferer is in transferee bridge
	 * 3) Transferee is on hold
	 *
	 * Transitions to TRANSFER_CALLING_TARGET:
	 * 1) This is the initial state for an attended transfer.
	 * 2) TRANSFER_HESITANT: Transferer presses DTMF swap sequence
	 *
	 * State operation:
	 * The transferer is moved from the transferee bridge into the transfer
	 * target bridge.
	 *
	 * Transitions from TRANSFER_CALLING_TARGET:
	 * 1) TRANSFER_FAIL: Transferee hangs up.
	 * 2) TRANSFER_BLOND: Transferer hangs up or presses DTMF swap sequence
	 * and configured atxferdropcall setting is yes.
	 * 3) TRANSFER_BLOND_NONFINAL: Transferer hangs up or presses DTMF swap
	 * sequence and configured atxferdroppcall setting is no.
	 * 4) TRANSFER_CONSULTING: Transfer target answers the call.
	 * 5) TRANSFER_REBRIDGE: Transfer target hangs up, call to transfer target
	 * times out, or transferer presses DTMF abort sequence.
	 * 6) TRANSFER_THREEWAY: Transferer presses DTMF threeway sequence.
	 * 7) TRANSFER_HESITANT: Transferer presses DTMF swap sequence.
	 */
	TRANSFER_CALLING_TARGET,
	/*!
	 * \brief Hesitant state
	 *
	 * This state only arises if when waiting for the transfer target to
	 * answer, the transferer presses the DTMF swap sequence. This will
	 * cause the transferer to be rebridged with the transferee temporarily.
	 *
	 * Superstate: Transfer
	 *
	 * Preconditions:
	 * 1) Transfer target is in ringing state
	 * 2) Transferer is in transfer target bridge
	 * 3) Transferee is on hold
	 *
	 * Transitions to TRANSFER_HESITANT:
	 * 1) TRANSFER_CALLING_TARGET: Transferer presses DTMF swap sequence.
	 *
	 * State operation:
	 * The transferer is moved from the transfer target bridge into the
	 * transferee bridge, and the transferee is taken off hold.
	 *
	 * Transitions from TRANSFER_HESITANT:
	 * 1) TRANSFER_FAIL: Transferee hangs up
	 * 2) TRANSFER_BLOND: Transferer hangs up or presses DTMF swap sequence
	 * and configured atxferdropcall setting is yes.
	 * 3) TRANSFER_BLOND_NONFINAL: Transferer hangs up or presses DTMF swap
	 * sequence and configured atxferdroppcall setting is no.
	 * 4) TRANSFER_DOUBLECHECKING: Transfer target answers the call
	 * 5) TRANSFER_RESUME: Transfer target hangs up, call to transfer target
	 * times out, or transferer presses DTMF abort sequence.
	 * 6) TRANSFER_THREEWAY: Transferer presses DTMF threeway sequence.
	 * 7) TRANSFER_CALLING_TARGET: Transferer presses DTMF swap sequence.
	 */
	TRANSFER_HESITANT,
	/*!
	 * \brief Rebridge state
	 *
	 * This is a terminal state that indicates that the transferer needs
	 * to move back to the transferee's bridge. This is a failed attended
	 * transfer result.
	 *
	 * Superstate: Transfer
	 *
	 * Preconditions:
	 * 1) Transferer is in transfer target bridge
	 * 2) Transferee is on hold
	 *
	 * Transitions to TRANSFER_REBRIDGE:
	 * 1) TRANSFER_CALLING_TARGET: Transfer target hangs up, call to transfer target
	 * times out, or transferer presses DTMF abort sequence.
	 * 2) TRANSFER_STATE_CONSULTING: Transfer target hangs up, or transferer presses
	 * DTMF abort sequence.
	 *
	 * State operation:
	 * The transferer channel is moved from the transfer target bridge to the
	 * transferee bridge. The transferee is taken off hold. A stasis transfer
	 * message is published indicating a failed attended transfer.
	 *
	 * Transitions from TRANSFER_REBRIDGE:
	 * None
	 */
	TRANSFER_REBRIDGE,
	/*!
	 * \brief Resume state
	 *
	 * This is a terminal state that indicates that the party bridged with the
	 * transferee is the final party to be bridged with that transferee. This state
	 * may come about due to a successful recall or due to a failed transfer.
	 *
	 * Superstate: Transfer or Recall
	 *
	 * Preconditions:
	 * In Transfer Superstate:
	 * 1) Transferer is in transferee bridge
	 * 2) Transferee is not on hold
	 * In Recall Superstate:
	 * 1) The recall target is in the transferee bridge
	 * 2) Transferee is not on hold
	 *
	 * Transitions to TRANSFER_RESUME:
	 * TRANSFER_HESITANT: Transfer target hangs up, call to transfer target times out,
	 * or transferer presses DTMF abort sequence.
	 * TRANSFER_DOUBLECHECKING: Transfer target hangs up or transferer presses DTMF
	 * abort sequence.
	 * TRANSFER_BLOND_NONFINAL: Recall target answers
	 * TRANSFER_RECALLING: Recall target answers
	 * TRANSFER_RETRANSFER: Recall target answers
	 *
	 * State operations:
	 * None
	 *
	 * Transitions from TRANSFER_RESUME:
	 * None
	 */
	TRANSFER_RESUME,
	/*!
	 * \brief Threeway state
	 *
	 * This state results when the transferer wishes to have all parties involved
	 * in a transfer to be in the same bridge together.
	 *
	 * Superstate: Transfer
	 *
	 * Preconditions:
	 * 1) Transfer target state is either RINGING or UP
	 * 2) Transferer is in either bridge
	 * 3) Transferee is not on hold
	 *
	 * Transitions to TRANSFER_THREEWAY:
	 * 1) TRANSFER_CALLING_TARGET: Transferer presses DTMF threeway sequence.
	 * 2) TRANSFER_HESITANT: Transferer presses DTMF threeway sequence.
	 * 3) TRANSFER_CONSULTING: Transferer presses DTMF threeway sequence.
	 * 4) TRANSFER_DOUBLECHECKING: Transferer presses DTMF threeway sequence.
	 *
	 * State operation:
	 * The transfer target bridge is merged into the transferee bridge.
	 *
	 * Transitions from TRANSFER_THREEWAY:
	 * None.
	 */
	TRANSFER_THREEWAY,
	/*!
	 * \brief Consulting state
	 *
	 * This state describes the case where the transferer and transfer target
	 * are able to converse in the transfer target's bridge prior to completing
	 * the transfer.
	 *
	 * Superstate: Transfer
	 *
	 * Preconditions:
	 * 1) Transfer target is UP
	 * 2) Transferer is in target bridge
	 * 3) Transferee is on hold
	 *
	 * Transitions to TRANSFER_CONSULTING:
	 * 1) TRANSFER_CALLING_TARGET: Transfer target answers.
	 * 2) TRANSFER_DOUBLECHECKING: Transferer presses DTMF swap sequence.
	 *
	 * State operations:
	 * None.
	 *
	 * Transitions from TRANSFER_CONSULTING:
	 * TRANSFER_COMPLETE: Transferer hangs up or transferer presses DTMF complete sequence.
	 * TRANSFER_REBRIDGE: Transfer target hangs up or transferer presses DTMF abort sequence.
	 * TRANSFER_THREEWAY: Transferer presses DTMF threeway sequence.
	 * TRANSFER_DOUBLECHECKING: Transferer presses DTMF swap sequence.
	 */
	TRANSFER_CONSULTING,
	/*!
	 * \brief Double-checking state
	 *
	 * This state describes the case where the transferer and transferee are
	 * able to converse in the transferee's bridge prior to completing the transfer. The
	 * difference between this and TRANSFER_HESITANT is that the transfer target is
	 * UP in this case.
	 *
	 * Superstate: Transfer
	 *
	 * Preconditions:
	 * 1) Transfer target is UP and on hold
	 * 2) Transferer is in transferee bridge
	 * 3) Transferee is off hold
	 *
	 * Transitions to TRANSFER_DOUBLECHECKING:
	 * 1) TRANSFER_HESITANT: Transfer target answers.
	 * 2) TRANSFER_CONSULTING: Transferer presses DTMF swap sequence.
	 *
	 * State operations:
	 * None.
	 *
	 * Transitions from TRANSFER_DOUBLECHECKING:
	 * 1) TRANSFER_FAIL: Transferee hangs up.
	 * 2) TRANSFER_COMPLETE: Transferer hangs up or presses DTMF complete sequence.
	 * 3) TRANSFER_RESUME: Transfer target hangs up or transferer presses DTMF abort sequence.
	 * 4) TRANSFER_THREEWAY: Transferer presses DTMF threeway sequence.
	 * 5) TRANSFER_CONSULTING: Transferer presses the DTMF swap sequence.
	 */
	TRANSFER_DOUBLECHECKING,
	/*!
	 * \brief Complete state
	 *
	 * This is a terminal state where a transferer has successfully completed an attended
	 * transfer. This state's goal is to get the transfer target and transferee into
	 * the same bridge and the transferer off the call.
	 *
	 * Superstate: Transfer
	 *
	 * Preconditions:
	 * 1) Transfer target is UP and off hold.
	 * 2) Transferer is in either bridge.
	 * 3) Transferee is off hold.
	 *
	 * Transitions to TRANSFER_COMPLETE:
	 * 1) TRANSFER_CONSULTING: transferer hangs up or presses the DTMF complete sequence.
	 * 2) TRANSFER_DOUBLECHECKING: transferer hangs up or presses the DTMF complete sequence.
	 *
	 * State operation:
	 * The transfer target bridge is merged into the transferee bridge. The transferer
	 * channel is kicked out of the bridges as part of the merge.
	 *
	 * State operations:
	 * 1) Merge the transfer target bridge into the transferee bridge,
	 * excluding the transferer channel from the merge.
	 * 2) Publish a stasis transfer message.
	 *
	 * Exit operations:
	 * This is a terminal state, so there are no exit operations.
	 */
	TRANSFER_COMPLETE,
	/*!
	 * \brief Blond state
	 *
	 * This is a terminal state where a transferer has completed an attended transfer prior
	 * to the transfer target answering. This state is only entered if atxferdropcall
	 * is set to 'yes'. This is considered to be a successful attended transfer.
	 *
	 * Superstate: Transfer
	 *
	 * Preconditions:
	 * 1) Transfer target is RINGING.
	 * 2) Transferer is in either bridge.
	 * 3) Transferee is off hold.
	 *
	 * Transitions to TRANSFER_BLOND:
	 * 1) TRANSFER_CALLING_TARGET: Transferer hangs up or presses the DTMF complete sequence.
	 *    atxferdropcall is set to 'yes'.
	 * 2) TRANSFER_HESITANT: Transferer hangs up or presses the DTMF complete sequence.
	 *    atxferdropcall is set to 'yes'.
	 *
	 * State operations:
	 * The transfer target bridge is merged into the transferee bridge. The transferer
	 * channel is kicked out of the bridges as part of the merge. A stasis transfer
	 * publication is sent indicating a successful transfer.
	 *
	 * Transitions from TRANSFER_BLOND:
	 * None
	 */
	TRANSFER_BLOND,
	/*!
	 * \brief Blond non-final state
	 *
	 * This state is very similar to the TRANSFER_BLOND state, except that
	 * this state is entered when atxferdropcall is set to 'no'. This is the
	 * initial state of the Recall superstate, so state operations mainly involve
	 * moving to the Recall superstate. This means that the transfer target, that
	 * is currently ringing is now known as the recall target.
	 *
	 * Superstate: Recall
	 *
	 * Preconditions:
	 * 1) Recall target is RINGING.
	 * 2) Transferee is off hold.
	 *
	 * Transitions to TRANSFER_BLOND_NONFINAL:
	 * 1) TRANSFER_CALLING_TARGET: Transferer hangs up or presses the DTMF complete sequence.
	 *    atxferdropcall is set to 'no'.
	 * 2) TRANSFER_HESITANT: Transferer hangs up or presses the DTMF complete sequence.
	 *    atxferdropcall is set to 'no'.
	 *
	 * State operation:
	 * The superstate of the attended transfer is changed from Transfer to Recall.
	 * The transfer target bridge is merged into the transferee bridge. The transferer
	 * channel is kicked out of the bridges as part of the merge.
	 *
	 * Transitions from TRANSFER_BLOND_NONFINAL:
	 * 1) TRANSFER_FAIL: Transferee hangs up
	 * 2) TRANSFER_RESUME: Recall target answers
	 * 3) TRANSFER_RECALLING: Recall target hangs up or time expires.
	 */
	TRANSFER_BLOND_NONFINAL,
	/*!
	 * \brief Recalling state
	 *
	 * This state is entered if the recall target from the TRANSFER_BLOND_NONFINAL
	 * or TRANSFER_RETRANSFER states hangs up or does not answer. The goal of this
	 * state is to call back the original transferer in an attempt to recover the
	 * original call.
	 *
	 * Superstate: Recall
	 *
	 * Preconditions:
	 * 1) Recall target is down.
	 * 2) Transferee is off hold.
	 *
	 * Transitions to TRANSFER_RECALLING:
	 * 1) TRANSFER_BLOND_NONFINAL: Recall target hangs up or time expires.
	 * 2) TRANSFER_RETRANSFER: Recall target hangs up or time expires.
	 *    atxferloopdelay is non-zero.
	 * 3) TRANSFER_WAIT_TO_RECALL: Time expires.
	 *
	 * State operation:
	 * The original transferer becomes the recall target and is called using the Dialing API.
	 * Ringing is indicated to the transferee.
	 *
	 * Transitions from TRANSFER_RECALLING:
	 * 1) TRANSFER_FAIL:
	 *    a) Transferee hangs up.
	 *    b) Recall target hangs up or time expires, and number of recall attempts exceeds atxfercallbackretries
	 * 2) TRANSFER_WAIT_TO_RETRANSFER: Recall target hangs up or time expires.
	 *    atxferloopdelay is non-zero.
	 * 3) TRANSFER_RETRANSFER: Recall target hangs up or time expires.
	 *    atxferloopdelay is zero.
	 * 4) TRANSFER_RESUME: Recall target answers.
	 */
	TRANSFER_RECALLING,
	/*!
	 * \brief Wait to Retransfer state
	 *
	 * This state is used simply to give a bit of breathing room between attempting
	 * to call back the original transferer and attempting to call back the original
	 * transfer target. The transferee hears music on hold during this state as an
	 * auditory clue that no one is currently being dialed.
	 *
	 * Superstate: Recall
	 *
	 * Preconditions:
	 * 1) Recall target is down.
	 * 2) Transferee is off hold.
	 *
	 * Transitions to TRANSFER_WAIT_TO_RETRANSFER:
	 * 1) TRANSFER_RECALLING: Recall target hangs up or time expires.
	 *    atxferloopdelay is non-zero.
	 *
	 * State operation:
	 * The transferee is placed on hold.
	 *
	 * Transitions from TRANSFER_WAIT_TO_RETRANSFER:
	 * 1) TRANSFER_FAIL: Transferee hangs up.
	 * 2) TRANSFER_RETRANSFER: Time expires.
	 */
	TRANSFER_WAIT_TO_RETRANSFER,
	/*!
	 * \brief Retransfer state
	 *
	 * This state is used in order to attempt to call back the original
	 * transfer target channel from the transfer. The transferee hears
	 * ringing during this state as an auditory cue that a party is being
	 * dialed.
	 *
	 * Superstate: Recall
	 *
	 * Preconditions:
	 * 1) Recall target is down.
	 * 2) Transferee is off hold.
	 *
	 * Transitions to TRANSFER_RETRANSFER:
	 * 1) TRANSFER_RECALLING: Recall target hangs up or time expires.
	 *    atxferloopdelay is zero.
	 * 2) TRANSFER_WAIT_TO_RETRANSFER: Time expires.
	 *
	 * State operation:
	 * The original transfer target is requested and is set as the recall target.
	 * The recall target is called and placed into the transferee bridge.
	 *
	 * Transitions from TRANSFER_RETRANSFER:
	 * 1) TRANSFER_FAIL: Transferee hangs up.
	 * 2) TRANSFER_WAIT_TO_RECALL: Recall target hangs up or time expires.
	 *    atxferloopdelay is non-zero.
	 * 3) TRANSFER_RECALLING: Recall target hangs up or time expires.
	 *    atxferloopdelay is zero.
	 */
	TRANSFER_RETRANSFER,
	/*!
	 * \brief Wait to recall state
	 *
	 * This state is used simply to give a bit of breathing room between attempting
	 * to call back the original transfer target and attempting to call back the
	 * original transferer. The transferee hears music on hold during this state as an
	 * auditory clue that no one is currently being dialed.
	 *
	 * Superstate: Recall
	 *
	 * Preconditions:
	 * 1) Recall target is down.
	 * 2) Transferee is off hold.
	 *
	 * Transitions to TRANSFER_WAIT_TO_RECALL:
	 * 1) TRANSFER_RETRANSFER: Recall target hangs up or time expires.
	 *    atxferloopdelay is non-zero.
	 *
	 * State operation:
	 * Transferee is placed on hold.
	 *
	 * Transitions from TRANSFER_WAIT_TO_RECALL:
	 * 1) TRANSFER_FAIL: Transferee hangs up
	 * 2) TRANSFER_RECALLING: Time expires
	 */
	TRANSFER_WAIT_TO_RECALL,
	/*!
	 * \brief Fail state
	 *
	 * This state indicates that something occurred during the transfer that
	 * makes a graceful completion impossible. The most common stimulus for this
	 * state is when the transferee hangs up.
	 *
	 * Superstate: Transfer and Recall
	 *
	 * Preconditions:
	 * None
	 *
	 * Transitions to TRANSFER_FAIL:
	 * 1) TRANSFER_CALLING_TARGET: Transferee hangs up.
	 * 2) TRANSFER_HESITANT: Transferee hangs up.
	 * 3) TRANSFER_DOUBLECHECKING: Transferee hangs up.
	 * 4) TRANSFER_BLOND_NONFINAL: Transferee hangs up.
	 * 5) TRANSFER_RECALLING:
	 *    a) Transferee hangs up.
	 *    b) Recall target hangs up or time expires, and number of
	 *       recall attempts exceeds atxfercallbackretries.
	 * 6) TRANSFER_WAIT_TO_RETRANSFER: Transferee hangs up.
	 * 7) TRANSFER_RETRANSFER: Transferee hangs up.
	 * 8) TRANSFER_WAIT_TO_RECALL: Transferee hangs up.
	 *
	 * State operation:
	 * A transfer stasis publication is made indicating a failed transfer.
	 * The transferee bridge is destroyed.
	 *
	 * Transitions from TRANSFER_FAIL:
	 * None.
	 */
	TRANSFER_FAIL,
};

/*!
 * \brief Stimuli that can cause transfer state changes
 */
enum attended_transfer_stimulus {
	/*! No stimulus. This literally can never happen. */
	STIMULUS_NONE,
	/*! All of the transferee channels have been hung up. */
	STIMULUS_TRANSFEREE_HANGUP,
	/*! The transferer has hung up. */
	STIMULUS_TRANSFERER_HANGUP,
	/*! The transfer target channel has hung up. */
	STIMULUS_TRANSFER_TARGET_HANGUP,
	/*! The transfer target channel has answered. */
	STIMULUS_TRANSFER_TARGET_ANSWER,
	/*! The recall target channel has hung up. */
	STIMULUS_RECALL_TARGET_HANGUP,
	/*! The recall target channel has answered. */
	STIMULUS_RECALL_TARGET_ANSWER,
	/*! The current state's timer has expired. */
	STIMULUS_TIMEOUT,
	/*! The transferer pressed the abort DTMF sequence. */
	STIMULUS_DTMF_ATXFER_ABORT,
	/*! The transferer pressed the complete DTMF sequence. */
	STIMULUS_DTMF_ATXFER_COMPLETE,
	/*! The transferer pressed the three-way DTMF sequence. */
	STIMULUS_DTMF_ATXFER_THREEWAY,
	/*! The transferer pressed the swap DTMF sequence. */
	STIMULUS_DTMF_ATXFER_SWAP,
};

/*!
 * \brief String representations of the various stimuli
 *
 * Used for debugging purposes
 */
const char *stimulus_strs[] = {
	[STIMULUS_NONE] = "None",
	[STIMULUS_TRANSFEREE_HANGUP] = "Transferee Hangup",
	[STIMULUS_TRANSFERER_HANGUP] = "Transferer Hangup",
	[STIMULUS_TRANSFER_TARGET_HANGUP] = "Transfer Target Hangup",
	[STIMULUS_TRANSFER_TARGET_ANSWER] = "Transfer Target Answer",
	[STIMULUS_RECALL_TARGET_HANGUP] = "Recall Target Hangup",
	[STIMULUS_RECALL_TARGET_ANSWER] = "Recall Target Answer",
	[STIMULUS_TIMEOUT] = "Timeout",
	[STIMULUS_DTMF_ATXFER_ABORT] = "DTMF Abort",
	[STIMULUS_DTMF_ATXFER_COMPLETE] = "DTMF Complete",
	[STIMULUS_DTMF_ATXFER_THREEWAY] = "DTMF Threeway",
	[STIMULUS_DTMF_ATXFER_SWAP] = "DTMF Swap",
};

struct stimulus_list {
	enum attended_transfer_stimulus stimulus;
	AST_LIST_ENTRY(stimulus_list) next;
};

/*!
 * \brief Collection of data related to an attended transfer attempt
 */
struct attended_transfer_properties {
	AST_DECLARE_STRING_FIELDS (
		/*! Extension of transfer target */
		AST_STRING_FIELD(exten);
		/*! Context of transfer target */
		AST_STRING_FIELD(context);
		/*! Sound to play when transfer completes */
		AST_STRING_FIELD(xfersound);
		/*! The channel technology of the transferer channel */
		AST_STRING_FIELD(transferer_type);
		/*! The transferer channel address */
		AST_STRING_FIELD(transferer_addr);
	);
	/*! Condition used to synchronize when stimuli are reported to the monitor thread */
	ast_cond_t cond;
	/*! The bridge where the transferee resides. This bridge is also the bridge that
	 * survives a successful attended transfer.
	 */
	struct ast_bridge *transferee_bridge;
	/*! The bridge used to place an outbound call to the transfer target. This
	 * bridge is merged with the transferee_bridge on a successful transfer.
	 */
	struct ast_bridge *target_bridge;
	/*! The party that performs the attended transfer. */
	struct ast_channel *transferer;
	/*! The local channel dialed to reach the transfer target. */
	struct ast_channel *transfer_target;
	/*! The party that is currently being recalled. Depending on
	 * the current state, this may be either the party that originally
	 * was the transferer or the original transfer target.  This is
	 * set with reference when entering the BLOND_NONFINAL, RECALLING,
	 * and RETRANSFER states, and the reference released on state exit
	 * if continuing with recall or retransfer to avoid leak.
	 */
	struct ast_channel *recall_target;
	/*! The absolute starting time for running timers */
	struct timeval start;
	AST_LIST_HEAD_NOLOCK(,stimulus_list) stimulus_queue;
	/*! The current state of the attended transfer */
	enum attended_transfer_state state;
	/*! The current superstate of the attended transfer */
	enum attended_transfer_superstate superstate;
	/*! Configured atxferdropcall from features.conf */
	int atxferdropcall;
	/*! Configured atxfercallbackretries from features.conf */
	int atxfercallbackretries;
	/*! Configured atxferloopdelay from features.conf */
	int atxferloopdelay;
	/*! Configured atxfernoanswertimeout from features.conf */
	int atxfernoanswertimeout;
	/*! Count of the number of times that recalls have been attempted */
	int retry_attempts;
	/*! Framehook ID for outbounc call to transfer target or recall target */
	int target_framehook_id;
	/*! Dial structure used when recalling transferer channel */
	struct ast_dial *dial;
	/*! The bridging features the transferer has available */
	struct ast_flags transferer_features;
	/*! Saved transferer connected line data for recalling the transferer. */
	struct ast_party_connected_line original_transferer_colp;
};

static void attended_transfer_properties_destructor(void *obj)
{
	struct attended_transfer_properties *props = obj;

	ast_debug(1, "Destroy attended transfer properties %p\n", props);

	ao2_cleanup(props->target_bridge);
	ao2_cleanup(props->transferee_bridge);
	/* Use ast_channel_cleanup() instead of ast_channel_unref() for channels since they may be NULL */
	ast_channel_cleanup(props->transferer);
	ast_channel_cleanup(props->transfer_target);
	ast_channel_cleanup(props->recall_target);
	ast_party_connected_line_free(&props->original_transferer_colp);
	ast_string_field_free_memory(props);
	ast_cond_destroy(&props->cond);
}

/*!
 * \internal
 * \brief Determine the transfer context to use.
 * \since 12.0.0
 *
 * \param transferer Channel initiating the transfer.
 * \param context User supplied context if available.  May be NULL.
 *
 * \return The context to use for the transfer.
 */
static const char *get_transfer_context(struct ast_channel *transferer, const char *context)
{
	if (!ast_strlen_zero(context)) {
		return context;
	}
	context = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
	if (!ast_strlen_zero(context)) {
		return context;
	}
	context = ast_channel_macrocontext(transferer);
	if (!ast_strlen_zero(context)) {
		return context;
	}
	context = ast_channel_context(transferer);
	if (!ast_strlen_zero(context)) {
		return context;
	}
	return "default";
}

/*!
 * \brief Allocate and initialize attended transfer properties
 *
 * \param transferer The channel performing the attended transfer
 * \param context Suggestion for what context the transfer target extension can be found in
 *
 * \retval NULL Failure to allocate or initialize
 * \retval non-NULL Newly allocated properties
 */
static struct attended_transfer_properties *attended_transfer_properties_alloc(
	struct ast_channel *transferer, const char *context)
{
	struct attended_transfer_properties *props;
	char *tech;
	char *addr;
	char *serial;
	struct ast_features_xfer_config *xfer_cfg;
	struct ast_flags *transferer_features;

	props = ao2_alloc(sizeof(*props), attended_transfer_properties_destructor);
	if (!props) {
		ast_log(LOG_ERROR, "Unable to create props - channel %s, context %s\n",
			ast_channel_name(transferer), context);
		return NULL;
	}

	ast_cond_init(&props->cond, NULL);

	if (ast_string_field_init(props, 64)) {
		ast_log(LOG_ERROR, "Unable to initialize prop fields - channel %s, context %s\n",
			ast_channel_name(transferer), context);
		ao2_ref(props, -1);
		return NULL;
	}

	props->target_framehook_id = -1;
	props->transferer = ast_channel_ref(transferer);

	ast_channel_lock(props->transferer);
	xfer_cfg = ast_get_chan_features_xfer_config(props->transferer);
	if (!xfer_cfg) {
		ast_log(LOG_ERROR, "Unable to get transfer configuration from channel %s\n", ast_channel_name(props->transferer));
		ao2_ref(props, -1);
		return NULL;
	}
	transferer_features = ast_bridge_features_ds_get(props->transferer);
	if (transferer_features) {
		props->transferer_features = *transferer_features;
	}
	props->atxferdropcall = xfer_cfg->atxferdropcall;
	props->atxfercallbackretries = xfer_cfg->atxfercallbackretries;
	props->atxfernoanswertimeout = xfer_cfg->atxfernoanswertimeout;
	props->atxferloopdelay = xfer_cfg->atxferloopdelay;
	ast_string_field_set(props, context, get_transfer_context(transferer, context));
	ast_string_field_set(props, xfersound, xfer_cfg->xfersound);
	/*
	 * Save the transferee's party information for any recall calls.
	 * This is the only piece of information needed that gets overwritten
	 * on the transferer channel by the inital call to the transfer target.
	 */
	ast_party_connected_line_copy(&props->original_transferer_colp,
		ast_channel_connected(props->transferer));

	tech = ast_strdupa(ast_channel_name(props->transferer));
	addr = strchr(tech, '/');
	if (!addr) {
		ast_log(LOG_ERROR, "Transferer channel name does not follow typical channel naming format (tech/address)\n");
		ast_channel_unlock(props->transferer);
		ao2_ref(props, -1);
		return NULL;
	}
	*addr++ = '\0';
	serial = strrchr(addr, '-');
	if (serial) {
		*serial = '\0';
	}
	ast_string_field_set(props, transferer_type, tech);
	ast_string_field_set(props, transferer_addr, addr);

	ast_channel_unlock(props->transferer);

	ast_debug(1, "Allocated attended transfer properties %p for transfer from %s\n",
			props, ast_channel_name(props->transferer));
	return props;
}

/*!
 * \brief Free backlog of stimuli in the queue
 */
static void clear_stimulus_queue(struct attended_transfer_properties *props)
{
	struct stimulus_list *list;
	SCOPED_AO2LOCK(lock, props);

	while ((list = AST_LIST_REMOVE_HEAD(&props->stimulus_queue, next))) {
		ast_free(list);
	}
}

/*!
 * \brief Initiate shutdown of attended transfer properties
 *
 * Calling this indicates that the attended transfer properties are no longer needed
 * because the transfer operation has concluded.
 */
static void attended_transfer_properties_shutdown(struct attended_transfer_properties *props)
{
	ast_debug(1, "Shutting down attended transfer %p\n", props);

	if (props->transferee_bridge) {
		bridge_basic_change_personality(props->transferee_bridge,
				BRIDGE_BASIC_PERSONALITY_NORMAL, NULL);
		ast_bridge_merge_inhibit(props->transferee_bridge, -1);
		ast_bridge_destroy(props->target_bridge, 0);
		props->target_bridge = NULL;
	}

	if (props->transferer) {
		ast_channel_remove_bridge_role(props->transferer, AST_TRANSFERER_ROLE_NAME);
	}

	clear_stimulus_queue(props);

	ao2_cleanup(props);
}

static void stimulate_attended_transfer(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus)
{
	struct stimulus_list *list;

	list = ast_calloc(1, sizeof(*list));
	if (!list) {
		ast_log(LOG_ERROR, "Unable to push event to attended transfer queue. Expect transfer to fail\n");
		return;
	}

	list->stimulus = stimulus;
	ao2_lock(props);
	AST_LIST_INSERT_TAIL(&props->stimulus_queue, list, next);
	ast_cond_signal(&props->cond);
	ao2_unlock(props);
}

static void remove_attended_transfer_stimulus(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus)
{
	struct stimulus_list *list;

	ao2_lock(props);
	AST_LIST_TRAVERSE_SAFE_BEGIN(&props->stimulus_queue, list, next) {
		if (list->stimulus == stimulus) {
			AST_LIST_REMOVE_CURRENT(next);
			ast_free(list);
			break;
		}
	}
	AST_LIST_TRAVERSE_SAFE_END;
	ao2_unlock(props);
}

/*!
 * \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,
		struct ast_channel *transferee_channel, struct ast_channel *target_channel)
	struct ast_attended_transfer_message *transfer_msg;
	transfer_msg = ast_attended_transfer_message_create(0, props->transferer,
			props->transferee_bridge, props->transferer, props->target_bridge,
			transferee_channel, target_channel);
	if (!transfer_msg) {
		ast_log(LOG_ERROR, "Unable to publish successful attended transfer from %s\n",
				ast_channel_name(props->transferer));
		return;

	ast_attended_transfer_message_add_merge(transfer_msg, props->transferee_bridge);
	ast_bridge_publish_attended_transfer(transfer_msg);
	ao2_cleanup(transfer_msg);
}

/*!
 * \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,
		struct ast_channel *transferee_channel, struct ast_channel *target_channel)
	struct ast_attended_transfer_message *transfer_msg;
	transfer_msg = ast_attended_transfer_message_create(0, props->transferer,
			props->transferee_bridge, props->transferer, props->target_bridge,
			transferee_channel, target_channel);
	if (!transfer_msg) {
		ast_log(LOG_ERROR, "Unable to publish successful three-way transfer from %s\n",
				ast_channel_name(props->transferer));
		return;

	ast_attended_transfer_message_add_threeway(transfer_msg, props->transferer,
			props->transferee_bridge);
	ast_bridge_publish_attended_transfer(transfer_msg);
	ao2_cleanup(transfer_msg);
}

/*!
 * \brief Send a stasis publication for a failed attended transfer
 */
static void publish_transfer_fail(struct attended_transfer_properties *props)
{
	struct ast_attended_transfer_message *transfer_msg;
	transfer_msg = ast_attended_transfer_message_create(0, props->transferer,
			props->transferee_bridge, props->transferer, props->target_bridge,
			NULL, NULL);
	if (!transfer_msg) {
		ast_log(LOG_ERROR, "Unable to publish failed transfer from %s\n",
				ast_channel_name(props->transferer));
		return;

	transfer_msg->result = AST_BRIDGE_TRANSFER_FAIL;
	ast_bridge_publish_attended_transfer(transfer_msg);
	ao2_cleanup(transfer_msg);
}

/*!
 * \brief Helper method to play a sound on a channel in a bridge
 *
 * \param chan The channel to play the sound to
 * \param sound The sound to play
 */
static void play_sound(struct ast_channel *chan, const char *sound)
{
	struct ast_bridge_channel *bridge_channel;

	ast_channel_lock(chan);
	bridge_channel = ast_channel_get_bridge_channel(chan);
	ast_channel_unlock(chan);

	if (bridge_channel) {
		ast_bridge_channel_queue_playfile(bridge_channel, NULL, sound, NULL);
		ao2_ref(bridge_channel, -1);
/*!
 * \brief Helper method to play a fail sound on a channel in a bridge
 *
 * \param chan The channel to play the fail sound to
 */
static void play_failsound(struct ast_channel *chan)
{
	char *sound;

	ast_channel_lock(chan);
	sound = ast_get_chan_features_xferfailsound(chan);
	ast_channel_unlock(chan);

	if (sound) {
		play_sound(chan, sound);
		ast_free(sound);
	}
}

/*!
 * \brief Helper method to stream a fail sound on a channel
 *
 * \param chan The channel to stream the fail sound to
 */
static void stream_failsound(struct ast_channel *chan)
{
	char *sound;

	ast_channel_lock(chan);
	sound = ast_get_chan_features_xferfailsound(chan);
	ast_channel_unlock(chan);

	if (sound) {
		ast_stream_and_wait(chan, sound, AST_DIGIT_NONE);
		ast_free(sound);
	}
}

/*!
 * \brief Helper method to place a channel in a bridge on hold
 */
static void hold(struct ast_channel *chan)
{
	struct ast_bridge_channel *bridge_channel;
	ast_channel_lock(chan);
	bridge_channel = ast_channel_get_bridge_channel(chan);
	ast_channel_unlock(chan);
		ast_bridge_channel_write_hold(bridge_channel, NULL);
	}
}

/*!
 * \brief Helper method to take a channel in a bridge off hold
 */
static void unhold(struct ast_channel *chan)
{
	struct ast_bridge_channel *bridge_channel;

	if (!chan) {
		return;
	}

	ast_channel_lock(chan);
	bridge_channel = ast_channel_get_bridge_channel(chan);
	ast_channel_unlock(chan);

	if (bridge_channel) {
		ast_bridge_channel_write_unhold(bridge_channel);
		ao2_ref(bridge_channel, -1);
	}
}

/*!
 * \brief Helper method to send a ringing indication to a channel in a bridge
 */
static void ringing(struct ast_channel *chan)
{
	struct ast_bridge_channel *bridge_channel;

	ast_channel_lock(chan);
	bridge_channel = ast_channel_get_bridge_channel(chan);
	ast_channel_unlock(chan);

	if (bridge_channel) {
		ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_RINGING, NULL, 0);
		ao2_ref(bridge_channel, -1);
	}
}

/*!
 * \brief Helper method to send a ringing indication to all channels in a bridge
 */
static void bridge_ringing(struct ast_bridge *bridge)
{
	struct ast_frame ringing = {
		.frametype = AST_FRAME_CONTROL,
		.subclass.integer = AST_CONTROL_RINGING,
	};

	ast_bridge_queue_everyone_else(bridge, NULL, &ringing);
}

/*!
 * \brief Helper method to send a hold frame to all channels in a bridge
 */
static void bridge_hold(struct ast_bridge *bridge)
{
	struct ast_frame hold = {
		.frametype = AST_FRAME_CONTROL,
		.subclass.integer = AST_CONTROL_HOLD,
	};

	ast_bridge_queue_everyone_else(bridge, NULL, &hold);
}

/*!
 * \brief Helper method to send an unhold frame to all channels in a bridge
 */
static void bridge_unhold(struct ast_bridge *bridge)
{
	struct ast_frame unhold = {
		.frametype = AST_FRAME_CONTROL,
		.subclass.integer = AST_CONTROL_UNHOLD,
	};

	ast_bridge_queue_everyone_else(bridge, NULL, &unhold);
}

/*!
 * \brief Wrapper for \ref bridge_do_move
static void bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct ast_channel *channel, struct ast_channel *swap)
	struct ast_bridge_channel *bridge_channel;

	ast_bridge_lock_both(src, dest);

	ast_channel_lock(channel);
	bridge_channel = ast_channel_get_bridge_channel(channel);
	ast_channel_unlock(channel);

	if (bridge_channel) {
		ao2_lock(bridge_channel);
		bridge_channel->swap = swap;
		ao2_unlock(bridge_channel);
		bridge_do_move(dest, bridge_channel, 1, 0);
	}

	ast_bridge_unlock(dest);
	ast_bridge_unlock(src);

 * \brief Wrapper for \ref bridge_do_merge
 */
static void bridge_merge(struct ast_bridge *dest, struct ast_bridge *src, struct ast_channel **kick_channels, unsigned int num_channels)
{
	struct ast_bridge_channel **kick_bridge_channels = num_channels ?
		ast_alloca(num_channels * sizeof(*kick_bridge_channels)) : NULL;
	int i;
	int num_bridge_channels = 0;

	ast_bridge_lock_both(dest, src);

	for (i = 0; i < num_channels; ++i) {
		struct ast_bridge_channel *kick_bridge_channel;

		kick_bridge_channel = bridge_find_channel(src, kick_channels[i]);
		if (!kick_bridge_channel) {
			kick_bridge_channel = bridge_find_channel(dest, kick_channels[i]);
		}

		/* It's possible (and fine) for the bridge channel to be NULL at this point if the
		 * channel has hung up already. If that happens, we can just remove it from the list
		 * of bridge channels to kick from the bridge
		 */
		if (!kick_bridge_channel) {
			continue;
		}

		kick_bridge_channels[num_bridge_channels++] = kick_bridge_channel;
	}

	bridge_do_merge(dest, src, kick_bridge_channels, num_bridge_channels, 0);
	ast_bridge_unlock(dest);
	ast_bridge_unlock(src);

/*!
 * \brief Flags that indicate properties of attended transfer states
 */
enum attended_transfer_state_flags {
	/*! This state requires that the timer be reset when entering the state */
	TRANSFER_STATE_FLAG_TIMER_RESET = (1 << 0),
	/*! This state's timer uses atxferloopdelay */
	TRANSFER_STATE_FLAG_TIMER_LOOP_DELAY = (1 << 1),
	/*! This state's timer uses atxfernoanswertimeout */
	TRANSFER_STATE_FLAG_ATXFER_NO_ANSWER = (1 << 2),
	/*! This state has a time limit associated with it */
	TRANSFER_STATE_FLAG_TIMED = (TRANSFER_STATE_FLAG_TIMER_RESET |
			TRANSFER_STATE_FLAG_TIMER_LOOP_DELAY | TRANSFER_STATE_FLAG_ATXFER_NO_ANSWER),
	/*! This state does not transition to any other states */
	TRANSFER_STATE_FLAG_TERMINAL = (1 << 3),
};

static int calling_target_enter(struct attended_transfer_properties *props);
static enum attended_transfer_state calling_target_exit(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus);

static int hesitant_enter(struct attended_transfer_properties *props);
static enum attended_transfer_state hesitant_exit(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus);

static int rebridge_enter(struct attended_transfer_properties *props);

static int resume_enter(struct attended_transfer_properties *props);

static int threeway_enter(struct attended_transfer_properties *props);

static int consulting_enter(struct attended_transfer_properties *props);
static enum attended_transfer_state consulting_exit(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus);

static int double_checking_enter(struct attended_transfer_properties *props);
static enum attended_transfer_state double_checking_exit(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus);

static int complete_enter(struct attended_transfer_properties *props);

static int blond_enter(struct attended_transfer_properties *props);

static int blond_nonfinal_enter(struct attended_transfer_properties *props);
static enum attended_transfer_state blond_nonfinal_exit(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus);

static int recalling_enter(struct attended_transfer_properties *props);
static enum attended_transfer_state recalling_exit(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus);

static int wait_to_retransfer_enter(struct attended_transfer_properties *props);
static enum attended_transfer_state wait_to_retransfer_exit(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus);

static int retransfer_enter(struct attended_transfer_properties *props);
static enum attended_transfer_state retransfer_exit(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus);

static int wait_to_recall_enter(struct attended_transfer_properties *props);
static enum attended_transfer_state wait_to_recall_exit(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus);

static int fail_enter(struct attended_transfer_properties *props);

/*!
 * \brief Properties of an attended transfer state
 */
struct attended_transfer_state_properties {
	/*! The name of the state. Used for debugging */
	const char *state_name;
	/*! Function used to enter a state */
	int (*enter)(struct attended_transfer_properties *props);
	/*!
	 * Function used to exit a state
	 * This is used both to determine what the next state
	 * to transition to will be and to perform any cleanup
	 * necessary before exiting the current state.
	 */
	enum attended_transfer_state (*exit)(struct attended_transfer_properties *props,
			enum attended_transfer_stimulus stimulus);
	/*! Flags associated with this state */
	enum attended_transfer_state_flags flags;
};

static const struct attended_transfer_state_properties state_properties[] = {
	[TRANSFER_CALLING_TARGET] = {
		.state_name = "Calling Target",
		.enter = calling_target_enter,
		.exit = calling_target_exit,
		.flags = TRANSFER_STATE_FLAG_ATXFER_NO_ANSWER | TRANSFER_STATE_FLAG_TIMER_RESET,
	},
	[TRANSFER_HESITANT] = {
		.state_name = "Hesitant",
		.enter = hesitant_enter,
		.exit = hesitant_exit,
		.flags = TRANSFER_STATE_FLAG_ATXFER_NO_ANSWER,
	},
	[TRANSFER_REBRIDGE] = {
		.state_name = "Rebridge",
		.enter = rebridge_enter,
		.flags = TRANSFER_STATE_FLAG_TERMINAL,
	},
	[TRANSFER_RESUME] = {
		.state_name = "Resume",
		.enter = resume_enter,
		.flags = TRANSFER_STATE_FLAG_TERMINAL,
	},
	[TRANSFER_THREEWAY] = {
		.state_name = "Threeway",
		.enter = threeway_enter,
		.flags = TRANSFER_STATE_FLAG_TERMINAL,
	},
	[TRANSFER_CONSULTING] = {
		.state_name = "Consulting",
		.enter = consulting_enter,
		.exit = consulting_exit,
	},
	[TRANSFER_DOUBLECHECKING] = {
		.state_name = "Double Checking",
		.enter = double_checking_enter,
		.exit = double_checking_exit,
	},
	[TRANSFER_COMPLETE] = {
		.state_name = "Complete",
		.enter = complete_enter,
		.flags = TRANSFER_STATE_FLAG_TERMINAL,
	},
	[TRANSFER_BLOND] = {
		.state_name = "Blond",
		.enter = blond_enter,
		.flags = TRANSFER_STATE_FLAG_TERMINAL,
	},
	[TRANSFER_BLOND_NONFINAL] = {
		.state_name = "Blond Non-Final",
		.enter = blond_nonfinal_enter,
		.exit = blond_nonfinal_exit,
		.flags = TRANSFER_STATE_FLAG_ATXFER_NO_ANSWER,
	},
	[TRANSFER_RECALLING] = {
		.state_name = "Recalling",
		.enter = recalling_enter,
		.exit = recalling_exit,
		.flags = TRANSFER_STATE_FLAG_ATXFER_NO_ANSWER | TRANSFER_STATE_FLAG_TIMER_RESET,
	},
	[TRANSFER_WAIT_TO_RETRANSFER] = {
		.state_name = "Wait to Retransfer",
		.enter = wait_to_retransfer_enter,
		.exit = wait_to_retransfer_exit,
		.flags = TRANSFER_STATE_FLAG_TIMER_RESET | TRANSFER_STATE_FLAG_TIMER_LOOP_DELAY,
	},
	[TRANSFER_RETRANSFER] = {
		.state_name = "Retransfer",
		.enter = retransfer_enter,
		.exit = retransfer_exit,
		.flags = TRANSFER_STATE_FLAG_ATXFER_NO_ANSWER | TRANSFER_STATE_FLAG_TIMER_RESET,
	},
	[TRANSFER_WAIT_TO_RECALL] = {
		.state_name = "Wait to Recall",
		.enter = wait_to_recall_enter,
		.exit = wait_to_recall_exit,
		.flags = TRANSFER_STATE_FLAG_TIMER_RESET | TRANSFER_STATE_FLAG_TIMER_LOOP_DELAY,
	},
	[TRANSFER_FAIL] = {
		.state_name = "Fail",
		.enter = fail_enter,
		.flags = TRANSFER_STATE_FLAG_TERMINAL,
	},
};

static int calling_target_enter(struct attended_transfer_properties *props)
{
	bridge_move(props->target_bridge, props->transferee_bridge, props->transferer, NULL);
	return 0;
}

static enum attended_transfer_state calling_target_exit(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus)
{
	switch (stimulus) {
	case STIMULUS_TRANSFEREE_HANGUP:
		play_failsound(props->transferer);
		publish_transfer_fail(props);
		return TRANSFER_FAIL;
	case STIMULUS_DTMF_ATXFER_COMPLETE:
	case STIMULUS_TRANSFERER_HANGUP:
		bridge_unhold(props->transferee_bridge);
		return props->atxferdropcall ? TRANSFER_BLOND : TRANSFER_BLOND_NONFINAL;
	case STIMULUS_TRANSFER_TARGET_ANSWER:
		return TRANSFER_CONSULTING;
	case STIMULUS_TRANSFER_TARGET_HANGUP:
	case STIMULUS_TIMEOUT:
	case STIMULUS_DTMF_ATXFER_ABORT:
		play_failsound(props->transferer);
		return TRANSFER_REBRIDGE;
	case STIMULUS_DTMF_ATXFER_THREEWAY:
		bridge_unhold(props->transferee_bridge);
		return TRANSFER_THREEWAY;
	case STIMULUS_DTMF_ATXFER_SWAP:
		return TRANSFER_HESITANT;
	case STIMULUS_NONE:
	case STIMULUS_RECALL_TARGET_ANSWER:
	case STIMULUS_RECALL_TARGET_HANGUP:
	default:
		ast_log(LOG_WARNING, "Unexpected stimulus '%s' received in attended transfer state '%s'\n",
				stimulus_strs[stimulus], state_properties[props->state].state_name);
		return props->state;
	}
}

static int hesitant_enter(struct attended_transfer_properties *props)
{
	bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL);
	unhold(props->transferer);
	return 0;
}

static enum attended_transfer_state hesitant_exit(struct attended_transfer_properties *props,
		enum attended_transfer_stimulus stimulus)
{
	switch (stimulus) {
	case STIMULUS_TRANSFEREE_HANGUP:
		play_failsound(props->transferer);
		publish_transfer_fail(props);
		return TRANSFER_FAIL;
	case STIMULUS_DTMF_ATXFER_COMPLETE:
	case STIMULUS_TRANSFERER_HANGUP:
		return props->atxferdropcall ? TRANSFER_BLOND : TRANSFER_BLOND_NONFINAL;
	case STIMULUS_TRANSFER_TARGET_ANSWER:
		return TRANSFER_DOUBLECHECKING;
	case STIMULUS_TRANSFER_TARGET_HANGUP:
	case STIMULUS_TIMEOUT:
	case STIMULUS_DTMF_ATXFER_ABORT:
		play_failsound(props->transferer);
		return TRANSFER_RESUME;
	case STIMULUS_DTMF_ATXFER_THREEWAY:
		return TRANSFER_THREEWAY;
	case STIMULUS_DTMF_ATXFER_SWAP:
		hold(props->transferer);
		return TRANSFER_CALLING_TARGET;
	case STIMULUS_NONE:
	case STIMULUS_RECALL_TARGET_HANGUP:
	case STIMULUS_RECALL_TARGET_ANSWER:
	default:
		ast_log(LOG_WARNING, "Unexpected stimulus '%s' received in attended transfer state '%s'\n",
				stimulus_strs[stimulus], state_properties[props->state].state_name);
		return props->state;
	}
}

static int rebridge_enter(struct attended_transfer_properties *props)
{
	bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL);
	unhold(props->transferer);
	return 0;
}

static int resume_enter(struct attended_transfer_properties *props)
{
	return 0;
}

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);
Loading
Loading full blame...