/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "cpr_types.h" #include "cpr_stdlib.h" #include "cpr_stdio.h" #include "cpr_string.h" #include "fim.h" #include "lsm.h" #include "sm.h" #include "ccapi.h" #include "phone_debug.h" #include "text_strings.h" #include "fsm.h" #include "uiapi.h" #include "debug.h" #include "regmgrapi.h" #include "platform_api.h" extern fsmdef_dcb_t *fsmdef_dcbs; #define FSMXFR_NULL_DIALSTRING '\0' static fsmxfr_xcb_t *fsmxfr_xcbs; typedef enum fsmxfr_states_t_ { FSMXFR_S_MIN = -1, FSMXFR_S_IDLE, FSMXFR_S_ACTIVE, FSMXFR_S_MAX } fsmxfr_states_t; static const char *fsmxfr_state_names[] = { "IDLE", "ACTIVE" }; static sm_rcs_t fsmxfr_ev_idle_setup(sm_event_t *event); static sm_rcs_t fsmxfr_ev_idle_feature(sm_event_t *event); static sm_rcs_t fsmxfr_ev_idle_dialstring(sm_event_t *event); static sm_rcs_t fsmxfr_ev_active_connected_ack(sm_event_t *event); static sm_rcs_t fsmxfr_ev_active_release(sm_event_t *event); static sm_rcs_t fsmxfr_ev_active_release_complete(sm_event_t *event); static sm_rcs_t fsmxfr_ev_active_feature(sm_event_t *event); static sm_rcs_t fsmxfr_ev_active_feature_ack(sm_event_t *event); static sm_rcs_t fsmxfr_ev_active_onhook(sm_event_t *event); static sm_rcs_t fsmxfr_ev_active_dialstring(sm_event_t *event); static sm_rcs_t fsmxfr_ev_active_proceeding(sm_event_t *event); static sm_function_t fsmxfr_function_table[FSMXFR_S_MAX][CC_MSG_MAX] = { /* FSMXFR_S_IDLE ------------------------------------------------------------ */ { /* FSMXFR_E_SETUP */ fsmxfr_ev_idle_setup, /* FSMXFR_E_SETUP_ACK */ NULL, /* FSMXFR_E_PROCEEDING */ NULL, /* FSMXFR_E_ALERTING */ NULL, /* FSMXFR_E_CONNECTED */ NULL, /* FSMXFR_E_CONNECTED_ACK */ NULL, /* FSMXFR_E_RELEASE */ NULL, /* FSMXFR_E_RELEASE_COMPLETE */ NULL, /* FSMXFR_E_FEATURE */ fsmxfr_ev_idle_feature, /* FSMXFR_E_FEATURE_ACK */ NULL, /* FSMXFR_E_OFFHOOK */ NULL, /* FSMXFR_E_ONHOOK */ NULL, /* FSMXFR_E_LINE */ NULL, /* FSMXFR_E_DIGIT_BEGIN */ NULL, /* FSMXFR_E_DIGIT */ NULL, /* FSMXFR_E_DIALSTRING */ fsmxfr_ev_idle_dialstring, /* FSMXFR_E_MWI */ NULL, /* FSMXFR_E_SESSION_AUDIT */ NULL }, /* FSMXFR_S_ACTIVE ---------------------------------------------------- */ { /* FSMXFR_E_SETUP */ NULL, /* FSMXFR_E_SETUP_ACK */ NULL, /* FSMXFR_E_PROCEEDING */ fsmxfr_ev_active_proceeding, /* FSMXFR_E_ALERTING */ NULL, /* FSMXFR_E_CONNECTED */ NULL, /* FSMXFR_E_CONNECTED_ACK */ fsmxfr_ev_active_connected_ack, /* FSMXFR_E_RELEASE */ fsmxfr_ev_active_release, /* FSMXFR_E_RELEASE_COMPLETE */ fsmxfr_ev_active_release_complete, /* FSMXFR_E_FEATURE */ fsmxfr_ev_active_feature, /* FSMXFR_E_FEATURE_ACK */ fsmxfr_ev_active_feature_ack, /* FSMXFR_E_OFFHOOK */ NULL, /* FSMXFR_E_ONHOOK */ fsmxfr_ev_active_onhook, /* FSMXFR_E_LINE */ NULL, /* FSMXFR_E_DIGIT_BEGIN */ NULL, /* FSMXFR_E_DIGIT */ NULL, /* FSMXFR_E_DIALSTRING */ fsmxfr_ev_active_dialstring, /* FSMXFR_E_MWI */ NULL, /* FSMXFR_E_SESSION_AUDIT */ NULL } }; static sm_table_t fsmxfr_sm_table; sm_table_t *pfsmxfr_sm_table = &fsmxfr_sm_table; const char * fsmxfr_state_name (int state) { if ((state <= FSMXFR_S_MIN) || (state >= FSMXFR_S_MAX)) { return (get_debug_string(GSM_UNDEFINED)); } return (fsmxfr_state_names[state]); } static int fsmxfr_get_new_xfr_id (void) { static int xfr_id = 0; if (++xfr_id < 0) { xfr_id = 1; } return (xfr_id); } /** * * Sets/rest transfer complete flag in the DCB so it can be used in * UI to indicate if the call is ended because of xfer or endcall. * * @cns_call_id consult call id * @Xfr_call_id transfer call id * * @return none * * @pre (none) */ void fsmxfr_mark_dcb_for_xfr_complete(callid_t cns_call_id, callid_t xfr_call_id, boolean set_flag) { fsm_fcb_t *cns_fcb, *xfr_fcb; cns_fcb = fsm_get_fcb_by_call_id_and_type(cns_call_id, FSM_TYPE_DEF); xfr_fcb = fsm_get_fcb_by_call_id_and_type(xfr_call_id, FSM_TYPE_DEF); if (set_flag) { if (cns_fcb && cns_fcb->dcb) { FSM_SET_FLAGS(cns_fcb->dcb->flags, FSMDEF_F_XFER_COMPLETE); } if (xfr_fcb && xfr_fcb->dcb) { FSM_SET_FLAGS(xfr_fcb->dcb->flags, FSMDEF_F_XFER_COMPLETE); } } else { if (cns_fcb && cns_fcb->dcb) { FSM_RESET_FLAGS(cns_fcb->dcb->flags, FSMDEF_F_XFER_COMPLETE); } if (xfr_fcb && xfr_fcb->dcb) { FSM_RESET_FLAGS(xfr_fcb->dcb->flags, FSMDEF_F_XFER_COMPLETE); } } } /** * * Get active trasnfer state machine information (in active state). * * @param none * * @return fsm_fcb_t if there is a active trasnfer pending * else NULL * * @pre (none) */ fsm_fcb_t *fsmxfr_get_active_xfer(void) { fsm_fcb_t *fcb; fsmxfr_xcb_t *xcb; FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, FSMXFR_MAX_XCBS) { fcb = fsm_get_fcb_by_call_id_and_type(xcb->xfr_call_id, FSM_TYPE_XFR); if (fcb && fcb->state == FSMXFR_S_ACTIVE) { return(fcb); } } return(NULL); } static void fsmxfr_init_xcb (fsmxfr_xcb_t *xcb) { if (xcb != NULL) { xcb->xfr_id = FSM_NO_ID; xcb->xfr_call_id = CC_NO_CALL_ID; xcb->cns_call_id = CC_NO_CALL_ID; xcb->xfr_line = CC_NO_LINE; xcb->cns_line = CC_NO_LINE; xcb->type = FSMXFR_TYPE_NONE; xcb->method = CC_XFER_METHOD_NONE; xcb->cnf_xfr = FALSE; xcb->active = FALSE; xcb->mode = FSMXFR_MODE_TRANSFEROR; xcb->xfer_comp_req = FALSE; xcb->xfr_orig = CC_SRC_MIN; if (xcb->xcb2 != NULL) { fsmxfr_init_xcb(xcb->xcb2); xcb->xcb2 = NULL; } if (xcb->dialstring != NULL) { cpr_free(xcb->dialstring); xcb->dialstring = NULL; } if (xcb->queued_dialstring != NULL) { cpr_free(xcb->queued_dialstring); xcb->queued_dialstring = NULL; } if (xcb->referred_by != NULL) { cpr_free(xcb->referred_by); xcb->referred_by = NULL; } } } static fsmxfr_xcb_t * fsmxfr_get_xcb_by_xfr_id (int xfr_id) { static const char fname[] = "fsmxfr_get_xcb_by_xfr_id"; fsmxfr_xcb_t *xcb; fsmxfr_xcb_t *xcb_found = NULL; FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, FSMXFR_MAX_XCBS) { if (xcb->xfr_id == xfr_id) { xcb_found = xcb; FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_PTR), xcb->xfr_id, xcb->xfr_call_id, xcb->cns_call_id, fname, xcb); break; } } return (xcb_found); } /* * Function: fsmxfr_get_new_xfr_context * * Parameters: * xfr_call_id: call_id for the call initiating the transfer * type: attended or unattended transfer * method: BYE/ALSO or REFER method of transfer * local: local or remote initiated transfer * * Description: This function creates a new transfer context by: * - getting a free xcb * - creating new xfr_id and cns_call_id * * Returns: xcb * */ static fsmxfr_xcb_t * fsmxfr_get_new_xfr_context (callid_t xfr_call_id, line_t line, fsmxfr_types_t type, cc_xfer_methods_t method, fsmxfr_modes_t mode) { static const char fname[] = "fsmxfr_get_new_xfr_context"; fsmxfr_xcb_t *xcb; xcb = fsmxfr_get_xcb_by_xfr_id(FSM_NO_ID); if (xcb != NULL) { xcb->xfr_id = fsmxfr_get_new_xfr_id(); xcb->xfr_call_id = xfr_call_id; xcb->cns_call_id = cc_get_new_call_id(); xcb->xfr_line = line; xcb->cns_line = line; xcb->type = type; xcb->method = method; xcb->mode = mode; FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_PTR), xcb->xfr_id, xcb->xfr_call_id, xcb->cns_call_id, fname, xcb); } return (xcb); } fsmxfr_xcb_t * fsmxfr_get_xcb_by_call_id (callid_t call_id) { fsmxfr_xcb_t *xcb; fsmxfr_xcb_t *xcb_found = NULL; FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, FSMXFR_MAX_XCBS) { if ((xcb->xfr_call_id == call_id) || (xcb->cns_call_id == call_id)) { xcb_found = xcb; break; } } return (xcb_found); } /** * * Cancel tranfer operation by sending cancel event to SIP stack. * This routine is used in roundtable phone. * * @param line, call_id, target_call_id, cause (implicit or explicit) * * @return void * * @pre (none) */ void fsmxfr_feature_cancel (fsmxfr_xcb_t *xcb, line_t line, callid_t call_id, callid_t target_call_id, cc_rcc_skey_evt_type_e cause) { static const char fname[] = "fsmxfr_feature_cancel"; cc_feature_data_t data; fsm_fcb_t *fcb_def; DEF_DEBUG(DEB_F_PREFIX"Sending cancel call_id = %d, t_id=%d, cause = %d\n", DEB_F_PREFIX_ARGS(GSM, fname), call_id, target_call_id, cause); fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF); if ((cause == CC_SK_EVT_TYPE_EXPLI) && (fcb_def != NULL) && ((fcb_def->dcb->selected == FALSE) && ((fcb_def->state == FSMDEF_S_OUTGOING_ALERTING) || ((fcb_def->state == FSMDEF_S_CONNECTED) && (fcb_def->dcb->spoof_ringout_requested == TRUE) && (fcb_def->dcb->spoof_ringout_applied == TRUE))))) { cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, call_id, line, CC_FEATURE_END_CALL, NULL); } fcb_def = fsm_get_fcb_by_call_id_and_type(target_call_id, FSM_TYPE_DEF); if ((cause == CC_SK_EVT_TYPE_EXPLI) && (fcb_def != NULL) && ((fcb_def->dcb->selected == FALSE) && ((fcb_def->state == FSMDEF_S_OUTGOING_ALERTING) || ((fcb_def->state == FSMDEF_S_CONNECTED) && (fcb_def->dcb->spoof_ringout_requested == TRUE) && (fcb_def->dcb->spoof_ringout_applied == TRUE))))) { cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, target_call_id, line, CC_FEATURE_END_CALL, NULL); } data.cancel.target_call_id = target_call_id; data.cancel.call_id = call_id; data.cancel.cause = cause; cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, call_id, line, CC_FEATURE_CANCEL, &data); } void fsmxfr_update_xfr_context (fsmxfr_xcb_t *xcb, callid_t old_call_id, callid_t new_call_id) { static const char fname[] = "fsmxfr_update_xfr_context"; FSM_DEBUG_SM(DEB_F_PREFIX"Entered. \n", DEB_F_PREFIX_ARGS(FSM, "fsmxfr_update_xfr_context")); if (xcb != NULL) { if (old_call_id == xcb->xfr_call_id) { xcb->xfr_call_id = new_call_id; } else if (old_call_id == xcb->cns_call_id) { xcb->cns_call_id = new_call_id; } FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_PTR), xcb->xfr_id, xcb->xfr_call_id, xcb->cns_call_id, fname, xcb); } } /* * Function to get line number of other call associated in * transfer. * * @param xcb and call_id. * * @return void * */ line_t fsmxfr_get_other_line (fsmxfr_xcb_t *xcb, callid_t call_id) { line_t other_line = CC_NO_LINE; if (xcb != NULL) { if (xcb->xfr_call_id == call_id) { other_line = xcb->cns_line; } else if (xcb->cns_call_id == call_id) { other_line = xcb->xfr_line; } } return (other_line); } callid_t fsmxfr_get_other_call_id (fsmxfr_xcb_t *xcb, callid_t call_id) { callid_t other_call_id = CC_NO_CALL_ID; if (xcb != NULL) { if (xcb->xfr_call_id == call_id) { other_call_id = xcb->cns_call_id; } else if (xcb->cns_call_id == call_id) { other_call_id = xcb->xfr_call_id; } } return (other_call_id); } /* * Function: fsmxfr_remove_fcb * * Parameters: * xfr_id: xfr_id for the transfer * call_id: call_id that identifies the fcb to be removed * * Description: This function will remove the fcb identified by the given * call_id from the xcb. And the function will free the xcb * if both fcbs have been removed. * * Returns: none * * Note: This is a helper function for fsmxfr_cleanup. It allows fsmxfr_cleanup * to cleanup one fcb (one call involved in the transfer) independent * of the other involved fcb. */ static void fsmxfr_remove_fcb (fsm_fcb_t *fcb, callid_t call_id) { fsmxfr_xcb_t *xcb = fcb->xcb; FSM_DEBUG_SM(DEB_F_PREFIX"Entered. \n", DEB_F_PREFIX_ARGS(FSM, "fsmxfr_remove_fcb")); if (xcb != NULL) { fsmxfr_update_xfr_context(xcb, call_id, CC_NO_CALL_ID); /* * Free the xcb if both fcb references have been removed. */ if ((xcb->xfr_call_id == CC_NO_CALL_ID) && (xcb->cns_call_id == CC_NO_CALL_ID)) { fsmxfr_init_xcb(xcb); } } } static void fsmxfr_cnf_cleanup (fsmxfr_xcb_t *xcb) { fsmdef_dcb_t *xfr_dcb; fsmdef_dcb_t *cns_dcb; cc_feature_data_t ftr_data; cns_dcb = fsm_get_dcb(xcb->cns_call_id); xfr_dcb = fsm_get_dcb(xcb->xfr_call_id); ftr_data.endcall.cause = CC_CAUSE_NORMAL; ftr_data.endcall.dialstring[0] = '\0'; /* * This is a conference transfer hence we are cleaning * up both the lines */ cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, cns_dcb->call_id, cns_dcb->line, CC_FEATURE_END_CALL, &ftr_data); cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xfr_dcb->call_id, xfr_dcb->line, CC_FEATURE_END_CALL, &ftr_data); } static void fsmxfr_cleanup (fsm_fcb_t *fcb, int fname, boolean both) { fsm_fcb_t *other_fcb = NULL; callid_t call_id = fcb->call_id; callid_t other_call_id = CC_NO_CALL_ID; line_t other_line; FSM_DEBUG_SM(DEB_F_PREFIX"Entered. \n", DEB_F_PREFIX_ARGS(FSM, "fsmxfr_cleanup")); other_call_id = fsmxfr_get_other_call_id(fcb->xcb, call_id); other_line = fsmxfr_get_other_line(fcb->xcb, call_id); if (other_call_id != CC_NO_CALL_ID) { other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id, FSM_TYPE_XFR); } if (fcb->xcb && (fcb->xcb->cnf_xfr != TRUE) && (call_id == fcb->xcb->xfr_call_id)) { if (other_call_id != CC_NO_CALL_ID) { /* * Not clearing consulation call, so change consultation call * attribute to display connected softkey set. Do not change * softkey set if it is a transfer o */ cc_call_attribute(other_call_id, other_line, NORMAL_CALL); } } /* * Check if the user wanted to cleanup the whole xcb. * If so, then we will grab the other fcb first and call this function * again with this other fcb. The whole xcb will be freed after this block * of code because both call_ids will be -1, which tells * fsmxfr_remove_fcb to free the xcb. */ if (both) { FSM_DEBUG_SM(DEB_F_PREFIX"clean both. \n", DEB_F_PREFIX_ARGS(FSM, "fsmxfr_cleanup")); if (other_call_id != CC_NO_CALL_ID) { if (other_fcb != NULL) { fsmxfr_cleanup(other_fcb, fname, FALSE); } else { /* * If there is no other FCB, we need to clean up so that the * XCB will be deleted. */ fsmxfr_update_xfr_context(fcb->xcb, other_call_id, CC_NO_CALL_ID); } } } /* * Remove the reference to this fcb from the xcb */ fsmxfr_remove_fcb(fcb, fcb->call_id); /* * Move this fcb to the IDLE state */ fsm_change_state(fcb, fname, FSMXFR_S_IDLE); /* * Reset the data for this fcb. The fcb is still included in a call * so set the call_id and dcb values accordingly. */ fsm_init_fcb(fcb, fcb->call_id, fcb->dcb, FSM_TYPE_XFR); } void fsmxfr_free_cb (fim_icb_t *icb, callid_t call_id) { fsm_fcb_t *fcb = NULL; if (call_id != CC_NO_CALL_ID) { fcb = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_XFR); if (fcb != NULL) { fsmxfr_cleanup(fcb, __LINE__, FALSE); fsm_init_fcb(fcb, CC_NO_CALL_ID, FSMDEF_NO_DCB, FSM_TYPE_NONE); } } } fsmxfr_types_t fsmxfr_get_xfr_type (callid_t call_id) { fsmxfr_xcb_t *xcb; fsmxfr_types_t type = FSMXFR_TYPE_BLND_XFR; xcb = fsmxfr_get_xcb_by_call_id(call_id); if (xcb != NULL) { type = xcb->type; } return (type); } cc_features_t fsmxfr_type_to_feature (fsmxfr_types_t type) { cc_features_t feature; if (type == FSMXFR_TYPE_XFR) { feature = CC_FEATURE_XFER; } else { feature = CC_FEATURE_BLIND_XFER; } return (feature); } /******************************************************************* * event functions */ static sm_rcs_t fsmxfr_ev_idle_setup (sm_event_t *event) { fsm_fcb_t *fcb = (fsm_fcb_t *) event->data; cc_setup_t *msg = (cc_setup_t *) event->msg; fsmxfr_xcb_t *xcb; /* * If this is the consultation call involved in a transfer, * then set the rest of the data required to make the transfer * happen. The data is the xcb in the fcb. The data is set now * because we did not have the fcb when the transfer was * initiated. The fcb is created when a new_call event is * received by the FIM, not when a transfer event is received. */ /* * Ignore this event if this call is not involved in a transfer. */ xcb = fsmxfr_get_xcb_by_call_id(msg->call_id); if (xcb == NULL) { return (SM_RC_DEF_CONT); } fcb->xcb = xcb; fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE); return (SM_RC_CONT); } static boolean fsmxfr_copy_dialstring (char **saved_dialstring, char *dialstring) { char *tempstring; int len; if (saved_dialstring == NULL) { return (FALSE); } /* * Make sure we have a valid dialstring. */ if ((dialstring == NULL) || (dialstring[0] == '\0')) { return (FALSE); } /* * Copy the dialstring to the xcb. We will need it later when * SIP sends the RELEASE so that we can send out the * NEWCALL to start the call to the target. */ len = (strlen(dialstring) + 1) * sizeof(char); tempstring = (char *) cpr_malloc(len); if (tempstring != NULL) { sstrncpy(tempstring, dialstring, len); *saved_dialstring = tempstring; } return (TRUE); } static void fsmxfr_set_xfer_data (cc_causes_t cause, cc_xfer_methods_t method, callid_t target_call_id, char *dialstring, cc_feature_data_xfer_t *xfer) { xfer->cause = cause; xfer->method = method; xfer->target_call_id = target_call_id; sstrncpy(xfer->dialstring, dialstring, CC_MAX_DIALSTRING_LEN); } static boolean fsmxfr_remote_transfer (fsm_fcb_t *fcb, cc_features_t ftr_id, callid_t call_id, line_t line, char *dialstring, char *referred_by) { fsmxfr_types_t type; int free_lines; cc_feature_data_t data; fsmxfr_xcb_t *xcb; fsmxfr_xcb_t *primary_xcb; callid_t cns_call_id; line_t newcall_line = 0; memset(&data, 0, sizeof(cc_feature_data_t)); /* * Make sure we have a free line for the consultation * call if this is an attended transfer. We do not need this * check for an unattended transfer, because the stack will * send up a release for the call being transferred, which will * then trigger the fsmxfr to send out the newcall in the same * plane of the call being released. */ if (ftr_id == CC_FEATURE_XFER) { /* * Make sure we have a free line to start the * consultation call. */ free_lines = lsm_get_instances_available_cnt(line, TRUE); if (free_lines <= 0) { /* * No free lines - let the user know and end this * request. */ fsm_display_no_free_lines(); return (FALSE); } } newcall_line = lsm_get_newcall_line(line); if (newcall_line == NO_LINES_AVAILABLE) { /* * Error Pass Limit - let the user know and end this request. */ lsm_ui_display_notify_str_index(STR_INDEX_ERROR_PASS_LIMIT); return (FALSE); } /* * Get a new xcb and new xfr id - This is the handle that will * identify the xfr. */ type = ((ftr_id == CC_FEATURE_XFER) ? (FSMXFR_TYPE_XFR) : (FSMXFR_TYPE_BLND_XFR)); primary_xcb = fsmxfr_get_xcb_by_call_id(call_id); xcb = fsmxfr_get_new_xfr_context(call_id, line, type, CC_XFER_METHOD_REFER, FSMXFR_MODE_TRANSFEREE); if (xcb == NULL) { return (FALSE); } if (primary_xcb) { fcb->xcb->xcb2 = xcb; fcb->xcb->active = TRUE; } else { fcb->xcb = xcb; } xcb->cns_line = newcall_line; cns_call_id = xcb->cns_call_id; fsmxfr_set_xfer_data(CC_CAUSE_OK, CC_XFER_METHOD_REFER, cns_call_id, FSMXFR_NULL_DIALSTRING, &(data.xfer)); cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id, line, ftr_id, &data, CC_CAUSE_NORMAL); if (ftr_id == CC_FEATURE_XFER) { /* * This call needs to go on hold so we can start the * consultation call. The call may already be on hold, * so the event will just be silently ignored. We set * the line number to 0xFF so that GSM will know this * came from a transfer and we only want to put the call * on local hold. We don't want to send an Invite Hold * to the SIP stack. */ cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, call_id, 0xFF, CC_FEATURE_HOLD, NULL); /* * Initiate the consultation call. */ /* * Make sure we have a valid dialstring. */ if ((dialstring == NULL) || (dialstring[0] == '\0')) { return (FALSE); } /* * memset is done because if redirects[0].number is * corrupted we might think that it is a blind * transfer */ memset(data.newcall.redirect.redirects[0].number, 0, sizeof(CC_MAX_DIALSTRING_LEN)); data.newcall.cause = CC_CAUSE_XFER_REMOTE; data.newcall.redirect.redirects[0].redirect_reason = CC_REDIRECT_REASON_DEFLECTION; sstrncpy(data.newcall.dialstring, dialstring, CC_MAX_DIALSTRING_LEN); cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, cns_call_id, newcall_line, CC_FEATURE_NEW_CALL, &data); FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_XFR_INITIATED), xcb->xfr_id, call_id, cns_call_id, __LINE__); fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE); } else { /* CC_FEATURE_BLIND_XFER */ /* * Make sure we have a valid dialstring. */ if ((fsmxfr_copy_dialstring(&xcb->dialstring, dialstring) == TRUE) && (fsmxfr_copy_dialstring(&xcb->referred_by, referred_by) == TRUE)) { fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE); } else { fsmxfr_cleanup(fcb, __LINE__, TRUE); } } return (TRUE); } static sm_rcs_t fsmxfr_ev_idle_feature (sm_event_t *event) { const char *fname = "fsmxfr_ev_idle_feature"; fsm_fcb_t *fcb = (fsm_fcb_t *) event->data; cc_feature_t *msg = (cc_feature_t *) event->msg; callid_t call_id = msg->call_id; line_t line = msg->line; cc_srcs_t src_id = msg->src_id; cc_features_t ftr_id = msg->feature_id; cc_feature_data_t *ftr_data = &(msg->data); fsmdef_dcb_t *dcb = fcb->dcb; fsm_fcb_t *other_fcb; sm_rcs_t sm_rc = SM_RC_CONT; fsmxfr_types_t type; int free_lines; cc_feature_data_t data; cc_causes_t cause = msg->data.xfer.cause; cc_xfer_methods_t method; fsmxfr_xcb_t *xcb; fsm_fcb_t *fcb_def; fsm_fcb_t *cns_fcb, *con_fcb, *sel_fcb; boolean int_rc = FALSE; callid_t cns_call_id; line_t newcall_line = 0; memset(&data, 0, sizeof(data)); fsm_sm_ftr(ftr_id, src_id); /* * Consume the XFER events and don't pass them along to the other FSMs. */ if ((ftr_id == CC_FEATURE_BLIND_XFER) || (ftr_id == CC_FEATURE_XFER)) { sm_rc = SM_RC_END; } switch (src_id) { case CC_SRC_UI: switch (ftr_id) { case CC_FEATURE_DIRTRXFR: /* If there is a active xfer pending * then link this transfer to active transfer */ other_fcb = fsmxfr_get_active_xfer(); if (other_fcb) { if (other_fcb->xcb == NULL) { GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname); return (SM_RC_END); } /* End existing consult call and then link another * call with the trasfer */ cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_fcb->xcb->cns_call_id, other_fcb->xcb->cns_line, CC_FEATURE_END_CALL, NULL); other_fcb->xcb->cns_call_id = call_id; cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_fcb->xcb->xfr_call_id, other_fcb->xcb->xfr_line, CC_FEATURE_DIRTRXFR, NULL); return(SM_RC_END); } fsm_get_fcb_by_selected_or_connected_call_fcb(call_id, &con_fcb, &sel_fcb); /* If there is a call selected use that for direct transfer */ if (sel_fcb) { other_fcb = sel_fcb; } else if (con_fcb) { other_fcb = con_fcb; } else { /* No connected call or selected call */ return(SM_RC_CONT); } /* Make sure atleast one call has been selected, connected and this call * is not in the focus */ if ((fsmutil_get_num_selected_calls() > 1) && (dcb->selected == FALSE)) { //return error return(SM_RC_CONT); } if (other_fcb->xcb == NULL || other_fcb->xcb->xfr_line != line) { //Not on same line display a message GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname); return(SM_RC_CONT); } xcb = fsmxfr_get_new_xfr_context(call_id, line, FSMXFR_TYPE_XFR, CC_XFER_METHOD_REFER, FSMXFR_MODE_TRANSFEROR); if (xcb == NULL) { break; } xcb->xfr_orig = src_id; fcb->xcb = xcb; xcb->type = FSMXFR_TYPE_DIR_XFR; xcb->cns_call_id = other_fcb->dcb->call_id; other_fcb->xcb = xcb; /* * Emulating user hitting transfer key for second time * in attended transfer */ cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line, CC_FEATURE_DIRTRXFR, NULL); fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE); return(SM_RC_END); case CC_FEATURE_BLIND_XFER: case CC_FEATURE_XFER: /* Connect the existing call to active trasnfer state * machine. If the UI generates the event with target * call_id in the data then terminate the existing consulatative * call and link that to another call. */ if ((cause != CC_CAUSE_XFER_CNF) && (ftr_data && msg->data_valid) && (ftr_data->xfer.target_call_id != CC_NO_CALL_ID) && ((cns_fcb = fsm_get_fcb_by_call_id_and_type(ftr_data->xfer.target_call_id, FSM_TYPE_XFR)) != NULL)) { /* In this case there is no active xcb but upper layer * wants to complete a trasnfer with 2 different call_ids */ xcb = fsmxfr_get_new_xfr_context(call_id, line, FSMXFR_TYPE_XFR, CC_XFER_METHOD_REFER, FSMXFR_MODE_TRANSFEROR); if (xcb == NULL) { return(SM_RC_END); } fcb->xcb = xcb; cns_fcb->xcb = xcb; xcb->type = FSMXFR_TYPE_DIR_XFR; xcb->cns_call_id = ftr_data->xfer.target_call_id; fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF); /* Find line information for target call. */ if (fcb_def && fcb_def->dcb) { xcb->cns_line = fcb_def->dcb->line; } else { return(SM_RC_END); } fsm_change_state(cns_fcb, __LINE__, FSMXFR_S_ACTIVE); fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE); cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xcb->xfr_call_id, line, CC_FEATURE_DIRTRXFR, NULL); return(SM_RC_END); } /* * This call is the transferor and we are initiating a local * transfer. So: * 1. Make sure we have a free line to open a new call plane to * collect digits (and place call) for the transfer target, * 2. Create a new transfer context, * 3. Place this call on hold, * 4. Send a newcall feature back to the GSM so that the * consultation call can be initiated. */ /* * Check for any other active features which may block * the transfer. */ /* * The call must be in the connected state to initiate a transfer. */ fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF); if ((fcb_def != NULL) && (fcb_def->state != FSMDEF_S_CONNECTED) && (cause != CC_CAUSE_XFER_CNF)) { break; } /* * If it's not a conference transfer make sure we have a free * line to start the consultation call. */ if (cause != CC_CAUSE_XFER_CNF) { //CSCsz38962 don't use expline for local-initiated transfer //free_lines = lsm_get_instances_available_cnt(line, TRUE); free_lines = lsm_get_instances_available_cnt(line, FALSE); if (free_lines <= 0) { /* * No free lines - let the user know and end this request. */ fsm_display_no_free_lines(); break; } newcall_line = lsm_get_newcall_line(line); if (newcall_line == NO_LINES_AVAILABLE) { /* * Error Pass Limit - let the user know and end this request. */ lsm_ui_display_notify_str_index(STR_INDEX_ERROR_PASS_LIMIT); return (FALSE); } } /* * Get a new xcb and new xfr id - This is the handle that will * identify the xfr. */ type = ((ftr_id == CC_FEATURE_XFER) ? (FSMXFR_TYPE_XFR) : (FSMXFR_TYPE_BLND_XFR)); xcb = fsmxfr_get_new_xfr_context(call_id, line, type, CC_XFER_METHOD_REFER, FSMXFR_MODE_TRANSFEROR); if (xcb == NULL) { break; } xcb->xfr_orig = src_id; fcb->xcb = xcb; /* * If not conference transfer initiate the consultation call. * For conference transfer we already have both calls setup. */ if (cause != CC_CAUSE_XFER_CNF) { /* * Set the consultative line to new line id. */ xcb->cns_line = newcall_line; /* * Record the active feature if it is a blind xfer. */ if (ftr_id == CC_FEATURE_BLIND_XFER) { dcb->active_feature = ftr_id; } /* * This call needs to go on hold so we can start the * consultation call. Indicate feature indication should * be send and set the feature reason. */ data.hold.call_info.type = CC_FEAT_HOLD; data.hold.call_info.data.hold_resume_reason = CC_REASON_XFER; data.hold.msg_body.num_parts = 0; data.hold.call_info.data.call_info_feat_data.protect = TRUE; cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line, CC_FEATURE_HOLD, &data); cns_call_id = xcb->cns_call_id; if (ftr_data->xfer.cause == CC_CAUSE_XFER_LOCAL_WITH_DIALSTRING) { cc_int_dialstring(CC_SRC_GSM, CC_SRC_GSM, cns_call_id, newcall_line, ftr_data->xfer.dialstring, NULL, 0); } else { data.newcall.cause = CC_CAUSE_XFER_LOCAL; if (ftr_data->xfer.dialstring[0] != 0) { data.newcall.cause = CC_CAUSE_XFER_BY_REMOTE; sstrncpy(data.newcall.dialstring, ftr_data->xfer.dialstring, CC_MAX_DIALSTRING_LEN); } if (ftr_data->xfer.global_call_id[0] != 0) { sstrncpy(data.newcall.global_call_id, ftr_data->xfer.global_call_id, CC_GCID_LEN); } data.newcall.prim_call_id = xcb->xfr_call_id; data.newcall.hold_resume_reason = CC_REASON_XFER; cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, cns_call_id, newcall_line, CC_FEATURE_NEW_CALL, &data); } FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_XFR_INITIATED), xcb->xfr_id, call_id, cns_call_id, __LINE__); } else { other_fcb = fsm_get_fcb_by_call_id_and_type(msg->data.xfer.target_call_id, FSM_TYPE_XFR); if (other_fcb == NULL) { GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname); break; } other_fcb->xcb = xcb; /* * The xfr_call_id is the call that is being replaced by * the cns_call_id. */ fsmxfr_update_xfr_context(xcb, xcb->cns_call_id, msg->data.xfer.target_call_id); xcb->cnf_xfr = TRUE; fsm_change_state(other_fcb, __LINE__, FSMXFR_S_ACTIVE); /* * Emulating user hitting transfer key for second time * in attended transfer */ cc_int_feature(CC_SRC_UI, CC_SRC_GSM, dcb->call_id, dcb->line, CC_FEATURE_XFER, NULL); } fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE); break; default: fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); sm_rc = SM_RC_DEF_CONT; break; } /* switch (ftr_id) */ break; case CC_SRC_GSM: switch (ftr_id) { case CC_FEATURE_NEW_CALL: /* * If this is the consultation call involved in a transfer, * then set the rest of the data required to make the transfer * happen. The data is the xcb in the fcb. The data is set now * because we did not have the fcb when the transfer was * initiated. The fcb is created when a new_call event is * received by the FIM, not when a transfer event is received. * * Or this could be the call that originated the * transfer (the transferor) and * the person he was talking to (the transfer target) has * decided to transfer the transferor to another target. */ /* * Ignore this event if this call is not involved in a transfer. */ xcb = fsmxfr_get_xcb_by_call_id(call_id); if (xcb == NULL) { break; } fcb->xcb = xcb; fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE); break; default: fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); sm_rc = SM_RC_DEF_CONT; break; } /* switch (ftr_id) */ break; case CC_SRC_SIP: switch (ftr_id) { case CC_FEATURE_BLIND_XFER: case CC_FEATURE_XFER: if (msg->data_valid == FALSE) { break; } method = msg->data.xfer.method; switch (method) { case CC_XFER_METHOD_BYE: /* * This is a remote initiated transfer using the * BYE/ALSO method. * * The transferor has sent us, the transferee, a BYE with * the transfer target. * * 1. Create a new transfer context * 2. Ack the feature request. * * We need to wait for the RELEASE from SIP and then we will * send out the NEWCALL to initiate the call to the target. */ /* * Transfer is valid only for a remote originated transfer. */ if (msg->data.xfer.cause != CC_CAUSE_XFER_REMOTE) { break; } /* * Check for any other active features which may block * the transfer. */ /* * Get a new xcb and new xfr id - This is the handle that will * identify the xfr. */ type = ((ftr_id == CC_FEATURE_XFER) ? (FSMXFR_TYPE_XFR) : (FSMXFR_TYPE_BLND_XFR)); xcb = fsmxfr_get_new_xfr_context(call_id, line, type, method, FSMXFR_MODE_TRANSFEREE); if (xcb == NULL) { break; } xcb->xfr_orig = src_id; fcb->xcb = xcb; fsmxfr_set_xfer_data(CC_CAUSE_OK, method, xcb->cns_call_id, FSMXFR_NULL_DIALSTRING, &(data.xfer)); cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line, ftr_id, &data, CC_CAUSE_NORMAL); /* * Make sure we have a valid dialstring. */ if (fsmxfr_copy_dialstring(&xcb->dialstring, msg->data.xfer.dialstring) == TRUE) { fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE); } else { fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); } break; case CC_XFER_METHOD_REFER: /* * This event is because: * 1. we are the target involved in a transfer or * 2. it is a remote initiated transfer using the REFER method. * * Case 1: * The transferee has attempted to setup a call with us, * the target. The call has been setup successfully so the * SIP stack is informing us that we are the target. * * 1. Ack the request, * 2. Create a new transfer context. * * Case 2: * The transferor has sent us, the transferee, a REFER with * the transfer target. We will attempt to contact the target * and then report the result to the transferor. * * 1. Verify that we have a free line to start the * consultation call, * 2. Create a new transfer context, * 3. Place this call on hold, * 4. Send a newcall feature back to the GSM so that the * consultation call can be initiated. */ /* * Check if this is case 1. * We know this because the target_call_id can only be set * for this case. */ if ((msg->data.xfer.cause == CC_CAUSE_XFER_REMOTE) && (msg->data.xfer.target_call_id != CC_NO_CALL_ID)) { type = ((ftr_id == CC_FEATURE_XFER) ? (FSMXFR_TYPE_XFR) : (FSMXFR_TYPE_BLND_XFR)); xcb = fsmxfr_get_new_xfr_context(call_id, line, type, CC_XFER_METHOD_REFER, FSMXFR_MODE_TARGET); if (xcb == NULL) { break; } xcb->xfr_orig = src_id; fcb->xcb = xcb; /* * The xfr_call_id is the call that is being replaced by * the cns_call_id (which the stack supplied). */ fsmxfr_update_xfr_context(xcb, xcb->cns_call_id, msg->data.xfer.target_call_id); cns_call_id = xcb->cns_call_id; /* * Set the correct xfer_data. The target_call_id must be * CC_NO_CALL_ID so that the stack can tell that this is * a case 1 transfer. */ fsmxfr_set_xfer_data(CC_CAUSE_XFER_REMOTE, method, CC_NO_CALL_ID, FSMXFR_NULL_DIALSTRING, &(data.xfer)); cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line, ftr_id, &data, CC_CAUSE_NORMAL); fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE); break; } /* * I guess we are at case 2... * Transfer should be done only if call's present state * is either on hold or connected */ fcb_def = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF); if ((fcb_def->state == FSMDEF_S_CONNECTED) || (fcb_def->state == FSMDEF_S_CONNECTED_MEDIA_PEND) || (fcb_def->state == FSMDEF_S_RESUME_PENDING) || (fcb_def->state == FSMDEF_S_HOLD_PENDING) || (fcb_def->state == FSMDEF_S_HOLDING)) { int_rc = fsmxfr_remote_transfer(fcb, ftr_id, call_id, dcb->line, msg->data.xfer.dialstring, msg->data.xfer.referred_by); } if (int_rc == FALSE) { fsmxfr_set_xfer_data(CC_CAUSE_ERROR, CC_XFER_METHOD_REFER, CC_NO_CALL_ID, FSMXFR_NULL_DIALSTRING, &(data.xfer)); cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id, line, ftr_id, &data, CC_CAUSE_ERROR); } break; default: break; } /* switch (xfr_data->method) */ break; case CC_FEATURE_NOTIFY: if (ftr_data->notify.subscription != CC_SUBSCRIPTIONS_XFER) { /* This notify is not for XFER subscription */ break; } data.notify.cause = msg->data.notify.cause; data.notify.cause_code = msg->data.notify.cause_code; data.notify.subscription = CC_SUBSCRIPTIONS_XFER; data.notify.method = CC_XFER_METHOD_REFER; data.notify.blind_xferror_gsm_id = msg->data.notify.blind_xferror_gsm_id; data.notify.final = TRUE; if (data.notify.blind_xferror_gsm_id == CC_NO_CALL_ID) { if (msg->data.notify.cause == CC_CAUSE_OK) { data.endcall.cause = CC_CAUSE_OK; sm_rc = SM_RC_END; /* * If dcb is NULL, we received a final NOTIFY after the * the dcb was cleared. This can happen when we receive * BYE before final NOTIFY on the result of the REFER * sent for xfer. If dcb is NULL, just quietly ignore * the NOTIFY event. Otherwise, kick off the release of * the call. */ if (dcb) { cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line, CC_FEATURE_END_CALL, &data); } } } else { cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, data.notify.blind_xferror_gsm_id, line, CC_FEATURE_NOTIFY, &data); } break; default: fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); sm_rc = SM_RC_DEF_CONT; break; } /* switch (ftr_id) */ break; default: fsm_sm_ignore_src(fcb, __LINE__, src_id); sm_rc = SM_RC_DEF_CONT; break; } /* switch (src_id) */ return (sm_rc); } static sm_rcs_t fsmxfr_ev_idle_dialstring (sm_event_t *event) { fsm_fcb_t *fcb = (fsm_fcb_t *) event->data; cc_feature_t *msg = (cc_feature_t *) event->msg; callid_t call_id = msg->call_id; fsmxfr_xcb_t *xcb; sm_rcs_t sm_rc = SM_RC_CONT; /* * Ignore this event if this call is not involved in a transfer. */ xcb = fsmxfr_get_xcb_by_call_id(call_id); if (xcb == NULL) { return sm_rc; } fcb->xcb = xcb; fsm_change_state(fcb, __LINE__, FSMXFR_S_ACTIVE); return (fsmxfr_ev_active_dialstring(event)); } static sm_rcs_t fsmxfr_ev_active_proceeding (sm_event_t *event) { cc_proceeding_t *msg = (cc_proceeding_t *) event->msg; callid_t call_id = msg->call_id; line_t line = msg->line; cc_feature_data_t data; fsmxfr_xcb_t *xcb; fsm_fcb_t *other_fcb; callid_t other_call_id; line_t other_line; /* * Ignore this event if this call is not involved in a transfer * or we are not the target of the transfer. */ xcb = fsmxfr_get_xcb_by_call_id(call_id); if ((xcb == NULL) || (xcb->mode != FSMXFR_MODE_TARGET)) { return (SM_RC_CONT); } other_call_id = fsmxfr_get_other_call_id(xcb, call_id); other_line = fsmxfr_get_other_line(xcb, call_id); other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id, FSM_TYPE_DEF); /* * Release the transfer call. * * We need to release the transfer call (which is really * the consultation call from the transferor to us * the target), because we want the next call coming in * to replace this one. */ data.endcall.cause = CC_CAUSE_REPLACE; cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_call_id, other_line, CC_FEATURE_END_CALL, &data); /* * Only answer the call if the call being replaced was connected * or is currently connected, otherwise just let this call be setup * normally so that it will ring. */ if (other_fcb && (other_fcb->old_state == FSMDEF_S_CONNECTED || other_fcb->old_state == FSMDEF_S_CONNECTED_MEDIA_PEND || other_fcb->old_state == FSMDEF_S_RESUME_PENDING || other_fcb->state == FSMDEF_S_CONNECTED || other_fcb->state == FSMDEF_S_CONNECTED_MEDIA_PEND || other_fcb->state == FSMDEF_S_RESUME_PENDING)) { cc_int_feature(CC_SRC_UI, CC_SRC_GSM, call_id, line, CC_FEATURE_ANSWER, NULL); } return (SM_RC_CONT); } static sm_rcs_t fsmxfr_ev_active_connected_ack (sm_event_t *event) { fsm_fcb_t *fcb = (fsm_fcb_t *) event->data; cc_connected_ack_t *msg = (cc_connected_ack_t *) event->msg; fsmxfr_xcb_t *xcb; /* * If we are the target and this is the call from the transferree * to the target, then we need to cleanup the rest of the transfer. * We need to do this because the consultation call is already cleared * from the transfer but the transfer call was not. */ /* * Ignore this event if this call is not involved in a transfer. */ xcb = fsmxfr_get_xcb_by_call_id(msg->call_id); if ((xcb == NULL) || (xcb->mode != FSMXFR_MODE_TARGET)) { return (SM_RC_CONT); } /* * Remove this call from the transfer. */ fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, FALSE); return (SM_RC_CONT); } static sm_rcs_t fsmxfr_ev_active_release (sm_event_t *event) { static const char fname[] = "fsmxfr_ev_active_release"; fsm_fcb_t *fcb = (fsm_fcb_t *) event->data; cc_release_t *msg = (cc_release_t *) event->msg; callid_t call_id = msg->call_id; fsmdef_dcb_t *dcb = fcb->dcb; callid_t new_call_id; callid_t other_call_id; line_t other_line; fsmxfr_xcb_t *xcb = fcb->xcb; cc_feature_data_t data; boolean secondary = FALSE; cc_action_data_t action_data; fsm_fcb_t *other_fcb; FSM_DEBUG_SM(DEB_F_PREFIX"Entered. \n", DEB_F_PREFIX_ARGS(FSM, "fsmxfr_ev_active_release")); /* * Complete a transfer if we have a pending transfer. */ memset(&data, 0, sizeof(cc_feature_data_t)); /* * Check if this is a transfer of a transfer. */ if (xcb == NULL) { GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find a transfer call to cancel.\n", fname); return (SM_RC_CONT); } if ((xcb->active == TRUE) && (xcb->xcb2 != NULL)) { xcb = xcb->xcb2; secondary = TRUE; } if ((xcb->dialstring != NULL) && (xcb->dialstring[0] != '\0')) { /* * Grab the call_id for the call to the target. * This will either already be in the xcb or we will need to * get a new one. The call_id will be in the xcb if we are the * transferee. */ if (xcb->active == TRUE) { new_call_id = cc_get_new_call_id(); fsmxfr_update_xfr_context(xcb, call_id, new_call_id); } else { new_call_id = fsmxfr_get_other_call_id(xcb, call_id); if (secondary == TRUE) { fsmxfr_update_xfr_context(fcb->xcb, call_id, new_call_id); } } data.newcall.cause = CC_CAUSE_XFER_REMOTE; sstrncpy(data.newcall.dialstring, xcb->dialstring, CC_MAX_DIALSTRING_LEN); cpr_free(xcb->dialstring); xcb->dialstring = NULL; memset(data.newcall.redirect.redirects[0].number, 0, sizeof(CC_MAX_DIALSTRING_LEN)); if (xcb->referred_by != NULL) { sstrncpy(data.newcall.redirect.redirects[0].number, xcb->referred_by, CC_MAX_DIALSTRING_LEN); cpr_free(xcb->referred_by); xcb->referred_by = NULL; } cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, new_call_id, dcb->line, CC_FEATURE_NEW_CALL, &data); FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_XFR_INITIATED), xcb->xfr_id, xcb->xfr_call_id, xcb->cns_call_id, __LINE__); if (secondary == TRUE) { fsmxfr_init_xcb(xcb); fcb->xcb->active = FALSE; fcb->xcb->xcb2 = NULL; fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, FALSE); } else if (xcb->active == TRUE) { fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, FALSE); fcb->xcb->active = FALSE; } else { /* * Reset the xcb call_id that was used for the call to the target. * The value was just temporary and it needs to be reset so that * the cleanup function works properly. */ fsmxfr_update_xfr_context(xcb, new_call_id, CC_NO_CALL_ID); fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); } xcb->active = FALSE; } else { if (secondary == TRUE) { /* * This is the secondary transfer of a primary transfer. * We need to: * 1. update the primary xcb to point to this just transferred * call, * 2. mark the primary xcb as inactive since we just completed the * secondary transfer, * 3. point the newly transferred call to the primary xcb and * update the UI to show that this is a local transfer (the * UI was set as though the call was a remote transfer). * 4. blow away this secondary xcb and cleanup the fcb, */ new_call_id = fsmxfr_get_other_call_id(xcb, call_id); fsmxfr_update_xfr_context(fcb->xcb, call_id, new_call_id); fcb->xcb->active = FALSE; other_fcb = fsm_get_fcb_by_call_id_and_type(new_call_id, FSM_TYPE_XFR); if (other_fcb == NULL) { return (SM_RC_CONT); } other_fcb->xcb = fcb->xcb; fsmxfr_init_xcb(xcb); action_data.update_ui.action = CC_UPDATE_XFER_PRIMARY; (void)cc_call_action(other_fcb->dcb->call_id, dcb->line, CC_ACTION_UPDATE_UI, &action_data); fsmxfr_cleanup(fcb, __LINE__, FALSE); } else { /* * One of the parties in the transfer has decided to release * the call, so go ahead and cleanup this transfer. */ other_call_id = fsmxfr_get_other_call_id(xcb, call_id); other_line = fsmxfr_get_other_line(xcb, call_id); other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id, FSM_TYPE_XFR); if (xcb->cnf_xfr) { /* * This is the transfer for bridging a transfer call so * clear the second line also. */ xcb->cnf_xfr = FALSE; if (other_fcb == NULL) { return (SM_RC_CONT); } cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_call_id, other_line, CC_FEATURE_END_CALL, NULL); } fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); } } return (SM_RC_CONT); } static sm_rcs_t fsmxfr_ev_active_release_complete (sm_event_t *event) { fsmxfr_cleanup((fsm_fcb_t *) event->data, __LINE__, TRUE); return (SM_RC_CONT); } static char *fsmxfr_get_dialed_num (fsmdef_dcb_t *dcb) { static const char fname[] = "fsmxfr_get_dialed_num"; char *tmp_called_number; /* Get the dialed number only if the call is outgoing type */ tmp_called_number = lsm_get_gdialed_digits(); DEF_DEBUG(DEB_F_PREFIX"called_dialed_num = %s\n", DEB_F_PREFIX_ARGS(GSM, fname), tmp_called_number); /* Get dialed number to put in the refer-to header. If there * is no dialed number then use RPID or from header value */ if (tmp_called_number == NULL || (*tmp_called_number) == NUL) { if (dcb->caller_id.called_number[0] != NUL) { DEF_DEBUG(DEB_F_PREFIX"called_dcb_num = %s\n", DEB_F_PREFIX_ARGS(GSM, fname), (char *)dcb->caller_id.called_number); return((char *)dcb->caller_id.called_number); } else { DEF_DEBUG(DEB_F_PREFIX"calling_dcb_num = %s\n", DEB_F_PREFIX_ARGS(GSM, fname), (char *)dcb->caller_id.calling_number); return((char *)dcb->caller_id.calling_number); } } /* * if tmp_called_number is same as what we receive in RPID, * then get the called name from RPID if provided. */ if (dcb->caller_id.called_number != NULL && dcb->caller_id.called_number[0] != NUL) { /* if Cisco PLAR string is used, use the RPID value */ if (strncmp(tmp_called_number, CC_CISCO_PLAR_STRING, sizeof(CC_CISCO_PLAR_STRING)) == 0) { tmp_called_number = (char *)dcb->caller_id.called_number; } } return(tmp_called_number); } static void fsmxfr_initiate_xfr (sm_event_t *event) { static const char fname[] = "fsmxfr_initiate_xfr"; fsm_fcb_t *fcb = (fsm_fcb_t *) event->data; fsm_fcb_t *cns_fcb = NULL; fsmdef_dcb_t *dcb = fcb->dcb; fsmdef_dcb_t *xfr_dcb; fsmdef_dcb_t *cns_dcb; cc_feature_data_t data; fsmxfr_xcb_t *xcb = fcb->xcb; char *called_num = NULL; /* * Place the consultation call on hold. */ if (xcb == NULL) { GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname); return; } cns_dcb = fsm_get_dcb(xcb->cns_call_id); cns_fcb = fsm_get_fcb_by_call_id_and_type(xcb->cns_call_id, FSM_TYPE_DEF); xfr_dcb = fsm_get_dcb(xcb->xfr_call_id); /* * If the consultation call is not connected * treat it like a blind xfer. */ if (cns_fcb != NULL) { /* * If the transfer key is pressed twice, before the sofkey gets * updated in response to first transfer key, the 2nd transfer key * press is treated as a transfer complete and we try to initate * the transfer to the connected call itself. To prevent this, check * the state of the call to see if we should * ignore the 2nd transfer key press. */ if ((cns_fcb->state == FSMDEF_S_COLLECT_INFO) || (cns_fcb->state == FSMDEF_S_OUTGOING_PROCEEDING) || (cns_fcb->state == FSMDEF_S_KPML_COLLECT_INFO)) { FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Ignore the xfer xid %d cid %d %d\n", DEB_L_C_F_PREFIX_ARGS(FSM, xcb->xfr_line, xcb->xfr_call_id, "fsmxfr_initiate_xfr"), xcb->xfr_id, xcb->xfr_call_id, xcb->cns_call_id); return; } /* * Indicate that xfer completion has been requested */ xcb->xfer_comp_req = TRUE; if (cns_fcb->state < FSMDEF_S_CONNECTED) { data.endcall.cause = CC_CAUSE_NO_USER_RESP; cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, cns_dcb->call_id, cns_dcb->line, CC_FEATURE_END_CALL, &data); /* * Instruct the stack to transfer the call. */ called_num = fsmxfr_get_dialed_num(cns_dcb); if (called_num && called_num[0] != '\0') { fsmxfr_set_xfer_data(CC_CAUSE_XFER_LOCAL, xcb->method, cns_dcb->call_id, called_num, &(data.xfer)); cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, xfr_dcb->call_id, xfr_dcb->line, CC_FEATURE_XFER, &data); } else { /* * Can't transfer the call without a dialstring, so * just cleanup the transfer. */ fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); if (xcb->cnf_xfr) { /* * If it is a conference transfer clear up the * calls. */ fsmxfr_cnf_cleanup(xcb); } } } else { /* * If the consulation call is already on hold and * the intial call isn't then place ourselves on * hold (user hit the rocker arm switch). Otherwise, * place the consulation call on hold. Make sure we * do not send feature indication by setting the * call info type to none. */ data.hold.call_info.type = CC_FEAT_NONE; data.hold.msg_body.num_parts = 0; if (((cns_fcb->state == FSMDEF_S_HOLDING) || (cns_fcb->state == FSMDEF_S_HOLD_PENDING)) && ((fcb->state != FSMDEF_S_HOLDING) && (fcb->state != FSMDEF_S_HOLD_PENDING))) { cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line, CC_FEATURE_HOLD, &data); } else { /* just place on hold */ cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, cns_dcb->call_id, cns_dcb->line, CC_FEATURE_HOLD, &data); } } } } static sm_rcs_t fsmxfr_ev_active_feature (sm_event_t *event) { static const char fname[] = "fsmxfr_ev_active_feature"; fsm_fcb_t *fcb = (fsm_fcb_t *) event->data; cc_feature_t *msg = (cc_feature_t *) event->msg; callid_t call_id = msg->call_id; line_t line = msg->line; fsmdef_dcb_t *dcb = fcb->dcb; cc_srcs_t src_id = msg->src_id; cc_features_t ftr_id = msg->feature_id; cc_feature_data_t *feat_data = &(msg->data); fsmdef_dcb_t *xfr_dcb, *cns_dcb; cc_feature_data_t data; sm_rcs_t sm_rc = SM_RC_CONT; fsmxfr_xcb_t *xcb = fcb->xcb; boolean int_rc; char tmp_str[STATUS_LINE_MAX_LEN]; char *called_num = NULL; fsm_fcb_t *cns_fcb = NULL; fsm_sm_ftr(ftr_id, src_id); if (xcb == NULL) { GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname); return (SM_RC_CONT); } /* * Consume the XFER and NOTIFY events and don't pass them along * to the other FSMs. */ if ((ftr_id == CC_FEATURE_BLIND_XFER) || (ftr_id == CC_FEATURE_XFER) || (ftr_id == CC_FEATURE_NOTIFY)) { if (ftr_id == CC_FEATURE_NOTIFY) { if (msg->data_valid && (msg->data.notify.subscription != CC_SUBSCRIPTIONS_XFER)) { /* The subscription is not XFER, let the event flow through */ return (SM_RC_CONT); } } sm_rc = SM_RC_END; } switch (src_id) { case CC_SRC_UI: switch (ftr_id) { case CC_FEATURE_CANCEL: sm_rc = SM_RC_END; fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_EXPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); break; case CC_FEATURE_XFER: /* Connect the existing call to active trasnfer state * machine. If the UI generates the event with target * call_id in the data then terminate the existing consulatative * call and link that to another call. */ DEF_DEBUG(DEB_F_PREFIX"ACTIVE XFER call_id = %d, cns_id = %d, t_id=%d\n", DEB_F_PREFIX_ARGS(GSM, fname), xcb->xfr_call_id, feat_data->xfer.target_call_id, xcb->cns_call_id); if (feat_data && msg->data_valid && (xcb->cns_call_id != feat_data->xfer.target_call_id)) { cns_fcb = fsm_get_fcb_by_call_id_and_type(xcb->cns_call_id, FSM_TYPE_DEF); if (cns_fcb != NULL) { DEF_DEBUG(DEB_F_PREFIX"INVOKE ACTIVE XFER call_id = %d, t_id=%d\n", DEB_F_PREFIX_ARGS(GSM, fname), xcb->xfr_call_id, feat_data->xfer.target_call_id); cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xcb->xfr_call_id, xcb->xfr_line, CC_FEATURE_DIRTRXFR, NULL); } return(SM_RC_END); } switch (xcb->method) { case CC_XFER_METHOD_BYE: case CC_XFER_METHOD_REFER: /* * This is the second transfer event for a local * attended transfer with consultation. * * The user is attempting to complete the transfer, so * 1. place the consultation call on hold, * 2. instruct the stack to transfer the call. */ fsmxfr_initiate_xfr(event); lsm_set_hold_ringback_status(xcb->cns_call_id, FALSE); break; default: fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); break; } /* switch (ftr_id) { */ break; case CC_FEATURE_RESUME: break; case CC_FEATURE_END_CALL: if (xcb->mode == FSMXFR_MODE_TRANSFEREE) { xfr_dcb = fsm_get_dcb(xcb->xfr_call_id); if (call_id == xcb->cns_call_id) { /* * Transferee ended call before transfer was completed. * Notify the transfer call of the status of the * transfer. */ data.notify.cause = CC_CAUSE_ERROR; data.notify.subscription = CC_SUBSCRIPTIONS_XFER; data.notify.method = CC_XFER_METHOD_REFER; data.notify.final = TRUE; cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, xfr_dcb->call_id, xfr_dcb->line, CC_FEATURE_NOTIFY, &data); } } lsm_set_hold_ringback_status(xcb->cns_call_id, TRUE); fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); break; case CC_FEATURE_HOLD: if ((msg->data_valid) && (feat_data->hold.call_info.data.hold_resume_reason == CC_REASON_SWAP || feat_data->hold.call_info.data.hold_resume_reason == CC_REASON_XFER || feat_data->hold.call_info.data.hold_resume_reason == CC_REASON_INTERNAL)) { feat_data->hold.call_info.data.call_info_feat_data.protect = TRUE; } else { DEF_DEBUG(DEB_F_PREFIX"Invoke hold call_id = %d t_call_id=%d\n", DEB_F_PREFIX_ARGS(GSM, fname), xcb->xfr_call_id, xcb->cns_call_id); //Actual hold to this call, so break the feature layer. ui_terminate_feature(xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id); fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); } fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); break; default: fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); break; } /* switch (ftr_id) */ break; case CC_SRC_GSM: switch (ftr_id) { case CC_FEATURE_DIRTRXFR: xfr_dcb = fsm_get_dcb(xcb->xfr_call_id); called_num = fsmxfr_get_dialed_num(xfr_dcb); if (called_num && called_num[0] != '\0') { fsmxfr_set_xfer_data(CC_CAUSE_XFER_LOCAL, xcb->method, xcb->cns_call_id, called_num, &(data.xfer)); data.xfer.method = CC_XFER_METHOD_DIRXFR; cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, call_id, line, CC_FEATURE_XFER, &data); } else { /* * Can't transfer the call without a dialstring, so * just cleanup the transfer. */ fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); } return(SM_RC_END); case CC_FEATURE_END_CALL: /* * Only cleanup the whole xfer if we know that we don't have * an outstanding blind transfer and if we are not the target. * We need the xcb to hang around because other users (lsm,...) * may still want to do something special and the xcb is the only * way they know that the call is involved in a transfer. */ if (xcb->type == FSMXFR_TYPE_BLND_XFR) { fsmxfr_cleanup(fcb, __LINE__, FALSE); } else if ((xcb->type == FSMXFR_TYPE_XFR) && (msg->data.endcall.cause == CC_CAUSE_NO_USER_RESP)) { DEF_DEBUG(DEB_F_PREFIX"Xfer type =%d\n", DEB_F_PREFIX_ARGS(GSM, fname), xcb->type); if ((platGetPhraseText(STR_INDEX_TRANSFERRING, (char *) tmp_str, STATUS_LINE_MAX_LEN - 1)) == CPR_SUCCESS) { lsm_ui_display_status(tmp_str, xcb->xfr_line, xcb->xfr_call_id); } if (xcb->xfer_comp_req == FALSE) { fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); } else { // Mark it as transfer complete. fsmxfr_mark_dcb_for_xfr_complete(xcb->cns_call_id, xcb->xfr_call_id, TRUE); } fsmxfr_cleanup(fcb, __LINE__, FALSE); } else if (xcb->mode == FSMXFR_MODE_TARGET) { break; } else { /* Early attended transfer generates internal END_CALL event * do not send cancel in that case */ if (xcb->xfer_comp_req == FALSE) { fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); } fsmxfr_cleanup(fcb, __LINE__, TRUE); } break; case CC_FEATURE_HOLD: ui_set_local_hold(dcb->line, dcb->call_id); if(msg->data_valid) { feat_data->hold.call_info.data.call_info_feat_data.protect = TRUE; } fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); break; default: fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); break; } /* switch (ftr_id) */ break; case CC_SRC_SIP: switch (ftr_id) { case CC_FEATURE_CALL_PRESERVATION: DEF_DEBUG(DEB_F_PREFIX"Preservation call_id = %d t_call_id=%d\n", DEB_F_PREFIX_ARGS(GSM, fname), xcb->xfr_call_id, xcb->cns_call_id); ui_terminate_feature(xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id); fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); break; case CC_FEATURE_BLIND_XFER: case CC_FEATURE_XFER: if (msg->data_valid == FALSE) { break; } /* * Transfer is valid only for a remote originated transfer. */ if (msg->data.xfer.cause != CC_CAUSE_XFER_REMOTE) { break; } /* * This call is already involved in a transfer as the transferor, * but one of the parties, the transferee or target has decided to * transfer that leg also. So, now we have a transfer of a transfer. */ switch (msg->data.xfer.method) { case CC_XFER_METHOD_BYE: /* * Check for any other active features which may block * the transfer. */ fsmxfr_set_xfer_data(CC_CAUSE_OK, CC_XFER_METHOD_BYE, CC_NO_CALL_ID, FSMXFR_NULL_DIALSTRING, &(data.xfer)); cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line, ftr_id, &data, CC_CAUSE_NORMAL); /* * Make sure we have a valid dialstring. */ if (fsmxfr_copy_dialstring(&xcb->dialstring, msg->data.xfer.dialstring) == FALSE) { fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); break; } /* * Mark the active flag in the xcb. This flag is used later * when the RELEASE comes from SIP, so that the GSM * knows that this call is still involved in a transfer. */ xcb->active = TRUE; break; case CC_XFER_METHOD_REFER: int_rc = fsmxfr_remote_transfer(fcb, ftr_id, call_id, dcb->line, msg->data.xfer.dialstring, msg->data.xfer.referred_by); if (int_rc == FALSE) { fsmxfr_set_xfer_data(CC_CAUSE_ERROR, CC_XFER_METHOD_REFER, CC_NO_CALL_ID, FSMXFR_NULL_DIALSTRING, &(data.xfer)); cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id, dcb->line, ftr_id, &data, CC_CAUSE_ERROR); } break; default: break; } /* switch (msg->data.xfer.method) */ break; case CC_FEATURE_NOTIFY: /* * This could be: * 1. for an unattended transfer. * The transferee is notifying us, the transferor, of the status * of the transfer, ie. was the transferee able to connect to * the target? * * 2. Or this is an attended transfer, and this is the call from * the transferee to the target. The stack will NOTIFY the GSM * of the status of the call after it receives a message * from the network indicating success of failure. */ if (msg->data_valid == FALSE) { break; } /* * Ack the request. */ cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line, CC_FEATURE_NOTIFY, NULL, CC_CAUSE_NORMAL); switch (xcb->type) { case FSMXFR_TYPE_BLND_XFR: switch (msg->data.notify.method) { case CC_XFER_METHOD_BYE: /* * This notification is really from the SIP stack. * The network will not send a NOTIFY for a BYE/Also * transfer, so the SIP stack just sends one up when * it uses that method. */ /* * Release the transfer call. */ data.endcall.cause = CC_CAUSE_OK; cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line, CC_FEATURE_END_CALL, &data); break; case CC_XFER_METHOD_REFER: if (msg->data.notify.cause == CC_CAUSE_OK) { /* * Release the transfer call. */ /* Set the dcb flag to indicate transfer is complete, so that * it won't display endcall in this case */ fsmxfr_mark_dcb_for_xfr_complete(xcb->cns_call_id, xcb->xfr_call_id, TRUE); data.endcall.cause = CC_CAUSE_OK; cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line, CC_FEATURE_END_CALL, &data); } else { fsmxfr_cleanup(fcb, __LINE__, TRUE); cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line, CC_FEATURE_RESUME, NULL); } break; default: break; } /* switch (msg->data.notify.method) { */ break; case FSMXFR_TYPE_DIR_XFR: /* * Clear the transfer call if the transfer was OK, * else just cleanup the transfer. The consultation * call will be released by the target. */ xfr_dcb = fsm_get_dcb(xcb->xfr_call_id); if (msg->data.notify.cause == CC_CAUSE_OK) { data.endcall.cause = CC_CAUSE_OK; fsmxfr_mark_dcb_for_xfr_complete(xcb->cns_call_id, xcb->xfr_call_id, TRUE); cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xfr_dcb->call_id, xfr_dcb->line, CC_FEATURE_END_CALL, &data); } else { lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED), dcb->line, xcb->xfr_call_id); lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED), dcb->line, xcb->cns_call_id); } if (xcb->cnf_xfr) { /* * If it is a conference transfer clear up the * calls. */ fsmxfr_cnf_cleanup(xcb); } break; case FSMXFR_TYPE_XFR: switch (msg->data.notify.method) { case CC_XFER_METHOD_BYE: /* * Release the consultation call. */ cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xcb->cns_call_id, dcb->line, CC_FEATURE_END_CALL, NULL); /* * Release the call being transferred. */ cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xcb->xfr_call_id, dcb->line, CC_FEATURE_END_CALL, NULL); break; case CC_XFER_METHOD_REFER: /* * This notification is from either the target (which is * the consultation call) or the transferee (which is the * transfer call). * * So, do the following based on each case: * * 1. consultation call: this is the transferee receiving * notification of the status of the call from the * transferee to the target. * - notify the transfer call. * - the transfer call will be released by the transferor * after receiving the above notification and the * consultation call will remain. * * 2. transfer call: this is the transferor receiving * notification of the status of the call from the * transferee to the target. * - release the transfer call. * - the consultation call will be released by the target. */ xfr_dcb = fsm_get_dcb(xcb->xfr_call_id); if (call_id == xcb->cns_call_id) { /* * Notify the transfer call of the status of the * transfer. */ data.notify.cause = msg->data.notify.cause; data.notify.cause_code = msg->data.notify.cause_code; data.notify.subscription = CC_SUBSCRIPTIONS_XFER; data.notify.method = CC_XFER_METHOD_REFER; data.notify.blind_xferror_gsm_id = msg->data.notify.blind_xferror_gsm_id; data.notify.final = TRUE; if (data.notify.blind_xferror_gsm_id == CC_NO_CALL_ID) { cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, xfr_dcb->call_id, xfr_dcb->line, CC_FEATURE_NOTIFY, &data); } else { cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, data.notify.blind_xferror_gsm_id, msg->line, CC_FEATURE_NOTIFY, &data); } } else { /* * Clear the transfer call if the transfer was OK, * else just cleanup the transfer. The consultation * call will be released by the target. */ if (xcb == NULL) { GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname); break; } if (msg->data.notify.cause == CC_CAUSE_OK) { data.endcall.cause = CC_CAUSE_OK; fsmxfr_mark_dcb_for_xfr_complete(xcb->cns_call_id, xcb->xfr_call_id, TRUE); cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xfr_dcb->call_id, xfr_dcb->line, CC_FEATURE_END_CALL, &data); } else { lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED), dcb->line, xcb->xfr_call_id); fsmxfr_mark_dcb_for_xfr_complete(xcb->cns_call_id, xcb->xfr_call_id, FALSE); fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); /* * Resume the consultation call. * if xcb->cns_call_id == 0, then cns call is already ended. */ if (xcb->cns_call_id != CC_NO_CALL_ID) { lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED), dcb->line, xcb->cns_call_id); cns_dcb = fsm_get_dcb (xcb->cns_call_id); cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xcb->cns_call_id, cns_dcb->line, CC_FEATURE_RESUME, NULL); } if (xcb->cnf_xfr) { /* * If it is a conference transfer clear up the * calls. */ fsmxfr_cnf_cleanup(xcb); } } fsmxfr_cleanup(fcb, __LINE__, TRUE); } /* if (call_id == xcb->cns_call_id) */ break; default: break; } /* switch (msg->data.notify.method) { */ break; default: break; } /* switch (xcb->type) { */ break; default: fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); break; } /* switch (ftr_id) */ break; default: fsm_sm_ignore_src(fcb, __LINE__, src_id); break; } /* switch (src_id) */ return (sm_rc); } static void fsmxfr_requeue_blind_xfer_dialstring (fsmdef_dcb_t *dcb, fsmxfr_xcb_t *xcb) { /* * This is the result of the feature hold so we * can clear the active feature. */ dcb->active_feature = CC_FEATURE_NONE; /* * If there is a dialstring on the xcb, it is because the * dialstring event for the consultative call has already * been received. We delayed handling the dialstring until * the result of the hold request has been received. We * are now ready to process the dial string so requeue it * for processing */ if (xcb->queued_dialstring && xcb->queued_dialstring[0] != '\0') { cc_dialstring(CC_SRC_UI, xcb->cns_call_id, dcb->line, xcb->queued_dialstring); cpr_free(xcb->queued_dialstring); xcb->queued_dialstring = NULL; } } static sm_rcs_t fsmxfr_ev_active_feature_ack (sm_event_t *event) { static const char fname[] = "fsmxfr_ev_active_feature_ack"; fsm_fcb_t *fcb = (fsm_fcb_t *) event->data; cc_feature_ack_t *msg = (cc_feature_ack_t *) event->msg; cc_srcs_t src_id = msg->src_id; cc_features_t ftr_id = msg->feature_id; fsmdef_dcb_t *dcb = fcb->dcb; cc_feature_data_t data; sm_rcs_t sm_rc = SM_RC_CONT; fsmxfr_xcb_t *xcb = fcb->xcb; fsmdef_dcb_t *xfr_dcb = NULL; char *called_num = NULL; fsm_sm_ftr(ftr_id, src_id); /* * Consume the XFER events and don't pass them along to the other FSMs. */ if ((ftr_id == CC_FEATURE_BLIND_XFER) || (ftr_id == CC_FEATURE_XFER)) { sm_rc = SM_RC_END; } if (xcb == NULL) { GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname); return (sm_rc); } switch (src_id) { case CC_SRC_SIP: case CC_SRC_GSM: switch (ftr_id) { case CC_FEATURE_BLIND_XFER: switch (xcb->type) { case FSMXFR_TYPE_BLND_XFR: switch (msg->data.xfer.method) { case CC_XFER_METHOD_REFER: /* * Clear the call if the transfer was OK, else just cleanup * the transfer. */ if (msg->cause == CC_CAUSE_OK) { // This does not indicate that transfer was successful. So wait for the NOTIFYs. //data.endcall.cause = CC_CAUSE_OK; //cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, //dcb->line, CC_FEATURE_END_CALL, &data); } else { fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); } break; default: fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); break; } /* switch (msg->data.xfer.method) { */ break; default: break; } /* switch (xcb->type) { */ break; case CC_FEATURE_HOLD: if (msg->cause == CC_CAUSE_REQUEST_PENDING) { /* * HOLD request is pending. Let this event drop through * to the default sm for handling. */ fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); break; } /* * If this is the hold response during a blind transfer, * check to see if a dialstring has been queued for processing. */ if (xcb->type == FSMXFR_TYPE_BLND_XFR && xcb->xfr_call_id == fcb->call_id) { fsmxfr_requeue_blind_xfer_dialstring(dcb, xcb); break; } /* * If xfer soft key has not been pressed a second time then * don't transfer the call. */ if (!xcb->xfer_comp_req) { break; } /* If there is any error reported from SIP stack then clear the * transfer data. One such case is where digest authentication * fails due to maximum retry (of 2). SIP stack generates a valid * ACK event by setting cause code to ERROR */ if (msg->cause == CC_CAUSE_ERROR) { lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED), xcb->xfr_line, xcb->xfr_call_id); lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED), xcb->cns_line, xcb->cns_call_id); fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); break; } /* * Instruct the stack to transfer the call. */ if (xcb->type == FSMXFR_TYPE_XFR) { /* * Check to see which side of the transfer * the request is coming from. if it is from * the XFR side, make sure the CNS side is * indeed in the Held state. */ if (xcb->cns_call_id == fcb->call_id) { xfr_dcb = fsm_get_dcb(xcb->xfr_call_id); called_num = fsmxfr_get_dialed_num(fcb->dcb); if (called_num && called_num[0] != '\0') { fsmxfr_set_xfer_data(CC_CAUSE_XFER_LOCAL, xcb->method, fcb->dcb->call_id, called_num, &(data.xfer)); cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, xfr_dcb->call_id, xfr_dcb->line, CC_FEATURE_XFER, &data); } else { /* * Can't transfer the call without a dialstring, so * just cleanup the transfer. */ fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); } } else { /* * This is hold response on the xfer call leg. * * Check to see if dialstring has been queued for * processing. If so, it is requeued to GSM and * we delay until the consultative call is setup. */ //if (fsmxfr_requeue_blind_xfer_dialstring(dcb, xcb)) { // break; //} /* * Get the dcb of the consultative call. */ xfr_dcb = fsm_get_dcb(xcb->cns_call_id); /* * We must wait for the hold request on the consultative * call to complete before completing the xfer. The state * of the consultative call must be FSMDEF_S_HOLDING. * FSMDEF_S_HOLDING is not a completed hold request which * is why it is not checked for here. */ if (xfr_dcb->fcb->state != FSMDEF_S_HOLDING) { break; } else { called_num = fsmxfr_get_dialed_num(xfr_dcb); if (called_num && called_num[0] != '\0') { fsmxfr_set_xfer_data(CC_CAUSE_XFER_LOCAL, xcb->method, xfr_dcb->call_id, called_num, &(data.xfer)); cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, fcb->dcb->call_id, fcb->dcb->line, CC_FEATURE_XFER, &data); } else { /* * Can't transfer the call without a dialstring, so * just cleanup the transfer. */ fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); } } } } break; case CC_FEATURE_XFER: if (msg->cause != CC_CAUSE_OK) { lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED), xcb->xfr_line, xcb->xfr_call_id); lsm_ui_display_status(platform_get_phrase_index_str(TRANSFER_FAILED), xcb->cns_line, xcb->cns_call_id); fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); } else { fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); } break; default: fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id); break; } /* switch (ftr_id) */ break; default: fsm_sm_ignore_src(fcb, __LINE__, src_id); break; } /* switch (src_id) */ return (sm_rc); } static sm_rcs_t fsmxfr_ev_active_onhook (sm_event_t *event) { static const char fname[] = "fsmxfr_ev_active_onhook"; fsm_fcb_t *fcb = (fsm_fcb_t *) event->data; cc_onhook_t *msg = (cc_onhook_t *) event->msg; callid_t call_id = msg->call_id; callid_t other_call_id; fsmxfr_xcb_t *xcb = fcb->xcb; fsm_fcb_t *other_fcb; fsmdef_dcb_t *xfr_dcb; cc_feature_data_t data; fsm_fcb_t *cns_fcb, *xfr_fcb; int onhook_xfer = 0; if (xcb == NULL) { GSM_DEBUG_ERROR(GSM_F_PREFIX"Cannot find the active xfer\n", fname); return (SM_RC_CONT); } cns_fcb = fsm_get_fcb_by_call_id_and_type(xcb->cns_call_id, FSM_TYPE_DEF); xfr_fcb = fsm_get_fcb_by_call_id_and_type(xcb->xfr_call_id, FSM_TYPE_DEF); if (xcb->cnf_xfr) { /* * This is the conference transfer so clear the * second line also. */ xcb->cnf_xfr = FALSE; other_call_id = fsmxfr_get_other_call_id(xcb, call_id); other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id, FSM_TYPE_XFR); cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, other_call_id, other_fcb ? other_fcb->dcb->line:CC_NO_LINE, CC_FEATURE_END_CALL, NULL); fsmxfr_cleanup(fcb, __LINE__, TRUE); return (SM_RC_CONT); } if (xcb->mode == FSMXFR_MODE_TRANSFEREE) { xfr_dcb = fsm_get_dcb(xcb->xfr_call_id); if (call_id == xcb->cns_call_id) { /* * Transferee ended call before transfer was completed. * Notify the transfer call of the status of the * transfer. * * Note: This must be changed when fix is put in for configurable * onhook xfer (CSCsb86757) so that 200 NOTIFY is sent when * xfer is completed. fsmxfr_initiate_xfr will take care * of this for us so just need to make sure the NOTIFY is * sent to SIP stack only when xfer is abandoned due to * onhook. */ data.notify.cause = CC_CAUSE_ERROR; data.notify.subscription = CC_SUBSCRIPTIONS_XFER; data.notify.method = CC_XFER_METHOD_REFER; data.notify.final = TRUE; cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, xfr_dcb->call_id, xfr_dcb->line, CC_FEATURE_NOTIFY, &data); if (cns_fcb && cns_fcb->state != FSMDEF_S_HOLDING && cns_fcb->state != FSMDEF_S_HOLD_PENDING) { fsmxfr_feature_cancel(xcb, xfr_dcb->line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); } /* * fix bug CSCtb23681. */ if( xfr_dcb->fcb->state == FSMDEF_S_HOLDING ) cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, xfr_dcb->call_id,xfr_dcb->line, CC_FEATURE_END_CALL, NULL); return (SM_RC_CONT); } } if (msg->softkey) { /* * Softkey set to TRUE indicates endcall softkey was pressed. * This causes the call with focus to release. */ if ((call_id == xcb->cns_call_id) && (cns_fcb->state == FSMDEF_S_HOLDING || cns_fcb->state == FSMDEF_S_HOLD_PENDING)) { /* ignore the onhook event for the held consultation call */ } if (msg->active_list == CC_REASON_ACTIVECALL_LIST) { /* Active call list has been requested also * existing consult call is canceled * But transfer state machine will remain * intact as feature layer is still running.*/ xcb->cns_call_id = CC_NO_CALL_ID; xcb->cns_line = CC_NO_LINE; }else { fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); } return (SM_RC_CONT); } else { /* * Softkey set to FALSE indicates handset, speaker button, or * headset went onhook. This causes the transfer to complete * if onhook xfer is enabled by config. */ config_get_value(CFGID_XFR_ONHOOK_ENABLED, &onhook_xfer, sizeof(onhook_xfer)); if (onhook_xfer && ((cns_fcb->state == FSMDEF_S_OUTGOING_ALERTING)|| (cns_fcb->state == FSMDEF_S_CONNECTED))) { fsmxfr_initiate_xfr(event); return (SM_RC_END); } else if (onhook_xfer && xfr_fcb && ((xfr_fcb->state == FSMDEF_S_OUTGOING_ALERTING)|| (xfr_fcb->state == FSMDEF_S_CONNECTED))) { fsmxfr_initiate_xfr(event); return (SM_RC_END); } else { fsmxfr_feature_cancel(xcb, xcb->xfr_line, xcb->xfr_call_id, xcb->cns_call_id, CC_SK_EVT_TYPE_IMPLI); fsmxfr_cleanup(fcb, __LINE__, TRUE); return (SM_RC_CONT); } } } /* * This event can only happen if the user initiated a blind transfer. */ static sm_rcs_t fsmxfr_ev_active_dialstring (sm_event_t *event) { static const char fname[] = "fsmxfr_ev_active_dialstring"; fsm_fcb_t *fcb = (fsm_fcb_t *) event->data; cc_dialstring_t *msg = (cc_dialstring_t *) event->msg; callid_t call_id = msg->call_id; line_t line = msg->line; fsmdef_dcb_t *xfr_dcb; fsmxfr_xcb_t *xcb = fcb->xcb; cc_feature_data_t data; char *dialstring; /* * Make sure we have a valid dialstring. */ dialstring = msg->dialstring; if ((dialstring == NULL) || (dialstring[0] == '\0')) { FSM_DEBUG_SM(DEB_L_C_F_PREFIX"dialstring= %c\n", DEB_L_C_F_PREFIX_ARGS(FSM, msg->line, call_id, fname), '\0'); return (SM_RC_END); } FSM_DEBUG_SM(DEB_L_C_F_PREFIX"dialstring= %s\n", DEB_L_C_F_PREFIX_ARGS(FSM, msg->line, call_id, fname), dialstring); /* * If this is a blind xfer and we have received the dialstring * before the original call has received a response to the hold * request, defer processing the dialstring event. active_feature * will be set to CC_FEATURE_BLIND_XFER if we are still waiting * for the hold response. The dial string is saved on the xcb * until needed. */ if (xcb == NULL) { return (SM_RC_END); } xfr_dcb = fsm_get_dcb(xcb->xfr_call_id); if (xfr_dcb == NULL) { return (SM_RC_END); } if (xfr_dcb->active_feature == CC_FEATURE_BLIND_XFER) { if (!fsmxfr_copy_dialstring(&xcb->queued_dialstring, dialstring)) { GSM_DEBUG_ERROR(GSM_L_C_F_PREFIX"unable to copy dialstring\n", msg->line, call_id, fname); } return (SM_RC_END); } if (xcb->type == FSMXFR_TYPE_BLND_XFR) { lsm_set_hold_ringback_status(xcb->cns_call_id, FALSE); } /* * Make sure that this event came from the consultation call and that * this is a blind transfer. We ignore the event if this is a transfer * with consultation, because we want to talk to the target. For an * unattended transfer this event is the final event needed to complete * the transfer. */ if ((xcb->cns_call_id != call_id) || (xcb->type != FSMXFR_TYPE_BLND_XFR)) { return (SM_RC_CONT); } switch (xcb->method) { case CC_XFER_METHOD_BYE: case CC_XFER_METHOD_REFER: /* * This is an unattended transfer so: * 1. clear the consultation call, * 2. instruct the stack to initiate the transfer. */ /* * Release this call because it was only used to collect digits. */ data.endcall.cause = CC_CAUSE_NORMAL; cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, call_id, line, CC_FEATURE_END_CALL, &data); /* * Send an event to the transferer call leg so it can send the called * number to the transferee. */ fsmxfr_set_xfer_data(CC_CAUSE_XFER_LOCAL, xcb->method, CC_NO_CALL_ID, dialstring, &(data.xfer)); cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, xcb->xfr_call_id, line, fsmxfr_type_to_feature(xcb->type), &data); FSM_DEBUG_SM(get_debug_string(FSMXFR_DBG_XFR_INITIATED), xcb->xfr_id, xcb->xfr_call_id, xcb->cns_call_id, __LINE__); break; default: break; } /* switch (xcb->method) */ return (SM_RC_END); } cc_int32_t fsmxfr_show_cmd (cc_int32_t argc, const char *argv[]) { fsmxfr_xcb_t *xcb; int i = 0; PR_ASSERT( i == 0 ); /* * Check if need help. */ if ((argc == 2) && (argv[1][0] == '?')) { debugif_printf("%s", "show fsmxfr\n"); return (0); } debugif_printf("%s", "\n------------------------ FSMXFR xcbs -------------------------"); debugif_printf("%s", "\ni xfr_id xcb type method xfr_call_id cns_call_id"); debugif_printf("%s", "\n--------------------------------------------------------------\n"); FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, FSMXFR_MAX_XCBS) { debugif_printf("%-2d %-6d 0x%8p %-4d %-6d %-11d %-11d\n", i++, xcb->xfr_id, xcb, xcb->type, xcb->method, xcb->xfr_call_id, xcb->cns_call_id); } return (0); } void fsmxfr_init (void) { fsmxfr_xcb_t *xcb; /* * Initialize the xcbs. */ fsmxfr_xcbs = (fsmxfr_xcb_t *) cpr_calloc(FSMXFR_MAX_XCBS, sizeof(fsmxfr_xcb_t)); FSM_FOR_ALL_CBS(xcb, fsmxfr_xcbs, FSMXFR_MAX_XCBS) { fsmxfr_init_xcb(xcb); } /* * Initialize the state/event table. */ fsmxfr_sm_table.min_state = FSMXFR_S_MIN; fsmxfr_sm_table.max_state = FSMXFR_S_MAX; fsmxfr_sm_table.min_event = CC_MSG_MIN; fsmxfr_sm_table.max_event = CC_MSG_MAX; fsmxfr_sm_table.table = (&(fsmxfr_function_table[0][0])); } cc_transfer_mode_e cc_is_xfr_call (callid_t call_id) { static const char fname[] = "cc_is_xfr_call"; int mode; if (call_id == CC_NO_CALL_ID) { return CC_XFR_MODE_NONE; } mode = fsmutil_is_xfr_leg(call_id, fsmxfr_xcbs, FSMXFR_MAX_XCBS); switch (mode) { case FSMXFR_MODE_TRANSFEROR: FSM_DEBUG_SM(DEB_F_PREFIX"xfer mode is transferor for call id = %d\n", DEB_F_PREFIX_ARGS(FSM, fname), call_id); return CC_XFR_MODE_TRANSFEROR; case FSMXFR_MODE_TRANSFEREE: FSM_DEBUG_SM(DEB_F_PREFIX"xfer mode is transferee for call id = %d\n", DEB_F_PREFIX_ARGS(FSM, fname), call_id); return CC_XFR_MODE_TRANSFEREE; case FSMXFR_MODE_TARGET: FSM_DEBUG_SM(DEB_F_PREFIX"xfer mode is target for call id = %d\n", DEB_F_PREFIX_ARGS(FSM, fname), call_id); return CC_XFR_MODE_TARGET; default: FSM_DEBUG_SM(DEB_F_PREFIX"invalid xfer mode %d for call id = %d\n", DEB_F_PREFIX_ARGS(FSM, fname), mode, call_id); return CC_XFR_MODE_NONE; } } void fsmxfr_shutdown (void) { cpr_free(fsmxfr_xcbs); fsmxfr_xcbs = NULL; } int fsmutil_is_xfr_consult_call (callid_t call_id) { return fsmutil_is_xfr_consult_leg(call_id, fsmxfr_xcbs, FSMXFR_MAX_XCBS); } callid_t fsmxfr_get_consult_call_id (callid_t call_id) { fsmxfr_xcb_t *xcb; xcb = fsmxfr_get_xcb_by_call_id(call_id); if (xcb && call_id == xcb->xfr_call_id) { return (fsmxfr_get_other_call_id(xcb, call_id)); } else { return (CC_NO_CALL_ID); } } callid_t fsmxfr_get_primary_call_id (callid_t call_id) { fsmxfr_xcb_t *xcb; xcb = fsmxfr_get_xcb_by_call_id(call_id); if (xcb && (xcb->cns_call_id == call_id)) { return (fsmxfr_get_other_call_id(xcb, call_id)); } else { return (CC_NO_CALL_ID); } }