[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