[asterisk-commits] mmichelson: branch mmichelson/atxfer_features r393362 - /team/mmichelson/atxf...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Jul 1 16:25:09 CDT 2013


Author: mmichelson
Date: Mon Jul  1 16:25:07 2013
New Revision: 393362

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=393362
Log:
Add documentation to states and remove unnecessary assertions from state change functions.


Modified:
    team/mmichelson/atxfer_features/main/bridging_basic.c

Modified: team/mmichelson/atxfer_features/main/bridging_basic.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/atxfer_features/main/bridging_basic.c?view=diff&rev=393362&r1=393361&r2=393362
==============================================================================
--- team/mmichelson/atxfer_features/main/bridging_basic.c (original)
+++ team/mmichelson/atxfer_features/main/bridging_basic.c Mon Jul  1 16:25:07 2013
@@ -1173,494 +1173,66 @@
 }
 
 /*!
- * \brief enter callback for TRANSFER_CALLING_TARGET state
- *
- * This is the opening state when performing an attended transfer. The goal
- * of this callback is simply to place the transferer into the target bridge.
+ * \brief Flags that indicate properties of attended transfer states
  */
-static int calling_target_enter(struct attended_transfer_properties *props)
-{
-	return bridge_move(props->target_bridge, props->transferee_bridge, props->transferer, NULL);
-}
-
-static enum attended_transfer_state calling_target_next(struct attended_transfer_properties *props,
-		enum attended_transfer_stimulus stimulus)
-{
-	switch (stimulus) {
-	default:
-	case STIMULUS_NONE:
-	case STIMULUS_RECALL_TARGET_ANSWER:
-	case STIMULUS_RECALL_TARGET_HANGUP:
-		ast_assert(0);
-	case STIMULUS_TRANSFEREE_HANGUP:
-		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:
-		return TRANSFER_REBRIDGE;
-	case STIMULUS_DTMF_ATXFER_THREEWAY:
-		bridge_unhold(props->transferee_bridge);
-		return TRANSFER_THREEWAY;
-	case STIMULUS_DTMF_ATXFER_SWAP:
-		return TRANSFER_HESITANT;
-	}
-}
-
-static int hesitant_enter(struct attended_transfer_properties *props)
-{
-	if (bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL)) {
-		return -1;
-	}
-
-	unhold(props->transferer);
-	return 0;
-}
-
-static enum attended_transfer_state hesitant_next(struct attended_transfer_properties *props,
-		enum attended_transfer_stimulus stimulus)
-{
-	switch (stimulus) {
-	default:
-	case STIMULUS_NONE:
-	case STIMULUS_RECALL_TARGET_HANGUP:
-	case STIMULUS_RECALL_TARGET_ANSWER:
-		ast_assert(0);
-	case STIMULUS_TRANSFEREE_HANGUP:
-		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_CONSULTING;
-	case STIMULUS_TRANSFER_TARGET_HANGUP:
-	case STIMULUS_TIMEOUT:
-	case STIMULUS_DTMF_ATXFER_ABORT:
-		return TRANSFER_RESUME;
-	case STIMULUS_DTMF_ATXFER_THREEWAY:
-		return TRANSFER_THREEWAY;
-	case STIMULUS_DTMF_ATXFER_SWAP:
-		hold(props->transferer);
-		return TRANSFER_CALLING_TARGET;
-	}
-}
-
-static int rebridge_enter(struct attended_transfer_properties *props)
-{
-	if (bridge_move(props->transferee_bridge, props->target_bridge,
-			props->transferer, NULL)) {
-		return -1;
-	}
-
-	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)
-{
-	bridge_merge(props->transferee_bridge, props->target_bridge, NULL, 0);
-
-	return 0;
-}
-
-static int consulting_enter(struct attended_transfer_properties *props)
-{
-	RAII_VAR(struct ast_bridge *, transferer_bridge, NULL, ao2_cleanup);
-
-	ast_channel_lock(props->transferer);
-	transferer_bridge = ast_channel_get_bridge(props->transferer);
-	ast_channel_unlock(props->transferer);
-
-	ast_assert(transferer_bridge != NULL);
-
-	if (transferer_bridge == props->target_bridge) {
-		return 0;
-	}
-
-	if (bridge_move(props->target_bridge, props->transferee_bridge, props->transferer, NULL)) {
-		return -1;
-	}
-	unhold(props->transferer);
-	return 0;
-}
-
-static enum attended_transfer_state consulting_next(struct attended_transfer_properties *props,
-		enum attended_transfer_stimulus stimulus)
-{
-	switch (stimulus) {
-	default:
-	case STIMULUS_NONE:
-	case STIMULUS_TIMEOUT:
-	case STIMULUS_TRANSFER_TARGET_ANSWER:
-	case STIMULUS_RECALL_TARGET_HANGUP:
-	case STIMULUS_RECALL_TARGET_ANSWER:
-		ast_assert(0);
-	case STIMULUS_TRANSFEREE_HANGUP:
-		/* XXX I have absolutely no idea how to handle this case */
-		return TRANSFER_FAIL;
-	case STIMULUS_TRANSFERER_HANGUP:
-	case STIMULUS_DTMF_ATXFER_COMPLETE:
-		/* We know the transferer is in the target_bridge, so take the other bridge off hold */
-		bridge_unhold(props->transferee_bridge);
-		return TRANSFER_COMPLETE;
-	case STIMULUS_TRANSFER_TARGET_HANGUP:
-	case STIMULUS_DTMF_ATXFER_ABORT:
-		return TRANSFER_REBRIDGE;
-	case STIMULUS_DTMF_ATXFER_THREEWAY:
-		bridge_unhold(props->transferee_bridge);
-		return TRANSFER_THREEWAY;
-	case STIMULUS_DTMF_ATXFER_SWAP:
-		hold(props->transferer);
-		return TRANSFER_DOUBLECHECKING;
-	}
-}
-
-static int double_checking_enter(struct attended_transfer_properties *props)
-{
-	struct ast_bridge *transferer_bridge;
-
-	ast_channel_lock(props->transferer);
-	transferer_bridge = ast_channel_internal_bridge(props->transferer);
-	ast_channel_unlock(props->transferer);
-
-	ast_assert(transferer_bridge != NULL);
-
-	if (transferer_bridge == props->transferee_bridge) {
-		return 0;
-	}
-	if (bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL)) {
-		return -1;
-	}
-	unhold(props->transferer);
-	return 0;
-}
-
-static enum attended_transfer_state double_checking_next(struct attended_transfer_properties *props,
-		enum attended_transfer_stimulus stimulus)
-{
-	switch (stimulus) {
-	default:
-	case STIMULUS_NONE:
-	case STIMULUS_TIMEOUT:
-	case STIMULUS_TRANSFER_TARGET_ANSWER:
-	case STIMULUS_RECALL_TARGET_HANGUP:
-	case STIMULUS_RECALL_TARGET_ANSWER:
-		ast_assert(0);
-	case STIMULUS_TRANSFEREE_HANGUP:
-		return TRANSFER_FAIL;
-	case STIMULUS_TRANSFERER_HANGUP:
-	case STIMULUS_DTMF_ATXFER_COMPLETE:
-		/* We know the transferer is in the transferee, so take the other bridge off hold */
-		bridge_unhold(props->target_bridge);
-		return TRANSFER_COMPLETE;
-	case STIMULUS_TRANSFER_TARGET_HANGUP:
-	case STIMULUS_DTMF_ATXFER_ABORT:
-		return TRANSFER_RESUME;
-	case STIMULUS_DTMF_ATXFER_THREEWAY:
-		bridge_unhold(props->target_bridge);
-		return TRANSFER_THREEWAY;
-	case STIMULUS_DTMF_ATXFER_SWAP:
-		hold(props->transferer);
-		return TRANSFER_CONSULTING;
-	}
-}
-
-static int complete_enter(struct attended_transfer_properties *props)
-{
-	bridge_merge(props->transferee_bridge, props->target_bridge, &props->transferer, 1);
-	return 0;
-}
-
-static int blond_enter(struct attended_transfer_properties *props)
-{
-	bridge_merge(props->transferee_bridge, props->target_bridge, &props->transferer, 1);
-	ringing(props->transfer_target);
-	return 0;
-}
-
-static int blond_nonfinal_enter(struct attended_transfer_properties *props)
-{
-	props->superstate = SUPERSTATE_RECALL;
-	return blond_enter(props);
-}
-
-static enum attended_transfer_state blond_nonfinal_next(struct attended_transfer_properties *props,
-		enum attended_transfer_stimulus stimulus)
-{
-	switch (stimulus) {
-	default:
-	case STIMULUS_NONE:
-	case STIMULUS_DTMF_ATXFER_ABORT:
-	case STIMULUS_DTMF_ATXFER_COMPLETE:
-	case STIMULUS_DTMF_ATXFER_THREEWAY:
-	case STIMULUS_DTMF_ATXFER_SWAP:
-	case STIMULUS_TRANSFERER_HANGUP:
-	case STIMULUS_TRANSFER_TARGET_HANGUP:
-	case STIMULUS_TRANSFER_TARGET_ANSWER:
-		ast_assert(0);
-	case STIMULUS_TRANSFEREE_HANGUP:
-		return TRANSFER_FAIL;
-	case STIMULUS_RECALL_TARGET_ANSWER:
-		return TRANSFER_RESUME;
-	case STIMULUS_TIMEOUT:
-		ast_softhangup(props->transfer_target, AST_SOFTHANGUP_EXPLICIT);
-	case STIMULUS_RECALL_TARGET_HANGUP:
-		return TRANSFER_RECALLING;
-	}
-}
-
-static void recall_callback(struct ast_dial *dial);
-
-static int recalling_enter(struct attended_transfer_properties *props)
-{
-	RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc_nolock(), ast_format_cap_destroy);
-	struct ast_format fmt;
-
-	if (!cap) {
-		return -1;
-	}
-
-	ast_format_cap_add(cap, ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0));
-
-	/* When we dial the transfer target, since we are communicating
-	 * with a local channel, we can place the local channel in a bridge
-	 * and then call out to it. When recalling the transferer, though, we
-	 * have to use the dialing API because the channel is not local.
-	 */
-	props->dial = ast_dial_create();
-	if (!props->dial) {
-		return -1;
-	}
-
-	if (ast_dial_append(props->dial, props->transferer_type, props->transferer_addr)) {
-		return -1;
-	}
-
-	if (ast_dial_prerun(props->dial, NULL, cap)) {
-		return -1;
-	}
-
-	ast_dial_set_state_callback(props->dial, &recall_callback);
-
-	ao2_ref(props, +1);
-	ast_dial_set_user_data(props->dial, props);
-
-	if (ast_dial_run(props->dial, NULL, 1) == AST_DIAL_RESULT_FAILED) {
-		ao2_ref(props, -1);
-		return -1;
-	}
-
-	bridge_ringing(props->transferee_bridge);
-	return 0;
-}
-
-static enum attended_transfer_state recalling_next(struct attended_transfer_properties *props,
-		enum attended_transfer_stimulus stimulus)
-{
-	/* No matter what the outcome was, we need to kill off the dial */
-	ast_dial_join(props->dial);
-	ast_dial_destroy(props->dial);
-	props->dial = NULL;
-	/* This reference is the one we incremented for the dial state callback (recall_callback) to use */
-	ao2_ref(props, -1);
-
-	switch (stimulus) {
-	default:
-	case STIMULUS_NONE:
-	case STIMULUS_DTMF_ATXFER_ABORT:
-	case STIMULUS_DTMF_ATXFER_COMPLETE:
-	case STIMULUS_DTMF_ATXFER_THREEWAY:
-	case STIMULUS_DTMF_ATXFER_SWAP:
-	case STIMULUS_TRANSFER_TARGET_HANGUP:
-	case STIMULUS_TRANSFER_TARGET_ANSWER:
-	case STIMULUS_TRANSFERER_HANGUP:
-		ast_assert(0);
-	case STIMULUS_TRANSFEREE_HANGUP:
-		return TRANSFER_FAIL;
-	case STIMULUS_TIMEOUT:
-	case STIMULUS_RECALL_TARGET_HANGUP:
-		++props->retry_attempts;
-		if (props->retry_attempts >= props->atxfercallbackretries) {
-			return TRANSFER_FAIL;
-		}
-		if (props->atxferloopdelay) {
-			return TRANSFER_WAIT_TO_RETRANSFER;
-		}
-		return TRANSFER_RETRANSFER;
-	case STIMULUS_RECALL_TARGET_ANSWER:
-		ast_bridge_impart(props->transferee_bridge, props->recall_target, NULL, NULL, 1);
-		return TRANSFER_RESUME;
-	}
-}
-
-static int wait_to_retransfer_enter(struct attended_transfer_properties *props)
-{
-	bridge_hold(props->transferee_bridge);
-	return 0;
-}
-
-static enum attended_transfer_state wait_to_retransfer_next(struct attended_transfer_properties *props,
-		enum attended_transfer_stimulus stimulus)
-{
-	bridge_unhold(props->transferee_bridge);
-	switch (stimulus) {
-	default:
-	case STIMULUS_NONE:
-	case STIMULUS_DTMF_ATXFER_ABORT:
-	case STIMULUS_DTMF_ATXFER_COMPLETE:
-	case STIMULUS_DTMF_ATXFER_THREEWAY:
-	case STIMULUS_DTMF_ATXFER_SWAP:
-	case STIMULUS_TRANSFER_TARGET_HANGUP:
-	case STIMULUS_TRANSFER_TARGET_ANSWER:
-	case STIMULUS_TRANSFERER_HANGUP:
-	case STIMULUS_RECALL_TARGET_HANGUP:
-	case STIMULUS_RECALL_TARGET_ANSWER:
-		ast_assert(0);
-	case STIMULUS_TRANSFEREE_HANGUP:
-		return TRANSFER_FAIL;
-	case STIMULUS_TIMEOUT:
-		return TRANSFER_RETRANSFER;
-	}
-}
-
-static int attach_framehook(struct attended_transfer_properties *props, struct ast_channel *channel);
-
-static int retransfer_enter(struct attended_transfer_properties *props)
-{
-	RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc_nolock(), ast_format_cap_destroy);
-	struct ast_format fmt;
-	char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2];
-	int cause;
-
-	if (props->recall_target) {
-		ast_log(LOG_NOTICE, "props->recall_target is non-NULL and its refcount is %d\n", ao2_ref(props->recall_target, 0));
-	} else {
-		ast_log(LOG_NOTICE, "props->recall_target is NULL\n");
-	}
-	
-	if (!cap) {
-		return -1;
-	}
-
-	snprintf(destination, sizeof(destination), "%s@%s", props->exten, props->context);
-
-	ast_format_cap_add(cap, ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0));
-
-	/* Get a channel that is the destination we wish to call */
-	props->recall_target = ast_request("Local", cap, NULL, destination, &cause);
-	if (!props->recall_target) {
-		ast_log(LOG_ERROR, "Unable to request outbound channel for recall target\n");
-		return -1;
-	}
-
-	if (attach_framehook(props, props->recall_target)) {
-		ast_log(LOG_ERROR, "Unable to attach framehook to recall target\n");
-		ast_hangup(props->recall_target);
-		props->recall_target = NULL;
-		return -1;
-	}
-
-	if (ast_call(props->recall_target, destination, 0)) {
-		ast_log(LOG_ERROR, "Unable to place outbound call to recall target\n");
-		ast_hangup(props->recall_target);
-		props->recall_target = NULL;
-		return -1;
-	}
-
-	ast_channel_ref(props->recall_target);
-	if (ast_bridge_impart(props->transferee_bridge, props->recall_target, NULL, NULL, 1)) {
-		ast_log(LOG_ERROR, "Unable to place recall target into bridge\n");
-		ast_hangup(props->recall_target);
-		return -1;
-	}
-
-	return 0;
-}
-
-static enum attended_transfer_state retransfer_next(struct attended_transfer_properties *props,
-		enum attended_transfer_stimulus stimulus)
-{
-	switch (stimulus) {
-	default:
-	case STIMULUS_NONE:
-	case STIMULUS_DTMF_ATXFER_ABORT:
-	case STIMULUS_DTMF_ATXFER_COMPLETE:
-	case STIMULUS_DTMF_ATXFER_THREEWAY:
-	case STIMULUS_DTMF_ATXFER_SWAP:
-	case STIMULUS_TRANSFER_TARGET_HANGUP:
-	case STIMULUS_TRANSFER_TARGET_ANSWER:
-	case STIMULUS_TRANSFERER_HANGUP:
-		ast_assert(0);
-	case STIMULUS_TRANSFEREE_HANGUP:
-		return TRANSFER_FAIL;
-	case STIMULUS_TIMEOUT:
-		ast_softhangup(props->recall_target, AST_SOFTHANGUP_EXPLICIT);
-	case STIMULUS_RECALL_TARGET_HANGUP:
-		props->recall_target = ast_channel_unref(props->recall_target);
-		if (props->atxferloopdelay) {
-			return TRANSFER_WAIT_TO_RECALL;
-		}
-		return TRANSFER_RECALLING;
-	case STIMULUS_RECALL_TARGET_ANSWER:
-		return TRANSFER_RESUME;
-	}
-}
-
-static int wait_to_recall_enter(struct attended_transfer_properties *props)
-{
-	bridge_hold(props->transferee_bridge);
-	return 0;
-}
-
-static enum attended_transfer_state wait_to_recall_next(struct attended_transfer_properties *props,
-		enum attended_transfer_stimulus stimulus)
-{
-	bridge_unhold(props->transferee_bridge);
-	switch (stimulus) {
-	default:
-	case STIMULUS_NONE:
-	case STIMULUS_DTMF_ATXFER_ABORT:
-	case STIMULUS_DTMF_ATXFER_COMPLETE:
-	case STIMULUS_DTMF_ATXFER_THREEWAY:
-	case STIMULUS_DTMF_ATXFER_SWAP:
-	case STIMULUS_TRANSFER_TARGET_HANGUP:
-	case STIMULUS_TRANSFER_TARGET_ANSWER:
-	case STIMULUS_TRANSFERER_HANGUP:
-	case STIMULUS_RECALL_TARGET_HANGUP:
-	case STIMULUS_RECALL_TARGET_ANSWER:
-		ast_assert(0);
-	case STIMULUS_TRANSFEREE_HANGUP:
-		return TRANSFER_FAIL;
-	case STIMULUS_TIMEOUT:
-		return TRANSFER_RECALLING;
-	}
-}
-
-static int fail_enter(struct attended_transfer_properties *props)
-{
-	if (props->transferee_bridge) {
-		ast_bridge_destroy(props->transferee_bridge);
-		props->transferee_bridge = NULL;
-	}
-	return 0;
-}
-
 enum attended_transfer_state_flags {
+	/*! This state has a time limit associated with it */
 	TRANSFER_STATE_IS_TIMED = (1 << 0),
+	/*! This state requires that the timer be reset when entering the state */
 	TRANSFER_STATE_RESET_TIMER = (1 << 1),
+	/*! This state does not transition to any other states */
 	TRANSFER_STATE_IS_TERMINAL = (1 << 2),
+	/*! This state's timer uses atxferloopdelay instead of atxfernoanswertimeout */
 	TRANSFER_STATE_TIMER_LOOP_DELAY = (1 << 3),
 };
