2012-12-18 11:01:51 -05:00

3043 lines
109 KiB
C
Executable File

/* 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);
}
}