+
+static int calling_target_enter(struct attended_transfer_properties *props);
+static enum attended_transfer_state calling_target_next(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_next(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_next(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_next(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_next(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_next(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_next(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_next(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_next(struct attended_transfer_properties *props,
+		enum attended_transfer_stimulus stimulus);
+
+static int fail_enter(struct attended_transfer_properties *props);
 
 static struct attended_transfer_state_properties {
 	const char *state_name;
@@ -1753,6 +1325,507 @@
 	},
 };
 
+/*!
+ * \brief enter callback for TRANSFER_CALLING_TARGET state
+ *
+ * This is the opening state when performing an attended transfer. The goal
+ * of this callback is simply to place the transferer into the target bridge.
+ */
+static int calling_target_enter(struct attended_transfer_properties *props)
+{
+	return bridge_move(props->target_bridge, props->transferee_bridge, props->transferer, NULL);
+}
+
+static enum attended_transfer_state calling_target_next(struct attended_transfer_properties *props,
+		enum attended_transfer_stimulus stimulus)
+{
+	switch (stimulus) {
+	case STIMULUS_TRANSFEREE_HANGUP:
+		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:
+		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)
+{
+	if (bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL)) {
+		return -1;
+	}
+
+	unhold(props->transferer);
+	return 0;
+}
+
+static enum attended_transfer_state hesitant_next(struct attended_transfer_properties *props,
+		enum attended_transfer_stimulus stimulus)
+{
+	switch (stimulus) {
+	case STIMULUS_TRANSFEREE_HANGUP:
+		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_CONSULTING;
+	case STIMULUS_TRANSFER_TARGET_HANGUP:
+	case STIMULUS_TIMEOUT:
+	case STIMULUS_DTMF_ATXFER_ABORT:
+		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)
+{
+	if (bridge_move(props->transferee_bridge, props->target_bridge,
+			props->transferer, NULL)) {
+		return -1;
+	}
+
+	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)
+{
+	bridge_merge(props->transferee_bridge, props->target_bridge, NULL, 0);
+
+	return 0;
+}
+
+static int consulting_enter(struct attended_transfer_properties *props)
+{
+	RAII_VAR(struct ast_bridge *, transferer_bridge, NULL, ao2_cleanup);
+
+	ast_channel_lock(props->transferer);
+	transferer_bridge = ast_channel_get_bridge(props->transferer);
+	ast_channel_unlock(props->transferer);
+
+	ast_assert(transferer_bridge != NULL);
+
+	if (transferer_bridge == props->target_bridge) {
+		return 0;
+	}
+
+	if (bridge_move(props->target_bridge, props->transferee_bridge, props->transferer, NULL)) {
+		return -1;
+	}
+	unhold(props->transferer);
+	return 0;
+}
+
+static enum attended_transfer_state consulting_next(struct attended_transfer_properties *props,
+		enum attended_transfer_stimulus stimulus)
+{
+	switch (stimulus) {
+	case STIMULUS_TRANSFEREE_HANGUP:
+		/* XXX I have absolutely no idea how to handle this case */
+		return TRANSFER_FAIL;
+	case STIMULUS_TRANSFERER_HANGUP:
+	case STIMULUS_DTMF_ATXFER_COMPLETE:
+		/* We know the transferer is in the target_bridge, so take the other bridge off hold */
+		bridge_unhold(props->transferee_bridge);
+		return TRANSFER_COMPLETE;
+	case STIMULUS_TRANSFER_TARGET_HANGUP:
+	case STIMULUS_DTMF_ATXFER_ABORT:
+		return TRANSFER_REBRIDGE;
+	case STIMULUS_DTMF_ATXFER_THREEWAY:
+		bridge_unhold(props->transferee_bridge);
+		return TRANSFER_THREEWAY;
+	case STIMULUS_DTMF_ATXFER_SWAP:
+		hold(props->transferer);
+		return TRANSFER_DOUBLECHECKING;
+	case STIMULUS_NONE:
+	case STIMULUS_TIMEOUT:
+	case STIMULUS_TRANSFER_TARGET_ANSWER:
+	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 double_checking_enter(struct attended_transfer_properties *props)
+{
+	struct ast_bridge *transferer_bridge;
+
+	ast_channel_lock(props->transferer);
+	transferer_bridge = ast_channel_internal_bridge(props->transferer);
+	ast_channel_unlock(props->transferer);
+
+	ast_assert(transferer_bridge != NULL);
+
+	if (transferer_bridge == props->transferee_bridge) {
+		return 0;
+	}
+	if (bridge_move(props->transferee_bridge, props->target_bridge, props->transferer, NULL)) {
+		return -1;
+	}
+	unhold(props->transferer);
+	return 0;
+}
+
+static enum attended_transfer_state double_checking_next(struct attended_transfer_properties *props,
+		enum attended_transfer_stimulus stimulus)
+{
+	switch (stimulus) {
+	case STIMULUS_TRANSFEREE_HANGUP:
+		return TRANSFER_FAIL;
+	case STIMULUS_TRANSFERER_HANGUP:
+	case STIMULUS_DTMF_ATXFER_COMPLETE:
+		/* We know the transferer is in the transferee, so take the other bridge off hold */
+		bridge_unhold(props->target_bridge);
+		return TRANSFER_COMPLETE;
+	case STIMULUS_TRANSFER_TARGET_HANGUP:
+	case STIMULUS_DTMF_ATXFER_ABORT:
+		return TRANSFER_RESUME;
+	case STIMULUS_DTMF_ATXFER_THREEWAY:
+		bridge_unhold(props->target_bridge);
+		return TRANSFER_THREEWAY;
+	case STIMULUS_DTMF_ATXFER_SWAP:
+		hold(props->transferer);
+		return TRANSFER_CONSULTING;
+	case STIMULUS_NONE:
+	case STIMULUS_TIMEOUT:
+	case STIMULUS_TRANSFER_TARGET_ANSWER:
+	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 complete_enter(struct attended_transfer_properties *props)
+{
+	bridge_merge(props->transferee_bridge, props->target_bridge, &props->transferer, 1);
+	return 0;
+}
+
+static int blond_enter(struct attended_transfer_properties *props)
+{
+	bridge_merge(props->transferee_bridge, props->target_bridge, &props->transferer, 1);
+	ringing(props->transfer_target);
+	return 0;
+}
+
+static int blond_nonfinal_enter(struct attended_transfer_properties *props)
+{
+	props->superstate = SUPERSTATE_RECALL;
+	return blond_enter(props);
+}
+
+static enum attended_transfer_state blond_nonfinal_next(struct attended_transfer_properties *props,
+		enum attended_transfer_stimulus stimulus)
+{
+	switch (stimulus) {
+	case STIMULUS_TRANSFEREE_HANGUP:
+		return TRANSFER_FAIL;
+	case STIMULUS_RECALL_TARGET_ANSWER:
+		return TRANSFER_RESUME;
+	case STIMULUS_TIMEOUT:
+		ast_softhangup(props->transfer_target, AST_SOFTHANGUP_EXPLICIT);
+	case STIMULUS_RECALL_TARGET_HANGUP:
+		return TRANSFER_RECALLING;
+	case STIMULUS_NONE:
+	case STIMULUS_DTMF_ATXFER_ABORT:
+	case STIMULUS_DTMF_ATXFER_COMPLETE:
+	case STIMULUS_DTMF_ATXFER_THREEWAY:
+	case STIMULUS_DTMF_ATXFER_SWAP:
+	case STIMULUS_TRANSFERER_HANGUP:
+	case STIMULUS_TRANSFER_TARGET_HANGUP:
+	case STIMULUS_TRANSFER_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 void recall_callback(struct ast_dial *dial);
+
+static int recalling_enter(struct attended_transfer_properties *props)
+{
+	RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc_nolock(), ast_format_cap_destroy);
+	struct ast_format fmt;
+
+	if (!cap) {
+		return -1;
+	}
+
+	ast_format_cap_add(cap, ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0));
+
+	/* When we dial the transfer target, since we are communicating
+	 * with a local channel, we can place the local channel in a bridge
+	 * and then call out to it. When recalling the transferer, though, we
+	 * have to use the dialing API because the channel is not local.
+	 */
+	props->dial = ast_dial_create();
+	if (!props->dial) {
+		return -1;
+	}
+
+	if (ast_dial_append(props->dial, props->transferer_type, props->transferer_addr)) {
+		return -1;
+	}
+
+	if (ast_dial_prerun(props->dial, NULL, cap)) {
+		return -1;
+	}
+
+	ast_dial_set_state_callback(props->dial, &recall_callback);
+
+	ao2_ref(props, +1);
+	ast_dial_set_user_data(props->dial, props);
+
+	if (ast_dial_run(props->dial, NULL, 1) == AST_DIAL_RESULT_FAILED) {
+		ao2_ref(props, -1);
+		return -1;
+	}
+
+	bridge_ringing(props->transferee_bridge);
+	return 0;
+}
+
+static enum attended_transfer_state recalling_next(struct attended_transfer_properties *props,
+		enum attended_transfer_stimulus stimulus)
+{
+	/* No matter what the outcome was, we need to kill off the dial */
+	ast_dial_join(props->dial);
+	ast_dial_destroy(props->dial);
+	props->dial = NULL;
+	/* This reference is the one we incremented for the dial state callback (recall_callback) to use */
+	ao2_ref(props, -1);
+
+	switch (stimulus) {
+	case STIMULUS_TRANSFEREE_HANGUP:
+		return TRANSFER_FAIL;
+	case STIMULUS_TIMEOUT:
+	case STIMULUS_RECALL_TARGET_HANGUP:
+		++props->retry_attempts;
+		if (props->retry_attempts >= props->atxfercallbackretries) {
+			return TRANSFER_FAIL;
+		}
+		if (props->atxferloopdelay) {
+			return TRANSFER_WAIT_TO_RETRANSFER;
+		}
+		return TRANSFER_RETRANSFER;
+	case STIMULUS_RECALL_TARGET_ANSWER:
+		ast_bridge_impart(props->transferee_bridge, props->recall_target, NULL, NULL, 1);
+		return TRANSFER_RESUME;
+	case STIMULUS_NONE:
+	case STIMULUS_DTMF_ATXFER_ABORT:
+	case STIMULUS_DTMF_ATXFER_COMPLETE:
+	case STIMULUS_DTMF_ATXFER_THREEWAY:
+	case STIMULUS_DTMF_ATXFER_SWAP:
+	case STIMULUS_TRANSFER_TARGET_HANGUP:
+	case STIMULUS_TRANSFER_TARGET_ANSWER:
+	case STIMULUS_TRANSFERER_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 wait_to_retransfer_enter(struct attended_transfer_properties *props)
+{
+	bridge_hold(props->transferee_bridge);
+	return 0;
+}
+
+static enum attended_transfer_state wait_to_retransfer_next(struct attended_transfer_properties *props,
+		enum attended_transfer_stimulus stimulus)
+{
+	bridge_unhold(props->transferee_bridge);
+	switch (stimulus) {
+	case STIMULUS_TRANSFEREE_HANGUP:
+		return TRANSFER_FAIL;
+	case STIMULUS_TIMEOUT:
+		return TRANSFER_RETRANSFER;
+	case STIMULUS_NONE:
+	case STIMULUS_DTMF_ATXFER_ABORT:
+	case STIMULUS_DTMF_ATXFER_COMPLETE:
+	case STIMULUS_DTMF_ATXFER_THREEWAY:
+	case STIMULUS_DTMF_ATXFER_SWAP:
+	case STIMULUS_TRANSFER_TARGET_HANGUP:
+	case STIMULUS_TRANSFER_TARGET_ANSWER:
+	case STIMULUS_TRANSFERER_HANGUP:
+	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 attach_framehook(struct attended_transfer_properties *props, struct ast_channel *channel);
+
+static int retransfer_enter(struct attended_transfer_properties *props)
+{
+	RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc_nolock(), ast_format_cap_destroy);
+	struct ast_format fmt;
+	char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2];
+	int cause;
+
+	if (props->recall_target) {
+		ast_log(LOG_NOTICE, "props->recall_target is non-NULL and its refcount is %d\n", ao2_ref(props->recall_target, 0));
+	} else {
+		ast_log(LOG_NOTICE, "props->recall_target is NULL\n");
+	}
+	
+	if (!cap) {
+		return -1;
+	}
+
+	snprintf(destination, sizeof(destination), "%s@%s", props->exten, props->context);
+
+	ast_format_cap_add(cap, ast_format_set(&fmt, AST_FORMAT_SLINEAR, 0));
+
+	/* Get a channel that is the destination we wish to call */
+	props->recall_target = ast_request("Local", cap, NULL, destination, &cause);
+	if (!props->recall_target) {
+		ast_log(LOG_ERROR, "Unable to request outbound channel for recall target\n");
+		return -1;
+	}
+
+	if (attach_framehook(props, props->recall_target)) {
+		ast_log(LOG_ERROR, "Unable to attach framehook to recall target\n");
+		ast_hangup(props->recall_target);
+		props->recall_target = NULL;
+		return -1;
+	}
+
+	if (ast_call(props->recall_target, destination, 0)) {
+		ast_log(LOG_ERROR, "Unable to place outbound call to recall target\n");
+		ast_hangup(props->recall_target);
+		props->recall_target = NULL;
+		return -1;
+	}
+
+	ast_channel_ref(props->recall_target);
+	if (ast_bridge_impart(props->transferee_bridge, props->recall_target, NULL, NULL, 1)) {
+		ast_log(LOG_ERROR, "Unable to place recall target into bridge\n");
+		ast_hangup(props->recall_target);
+		return -1;
+	}
+
+	return 0;
+}
+
+static enum attended_transfer_state retransfer_next(struct attended_transfer_properties *props,
+		enum attended_transfer_stimulus stimulus)
+{
+	switch (stimulus) {
+	case STIMULUS_TRANSFEREE_HANGUP:
+		return TRANSFER_FAIL;
+	case STIMULUS_TIMEOUT:
+		ast_softhangup(props->recall_target, AST_SOFTHANGUP_EXPLICIT);
+	case STIMULUS_RECALL_TARGET_HANGUP:
+		props->recall_target = ast_channel_unref(props->recall_target);
+		if (props->atxferloopdelay) {
+			return TRANSFER_WAIT_TO_RECALL;
+		}
+		return TRANSFER_RECALLING;
+	case STIMULUS_RECALL_TARGET_ANSWER:
+		return TRANSFER_RESUME;
+	case STIMULUS_NONE:
+	case STIMULUS_DTMF_ATXFER_ABORT:
+	case STIMULUS_DTMF_ATXFER_COMPLETE:
+	case STIMULUS_DTMF_ATXFER_THREEWAY:
+	case STIMULUS_DTMF_ATXFER_SWAP:
+	case STIMULUS_TRANSFER_TARGET_HANGUP:
+	case STIMULUS_TRANSFER_TARGET_ANSWER:
+	case STIMULUS_TRANSFERER_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 wait_to_recall_enter(struct attended_transfer_properties *props)
+{
+	bridge_hold(props->transferee_bridge);
+	return 0;
+}
+
+static enum attended_transfer_state wait_to_recall_next(struct attended_transfer_properties *props,
+		enum attended_transfer_stimulus stimulus)
+{
+	bridge_unhold(props->transferee_bridge);
+	switch (stimulus) {
+	case STIMULUS_TRANSFEREE_HANGUP:
+		return TRANSFER_FAIL;
+	case STIMULUS_TIMEOUT:
+		return TRANSFER_RECALLING;
+	case STIMULUS_NONE:
+	case STIMULUS_DTMF_ATXFER_ABORT:
+	case STIMULUS_DTMF_ATXFER_COMPLETE:
+	case STIMULUS_DTMF_ATXFER_THREEWAY:
+	case STIMULUS_DTMF_ATXFER_SWAP:
+	case STIMULUS_TRANSFER_TARGET_HANGUP:
+	case STIMULUS_TRANSFER_TARGET_ANSWER:
+	case STIMULUS_TRANSFERER_HANGUP:
+	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 fail_enter(struct attended_transfer_properties *props)
+{
+	if (props->transferee_bridge) {
+		ast_bridge_destroy(props->transferee_bridge);
+		props->transferee_bridge = NULL;
+	}
+	return 0;
+}
+
 static void stimulate_attended_transfer(struct attended_transfer_properties *props,
 		enum attended_transfer_stimulus stimulus)
 {




More information about the asterisk-commits mailing list