/* 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 <errno.h>

#include "cpr_types.h"
#include "cpr_stdio.h"
#include "cpr_stdlib.h"
#include "cpr_timers.h"
#include "cpr_string.h"
#include "cpr_memory.h"
#include "cpr_ipc.h"
#include "cpr_in.h"
#include "util_string.h"
#include "task.h"
#include "phntask.h"
#include "ccsip_core.h"
#include "ccsip_messaging.h"
#include "phone_debug.h"
#include "ccsip_platform.h"
#include "ccsip_platform_timers.h"
#include "ccsip_macros.h"
#include "ccsip_pmh.h"
#include "ccsip_register.h"
#include "ccsip_task.h"
#include "ccsip_credentials.h"
#include "debug.h"
#include "logmsg.h"
#include "ccsip_pmh.h"
#include "ccsip_credentials.h"
#include "dns_utils.h"
#include "config.h"
#include "sip_common_transport.h"
#include "uiapi.h"
#include "sip_common_regmgr.h"
#include "text_strings.h"
#include "sip_interface_regmgr.h"
#include "phone_platform_constants.h"
#include "ccsip_common_cb.h"
#include "misc_util.h"

extern sipPlatformUITimer_t sipPlatformUISMTimers[];
extern void *new_standby_available;
extern boolean regall_fail_attempt;
extern boolean registration_reject;

extern void ui_set_sip_registration_state(line_t line, boolean registered);
extern void ui_update_registration_state_all_lines(boolean registered);

boolean dump_reg_msg = TRUE;
boolean refresh_reg_msg = FALSE;

static ccsip_register_states_t ccsip_register_state = SIP_REG_IDLE;

/* Need an additional ack timer for the backup proxy */
static cprTimer_t ack_tmrs[MAX_REG_LINES + 1];
ccm_act_stdby_table_t CCM_Active_Standby_Table;
extern ccm_failover_table_t CCM_Failover_Table;
static boolean start_standby_monitor = TRUE;
extern boolean Is794x;

static void show_register_data(void);

#define SIP_REG_TMR_EXPIRE_TICKS 55000 /* msec; re-registration timer */
#define REGISTER_CMD "register"
#define SIP_REG_TMR_ACK_TICKS    32000 /* msec; supervision timer equivalent to Timer F */

static const char *ccsip_register_state_names[] = {
    "IDLE",
    "REGISTERING",
    "REGISTERED",
    "UNREGISTERING",
    "PRE_FALLBACK",
    "IN_FAILOVER",
    "POST_FAILOVER",
    "STANDBY_FAILOVER",
    "NO_CC",
    "NO_STANDBY",
    "NO_REGISTER"
};

static const char *ccsip_register_reg_state_names[] = {
    "NONE",
    "IDLE",
    "REGISTERING",
    "REGISTERED",
    "UNREGISTERING",
    "PRE_FALLBACK",
    "IN_FAILOVER",
    "POST_FAILOVER",
    "STANDBY_FAILOVER",
    "NO_CC",
    "NO_STANDBY",
    "NO_REGISTER"
};


void ccsip_handle_ev_reg_req(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_reg_cancel(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_unreg_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_1xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_2xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_3xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_4xx(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_failure_response(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_tmr_expire(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_tmr_ack(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_tmr_retry(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_unreg_tmr_ack(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_standby_keepalive_tmr_expire(ccsipCCB_t *ccb, sipSMEvent_t *event);
void ccsip_handle_ev_idle_tmr_expire(ccsipCCB_t *ccb, sipSMEvent_t *event);

const char *ccsip_register_state_name(ccsip_register_states_t state);
const char *ccsip_register_reg_state_name(sipRegSMStateType_t state);
const char *sip_util_reg_event2string(sipRegSMEventType_t event);
char *sip_util_reg_state2string(sipRegSMStateType_t state);
void ccsip_register_retry_timer_start(ccsipCCB_t *ccb);
void ccsip_register_cleanup(ccsipCCB_t *ccb, boolean start);
cc_int32_t ccsip_register_cmd(cc_int32_t argc, const char *argv[]);
void ccsip_register_clear_all_logs(void);
boolean ccsip_register_all_registered(void);

/* convert sip line to reg line  */
#define SIP_REG_LINE2REGLINE(line)  ((line) - (REG_CCB_START))

/* convert reg line to sip line  */
#define SIP_REG_REGLINE2LINE(line)  ((line) + (REG_CCB_START))

// Date holder
static struct {
    boolean valid;
    char datestring[MAX_SIP_DATE_LENGTH];
} ccm_date;

static const sipSMEventActionFn_t
gSIPRegSMTable[SIP_REG_STATE_END - SIP_REG_STATE_BASE + 1]
              [SIPSPI_REG_EV_END - SIPSPI_REG_EV_BASE + 1] =
{
    /* SIP_REG_STATE_IDLE
     */
    {
     /* E_SIP_REG_REG_REQ          */ ccsip_handle_ev_reg_req,
     /* E_SIP_REG_CANCEL           */ ccsip_handle_ev_default,
     /* E_SIP_REG_1xx              */ ccsip_handle_ev_default,
     /* E_SIP_REG_2xx              */ ccsip_handle_ev_default,
     /* E_SIP_REG_3xx              */ ccsip_handle_ev_default,
     /* E_SIP_REG_4xx              */ ccsip_handle_ev_default,
     /* E_SIP_REG_FAILURE_RESPONSE */ ccsip_handle_ev_default,
     /* E_SIP_REG_TMR_ACK          */ ccsip_handle_ev_default,
     /* E_SIP_REG_TMR_EXPIRE       */ ccsip_handle_ev_idle_tmr_expire,
     /* E_SIP_REG_TMR_WAIT         */ ccsip_handle_ev_default,
     /* E_SIP_REG_TMR_RETRY        */ ccsip_handle_ev_default,
     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
     },

    /* SIP_REG_STATE_REGISTERING
     */
    {
     /* E_SIP_REG_REG_REQ          */ ccsip_handle_ev_reg_req,
     /* E_SIP_REG_CANCEL           */ ccsip_handle_ev_reg_cancel,
     /* E_SIP_REG_1xx              */ ccsip_handle_ev_1xx,
     /* E_SIP_REG_2xx              */ ccsip_handle_ev_2xx,
     /* E_SIP_REG_3xx              */ ccsip_handle_ev_3xx,
     /* E_SIP_REG_4xx              */ ccsip_handle_ev_4xx,
     /* E_SIP_REG_FAILURE_RESPONSE */ ccsip_handle_ev_failure_response,
     /* E_SIP_REG_TMR_ACK          */ ccsip_handle_ev_tmr_ack,
     /* E_SIP_REG_TMR_EXPIRE       */ ccsip_handle_ev_tmr_expire,
     /* E_SIP_REG_TMR_WAIT         */ ccsip_handle_ev_default,
     /* E_SIP_REG_TMR_RETRY        */ ccsip_handle_ev_tmr_retry,
     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
     },

    /* SIP_REG_STATE_REGISTERED
     */
    {
     /* E_SIP_REG_REG_REQ          */ ccsip_handle_ev_default,
     /* E_SIP_REG_CANCEL           */ ccsip_handle_ev_reg_cancel,
     /* E_SIP_REG_1xx              */ ccsip_handle_ev_default,
     /* E_SIP_REG_2xx              */ ccsip_handle_ev_default,
     /* E_SIP_REG_3xx              */ ccsip_handle_ev_default,
     /* E_SIP_REG_4xx              */ ccsip_handle_ev_default,
     /* E_SIP_REG_FAILURE_RESPONSE */ ccsip_handle_ev_default,
     /* E_SIP_REG_TMR_ACK          */ ccsip_handle_ev_default,
     /* E_SIP_REG_TMR_EXPIRE       */ ccsip_handle_ev_tmr_expire,
     /* E_SIP_REG_TMR_WAIT         */ ccsip_handle_ev_default,
     /* E_SIP_REG_TMR_RETRY        */ ccsip_handle_ev_tmr_retry,
     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
     },

    /* SIP_REG_STATE_UNREGISTERING
     */
    {
     /* E_SIP_REG_REG_REQ          */ ccsip_handle_ev_default,
     /* E_SIP_REG_CANCEL           */ ccsip_handle_ev_reg_cancel,
     /* E_SIP_REG_1xx              */ ccsip_handle_ev_1xx,
     /* E_SIP_REG_2xx              */ ccsip_handle_ev_unreg_2xx,
     /* E_SIP_REG_3xx              */ ccsip_handle_ev_3xx,
     /* E_SIP_REG_4xx              */ ccsip_handle_ev_4xx,
     /* E_SIP_REG_FAILURE_RESPONSE */ ccsip_handle_ev_failure_response,
     /* E_SIP_REG_TMR_ACK          */ ccsip_handle_ev_unreg_tmr_ack,
     /* E_SIP_REG_TMR_EXPIRE       */ ccsip_handle_ev_default,
     /* E_SIP_REG_TMR_WAIT         */ ccsip_handle_ev_standby_keepalive_tmr_expire,
     /* E_SIP_REG_TMR_RETRY        */ ccsip_handle_ev_tmr_retry,
     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
     },

    /* SIP_REG_STATE_IN_FALLBACK
     */
    {
     /* E_SIP_REG_REG_REQ          */ sip_regmgr_ev_default,
     /* E_SIP_REG_CANCEL           */ sip_regmgr_ev_cancel,
     /* E_SIP_REG_1xx              */ ccsip_handle_ev_1xx,
     /* E_SIP_REG_2xx              */ sip_regmgr_ev_in_fallback_2xx,
     /* E_SIP_REG_3xx              */ sip_regmgr_ev_default,
     /* E_SIP_REG_4xx              */ sip_regmgr_ev_fallback_retry,
     /* E_SIP_REG_FAILURE_RESPONSE */ sip_regmgr_ev_fallback_retry,
     /* E_SIP_REG_TMR_ACK          */ sip_regmgr_ev_tmr_ack_retry,
     /* E_SIP_REG_TMR_EXPIRE       */ sip_regmgr_ev_default,
     /* E_SIP_REG_TMR_WAIT         */ sip_regmgr_ev_default,
     /* E_SIP_REG_TMR_RETRY        */ sip_regmgr_ev_tmr_ack_retry,
     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
     },
    /* SIP_REG_STATE_STABILITY_CHECK
     */
    {
     /* E_SIP_REG_REG_REQ          */ sip_regmgr_ev_default,
     /* E_SIP_REG_CANCEL           */ sip_regmgr_ev_cancel,
     /* E_SIP_REG_1xx              */ ccsip_handle_ev_1xx,
     /* E_SIP_REG_2xx              */ sip_regmgr_ev_stability_check_2xx,
     /* E_SIP_REG_3xx              */ sip_regmgr_ev_default,
     /* E_SIP_REG_4xx              */ sip_regmgr_ev_default,
     /* E_SIP_REG_FAILURE_RESPONSE */ sip_regmgr_ev_default,
     /* E_SIP_REG_TMR_ACK          */ sip_regmgr_ev_tmr_ack_retry,
     /* E_SIP_REG_TMR_EXPIRE       */ sip_regmgr_ev_default,
     /* E_SIP_REG_TMR_WAIT         */ sip_regmgr_ev_stability_check_tmr_wait,
     /* E_SIP_REG_TMR_RETRY        */ sip_regmgr_ev_tmr_ack_retry,
     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_default,
     },
    /* SIP_REG_STATE_TOKEN_WAIT
     */
    {
     /* E_SIP_REG_REG_REQ          */ sip_regmgr_ev_default,
     /* E_SIP_REG_CANCEL           */ sip_regmgr_ev_default,
     /* E_SIP_REG_1xx              */ ccsip_handle_ev_1xx,
     /* E_SIP_REG_2xx              */ sip_regmgr_ev_token_wait_2xx,
     /* E_SIP_REG_3xx              */ sip_regmgr_ev_default,
     /* E_SIP_REG_4xx              */ ccsip_handle_ev_4xx,
     /* E_SIP_REG_FAILURE_RESPONSE */ ccsip_handle_ev_failure_response,
     /* E_SIP_REG_TMR_ACK          */ sip_regmgr_ev_tmr_ack_retry,
     /* E_SIP_REG_TMR_EXPIRE       */ sip_regmgr_ev_default,
     /* E_SIP_REG_TMR_WAIT         */ sip_regmgr_ev_token_wait_tmr_wait,
     /* E_SIP_REG_TMR_RETRY        */ sip_regmgr_ev_tmr_ack_retry,
     /* E_SIP_REG_CLEANUP          */ sip_regmgr_ev_cleanup,
     }
};

/*
 * This function gets the supported option tags from the 200 OK response
 * to the REG message sent.
 */
void
sip_get_supported_options_2xx (ccsipCCB_t *ccb, sipMessage_t *response)
{
    const char *supported;

    ccb->supported_tags = 0;
    supported = sippmh_get_cached_header_val(response, SUPPORTED);
    if (supported != NULL) {
        /*
         * Supported header is found, find the interrested supported
         * function from 2xx response from the REG msg. sent. Note that
         * the sippmh_parse_supported_require() can be used but
         * decided to minimize the impact on performance parsing all other
         * tags that are not interested during registration times and
         * vice versa making changes to sippmh_parse_supported_require()
         * to parse the tags that are not interested for other messages
         * also impact the processing time for other messages.
         */
        if (strcasestr(supported, REQ_SUPP_PARAM_CISCO_SRTP_FALLBACK)) {
            /* cisco SRTP fallback is supported by the other end */
            ccb->supported_tags |= cisco_srtp_fallback_tag;
        }
    }
}

/*
 * This function starts the Register message
 * ack timer. If no response is received from
 * the register message within four minutes,
 * this timer will pop and resend the Register
 * message.
 */
void
sip_start_ack_timer (ccsipCCB_t *ccb)
{
    uint16_t ack_timer_index;

    if (ccb->index == REG_BACKUP_CCB) {
        ack_timer_index = MAX_REG_LINES;
    } else {
        ack_timer_index = ccb->dn_line - 1;
    }

    CCSIP_DEBUG_REG_STATE( DEB_L_C_F_PREFIX " ccb->index=%d ack_timer_index=%d ",
        DEB_L_C_F_PREFIX_ARGS(SIP_STATE, ccb->dn_line, 0, "sip_start_ack_timer"),
        ccb->index, ack_timer_index);

    if (cprStartTimer(ack_tmrs[ack_timer_index], SIP_REG_TMR_ACK_TICKS,
                      (void *)(long)ccb->index) == CPR_FAILURE) {
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
                          "sip_start_ack_timer", "cprStartTimer");
    }
}


/*
 * This function stops the Register message
 * ack timer.
 */
void
sip_stop_ack_timer (ccsipCCB_t *ccb)
{
    uint16_t ack_timer_index;

    if (ccb->index == REG_BACKUP_CCB) {
        ack_timer_index = MAX_REG_LINES;
    } else {
        ack_timer_index = ccb->dn_line - 1;
    }

    CCSIP_DEBUG_REG_STATE( DEB_L_C_F_PREFIX " ccb->index=%d ack_timer_index=%d ",
        DEB_L_C_F_PREFIX_ARGS(SIP_STATE, ccb->dn_line, 0, "sip_stop_ack_timer"),
        ccb->index, ack_timer_index);

    if (cprCancelTimer(ack_tmrs[ack_timer_index]) == CPR_FAILURE) {
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
                          "sip_stop_ack_timer", "cprCancelTimer");
    }
}


void
sip_reg_sm_change_state (ccsipCCB_t *ccb, sipRegSMStateType_t new_state)
{
    if (g_disable_mass_reg_debug_print == FALSE) {
    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Registration state change: %s ---> "
                          "%s\n", DEB_L_C_F_PREFIX_ARGS(SIP_STATE, ccb->index, ccb->dn_line, "sip_reg_sm_change_state"),
                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
                          sip_util_reg_state2string(new_state));
    }
    ccb->state = (sipSMStateType_t) new_state;

    if (ccb->index == REG_CCB_START) {
        if ((new_state > SIP_REG_STATE_REGISTERED) || (!refresh_reg_msg)) {
            dump_reg_msg = TRUE;
        } else {
            dump_reg_msg = FALSE;
        }
    }
}


void
ccsip_handle_ev_reg_req (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    static const char fname[] = "ccsip_handle_ev_reg_req";
    char line_name[MAX_LINE_NAME_SIZE];
    int value;

    config_get_value(CFGID_PROXY_REGISTER, &value, sizeof(value));
    if (value == 0) {
        ui_set_sip_registration_state(ccb->dn_line, FALSE);
        CCSIP_DEBUG_REG_STATE(get_debug_string(DEBUG_REG_DISABLED),
                              ccb->index, ccb->dn_line, fname);

        return;
    }

    ccsip_register_clear_all_logs();

    sip_stop_ack_timer(ccb);
    sip_start_ack_timer(ccb);

    (void) sip_platform_register_expires_timer_stop(ccb->index);


    sip_util_get_new_call_id(ccb);

    ccb->authen.cred_type = 0;
    ccb->retx_counter = 0;
    config_get_line_string(CFGID_LINE_NAME, line_name, ccb->dn_line,
                           sizeof(line_name));

    config_get_value(CFGID_TIMER_REGISTER_EXPIRES, &ccb->reg.tmr_expire,
                     sizeof(ccb->reg.tmr_expire));
    ccb->reg.act_time = (int) time(NULL);

    if (sipSPISendRegister(ccb, 0, line_name, ccb->reg.tmr_expire) != TRUE) {
        // set expire timer and cleanup ccb in non-ccm mode.
        // do not change the expire timer value in ccm mode
        if (ccb->cc_type != CC_CCM) {
            ccsip_register_cleanup(ccb, TRUE);
        }
        log_clear(LOG_REG_MSG);
        log_msg(LOG_REG_MSG);
    }

    sip_reg_sm_change_state(ccb, SIP_REG_STATE_REGISTERING);
}



void
ccsip_handle_ev_default (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    if ((event->type == (int) E_SIP_REG_CANCEL) && (ccb->state == (int) SIP_REG_STATE_IDLE)) {
        (void) sip_platform_register_expires_timer_stop(ccb->index);
        ccb->authen.cred_type = 0;
        ccb->retx_counter     = 0;
        ccb->reg.tmr_expire   = 0;
        ccb->reg.act_time     = 0;
        ccsip_register_cleanup(ccb, FALSE);
    }

    /* only free SIP messages, timeouts are internal */
    if (event->type < (int) E_SIP_REG_TMR_ACK) {
        free_sip_message(event->u.pSipMessage);
    }
}


void
ccsip_handle_ev_tmr_expire (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    // Initiate registration if prime line or not in failover state
    if ((!CCM_Failover_Table.failover_started) ||
        (ccb->index == REG_CCB_START)) {
        /*If the phone is registered with CSPS, the failover_started flag
         * will always be false. Therefore, this code will always be
         * executed. */

        /* Cleanup and re-initialize CCB */
        sip_sm_call_cleanup(ccb);

        refresh_reg_msg = TRUE;
        /* send a message to the SIP_REG SM to initiate registration */
        if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index) != SIP_REG_OK) {
            ccsip_register_cleanup(ccb, TRUE);
        }
    } else {
        ccsip_handle_ev_default(ccb, event);
    }
}

void
ccsip_handle_ev_idle_tmr_expire (ccsipCCB_t *ccb, sipSMEvent_t *event)
{

    if (ccb->cc_type == CC_CCM) {
        if (ccb->index == REG_BACKUP_CCB) {
            ccsip_handle_ev_tmr_expire(ccb, event);
        } else {
            ccsip_handle_ev_default(ccb, event);
        }
    } else {
        /* assuming CSPS */
        /* Cleanup and re-initialize CCB */
        sip_sm_call_cleanup(ccb);

        /* send a message to the SIP_REG SM to initiate registration */
        if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index) != SIP_REG_OK) {
            ccsip_register_cleanup(ccb, TRUE);
        }
    }
}


void
ccsip_handle_ev_tmr_ack (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    log_clear(LOG_REG_AUTH_ACK_TMR);
    log_msg(LOG_REG_AUTH_ACK_TMR);

    if (ccb->cc_type == CC_CCM) {
        /*
         * regmgr - Send tmr ack event to the regmgr/
         */
        sip_regmgr_ev_tmr_ack_retry(ccb, event);
    } else {
        /*
         * regmgr - Assume it is csps
         */
        /* Cleanup and re-initialize CCB */
        sip_sm_call_cleanup(ccb);
        /* send a message to the SIP_REG SM to initiate registration */
        if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index) != SIP_REG_OK) {
            ccsip_register_cleanup(ccb, TRUE);
        }
    }
}


void
ccsip_handle_ev_1xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    static const char fname[] = "ccsip_handle_ev_1xx";
    sipMessage_t   *response = NULL;
    int             status_code = 0;
    char            status[LOG_MAX_LEN];

    response = event->u.pSipMessage;

    if (sipGetResponseCode(response, &status_code) < 0) {
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE),
                          ccb->index, ccb->dn_line, fname);
        return;
    }

    free_sip_message(response);

    switch (status_code) {
    case SIP_1XX_TRYING:
        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY2),
                          ccb->index, ccb->dn_line, fname,
                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
                          "SIP", status_code);
        return;

    default:
        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY2), ccb->index,
                          ccb->dn_line, fname,
                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
                          "SIP BAD", status_code);

        snprintf(status, sizeof(status), "in %d, information", status_code);
        log_clear(LOG_REG_UNSUPPORTED);
        log_msg(LOG_REG_UNSUPPORTED, status);

        ccsip_register_cleanup(ccb, TRUE);

        return;
    }
}

static boolean
ccsip_get_exp_time_2xx (ccsipCCB_t *ccb, sipContact_t *contact_info,
                        const char *expires, uint32_t *exp_time_ret)
{
    static const char fname[] = "ccsip_get_exp_time_2xx";
    boolean         exp_found = FALSE;
    char            src_addr_str[MAX_IPADDR_STR_LEN];
    char            user[MAX_LINE_NAME_SIZE];
    int             i;
    uint32_t        gmt_time; //TODO convert to time_t
    //uint32_t          diff_time; //TODO convert to time_t
    int32_t         gmt_rc;
    uint32_t        exp_time;
    uint32_t        register_delta;

    /* did the server change the expires time? */

    /*
     * Form the contact header.
     * The proxy can return multiple contacts so we will need to see which one
     * matches this REGISTER.
     */
    config_get_value(CFGID_TIMER_REGISTER_EXPIRES, &exp_time, sizeof(uint32_t));
    if (contact_info != NULL) {
        ipaddr2dotted(src_addr_str, &ccb->src_addr);
        config_get_line_string(CFGID_LINE_NAME, user, ccb->dn_line,
                               sizeof(user));

        /* is expires header for this line included in the contact? */
        for (i = 0; i < contact_info->num_locations; i++) {
            if ((sippmh_cmpURLStrings(contact_info->locations[i]->genUrl->u.sipUrl->user,
                                      user, FALSE) == 0) &&
                (strcmp(contact_info->locations[i]->genUrl->u.sipUrl->host,
                        src_addr_str) == 0)) {
                if ((contact_info->params[i].expires > 0) &&
                    (contact_info->params[i].expires < exp_time)) {
                    exp_time = contact_info->params[i].expires;
                    exp_found = TRUE;
                    CCSIP_DEBUG_REG_STATE(get_debug_string(DEBUG_REG_PROXY_EXPIRES),
                                          ccb->index, ccb->dn_line, fname);
                    break;
                }
            }
        }
    }


    if (exp_found != TRUE) {
        //expires param is not found in the CONTACT header
        // look for Expires header.
        config_get_value(CFGID_TIMER_REGISTER_EXPIRES, &exp_time,
                         sizeof(exp_time));
        if (expires) {
            gmt_rc = gmt_string_to_seconds((char *)expires,
                                           (unsigned long *)&gmt_time);
            if (gmt_rc != -1) {
                // We only want to update the expires timeout if it is lower
                // than our predefined threshold.  We don't want to allow
                // people to keep us hung up for infinite periods of time
                if (gmt_rc == 1) {
                    // We got a numeric entry in the expires field
                    if (gmt_time < exp_time) {
                        exp_time = gmt_time;
                        exp_found = TRUE;
                    }
                }
            }
        }
    }

    /*
     * Subtract some user defined period of time to account for network delay
     * or the possibility that the registration server is down and this
     * request has to be processed by a backup server after the timeout
     * occurs. If the user has not defined this value in the config, it
     * defaults to 5 seconds.
     */
    config_get_value(CFGID_TIMER_REGISTER_DELTA, &register_delta,
                     sizeof(register_delta));

    /*
     * Sanity check the register delta value as the proxy could have returned
     * a much lower registration period in its' response. The minimum
     * registration expiration is 60 seconds to prevent the phone from being
     * flooded with msgs.
     */
  if ((exp_time - register_delta) < MIN_REGISTRATION_PERIOD) {
        CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX
                "Warning - Registration period received (%d) "
                "minus configured timer_register_delta (%d) is less than "
                "%d seconds.\n", DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname), exp_time,
                register_delta, MIN_REGISTRATION_PERIOD);
        exp_found = FALSE;
    } else {
        exp_time = exp_time - register_delta;
        exp_found = TRUE;
    }
    *exp_time_ret = exp_time;
    return (exp_found);
}

/*
 *  Function: ccsip_check_ccm_restarted
 *
 *  Parameters:
 *      new_reg_ccb  - pointer to ccsipCCB_t of REG CCB.
 *      contact_info - Pointer to sipContact_t.
 *
 *  Description:
 *      The function detects CCM just came up i.e. it sees the phone
 *  registration as a new registration. This means CCM has restarted
 *  between a REG refresh cycle.
 *
 *  Note:
 *      CCM only sends x-cisco-newreg to the first REG that it sees
 *  from the phone as a new REG. If the phone has more than one lines,
 *  the REG of the rest of the lines will not have the x-cisco-newreg.
 *
 *  Returns:
 *      TRUE - CCM restarted is detected.
 *     FALSE - CCM restarted is not detected.
 */
static boolean
ccsip_check_ccm_restarted (sipContact_t *contact_info)
{
    int i;

    if (contact_info == NULL) {
        /* No contact info. */
        return (FALSE);
    }

    for (i = 0; i < contact_info->num_locations; i++) {
        if (contact_info->params[i].flags & SIP_CONTACT_PARM_X_CISCO_NEWREG) {
            /*
             * CCM or proxy just indicates that it sees the REG
             * as a new registration to it.  This means that the CCM or
             * proxy has restarted.
             */
            return (TRUE);
        }
    }
    return (FALSE);
}

/*
 *  Function: update_sis_protocol_version
 *
 *  Parameters:
 *      response - 200 OK message pointer
 *
 *  Description:
 *      The function parses theupported header in response and updates
 *  SIS protocol version received. If no version is received the default
 *  SEADRAGON version gets updated
 *
 *  Returns:
 *     void
 */

void
update_sis_protocol_version (sipMessage_t *response)
{
    const char *supported;
    char * sipver;
    supported = sippmh_get_cached_header_val(response, SUPPORTED);
    if (supported != NULL) {
       sipver = strcasestr(supported, REQ_SUPP_PARAM_CISCO_SISTAG);
       if (sipver) {
          cc_uint32_t major = SIS_PROTOCOL_MAJOR_VERSION_SEADRAGON, minor = 0, addtnl = 0;
          if ( sscanf ( &sipver[strlen(REQ_SUPP_PARAM_CISCO_SISTAG)], "%d.%d.%d",
			         &major, &minor, &addtnl) == 3) {
            platSetSISProtocolVer ( major, minor, addtnl,REQ_SUPP_PARAM_CISCO_SISTAG);
            return;
          }
        }
    }
    /* All other cases we set the version to 1.0.0 */
    platSetSISProtocolVer ( SIS_PROTOCOL_MAJOR_VERSION_SEADRAGON, 0, 0,REQ_SUPP_PARAM_CISCO_SISTAG);
}

void
update_cme_sis_version (sipMessage_t *response)
{
    const char *supported;
    char * sipver;

    supported = sippmh_get_cached_header_val(response, SUPPORTED);
    if (supported != NULL) {
      sipver = strcasestr(supported, REQ_SUPP_PARAM_CISCO_CME_SISTAG);
      if (sipver) {
        cc_uint32_t major = 0, minor = 0, addtnl = 0;
        if ( sscanf( &sipver[strlen(REQ_SUPP_PARAM_CISCO_CME_SISTAG)], "%d.%d.%d",
                            &major, &minor, &addtnl) ==3) {
          platSetSISProtocolVer(major, minor, addtnl,REQ_SUPP_PARAM_CISCO_CME_SISTAG);
        }
      }
    }
}

void
ccsip_handle_ev_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    static const char fname[] = "ccsip_handle_ev_2xx";
    sipMessage_t   *response = event->u.pSipMessage;
    const char     *pViaHeaderStr = NULL;
    sipVia_t       *via = NULL;
    uint32_t        exp_time;
    //uint32_t        line_feature;
    int             dns_err_code;
    int             nat_enable = 0;
    int             nat_rx_proc_enable = 0;
    //int            last_button_index =0;
    const char     *datehdr = NULL;
    const char     *contact = NULL;
    const char     *expires = NULL;
    sipContact_t   *contact_info = NULL;
    //line_t          line_index = 0;
    //ccsipCCB_t     *line_ccb = NULL;


    // Extract Date header and store
    datehdr = sippmh_get_header_val(response, SIP_HEADER_DATE, NULL);
    if (datehdr) {
        // Copy date header in a global variable since it would be needed
        // to set system time
        sstrncpy(ccm_date.datestring, datehdr, sizeof(ccm_date.datestring));
        ccm_date.valid = TRUE;

        // call ntp set time handler (TNP/cnu specific; stub for legacy)
        // do not call the handler if 2xx is from StdBy CCM
        // We do not need to set the time when in the browser
        //if (ccb->index != REG_BACKUP_CCB) {
        //    SipNtpUpdateClockFromCCM();
        //}
    }

    contact = sippmh_get_cached_header_val(response, CONTACT);
    if (contact != NULL) {
        /* There is a contact header pass it */
        contact_info = sippmh_parse_contact(contact);
    }

	/* Update the sip interface protocol version */
	update_sis_protocol_version(response);

    /*update the CME for the version negotiation feature */
    update_cme_sis_version(response);

    if (ccb->dn_line == PRIMARY_LINE && ccsip_check_ccm_restarted(contact_info)) {
        /* CCM has restarted detected */
        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Detected server has restarted via line %d\n",
                            DEB_F_PREFIX_ARGS(SIP_CCM_RESTART, fname), ccb->dn_line);
        sip_regmgr_ccm_restarted(ccb);
    }

    sip_stop_ack_timer(ccb);
    clean_method_request_trx(ccb, sipMethodRegister, TRUE);

    if (ccb->send_reason_header) {
        ccb->send_reason_header = FALSE;
        sipUnregisterReason[0] = '\0';
    }

    /*
     * Get expiration time. The expriation time are from contact info. or
     * from the expire header.
     */
    expires = sippmh_get_header_val(response, SIP_HEADER_EXPIRES, NULL);
    if (ccsip_get_exp_time_2xx(ccb, contact_info, expires, &exp_time)
            == FALSE) {
        /*
         * The expire time received in 200OK is smaller than the minimum
         * allowed.  The phone will be unregistered and a message will be
         * displayed under the Status Messages.  The phone will retry to
         * register after the configured expire-timer interval.
         */
        config_get_value(CFGID_TIMER_REGISTER_EXPIRES, &exp_time,
                         sizeof(uint32_t));
        ccb->reg.tmr_expire = exp_time;
        ccb->reg.act_time = (int) time(NULL);
        sip_reg_sm_change_state(ccb, SIP_REG_STATE_UNREGISTERING);

        if (ccsip_register_send_msg(SIP_REG_CANCEL, ccb->index)
            != SIP_REG_OK) {
            ccsip_register_cleanup(ccb, FALSE);
        }
        (void) sip_platform_register_expires_timer_start(ccb->reg.tmr_expire * 1000,
                                                         ccb->index);
        if (contact_info != NULL) {
            sippmh_free_contact(contact_info);
        }
        free_sip_message(response);
        log_clear(LOG_REG_EXPIRE);
        log_msg(LOG_REG_EXPIRE);
        return;
    }


           ccb->reg.registered = 1;
           sip_reg_sm_change_state(ccb, SIP_REG_STATE_REGISTERED);
           regall_fail_attempt = FALSE;

           if (ccb->index != REG_BACKUP_CCB) {
           	   registration_reject = FALSE;
               ui_set_sip_registration_state(ccb->dn_line, TRUE);
               CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Setting Reg state to TRUE for line=%d\n",
                              DEB_F_PREFIX_ARGS(SIP_REG_STATE, fname), ccb->dn_line);
              /* Get supported options from the supported header from the response */
               sip_get_supported_options_2xx(ccb, response);
           } else {
               log_clear(LOG_REG_BACKUP);
           }


    if (contact_info != NULL) {
        sippmh_free_contact(contact_info);
    }

    ccb->reg.tmr_expire = exp_time;

    ccb->reg.act_time = (int) time(NULL);
    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Starting expires timer (%d "
                          "sec)\n", DEB_L_C_F_PREFIX_ARGS(SIP_TIMER, ccb->index, ccb->dn_line, fname),
                          ccb->reg.tmr_expire);
    (void) sip_platform_register_expires_timer_start(ccb->reg.tmr_expire * 1000,
                                                     ccb->index);


    if (ccb->cc_type == CC_CCM) {
        /*
         * regmgr - A 200 OK will reach here only if the state is
         * REGISTERING, the unreg_200ok eve is handled
         * separately.
         */
        ti_config_table_t *ccm_table_ptr;

        ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;

        if ((ccm_table_ptr == CCM_Active_Standby_Table.active_ccm_entry) &&
                start_standby_monitor) {
            ccsipCCB_t     *standby_ccb;

            start_standby_monitor = FALSE;
            sip_platform_set_ccm_status();

            /*
             * Kickoff monitoring the standby
             */

            standby_ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
            if (CCM_Active_Standby_Table.standby_ccm_entry) {
                config_get_value(CFGID_TIMER_REGISTER_EXPIRES,
                                 &exp_time, sizeof(uint32_t));
                standby_ccb->reg.tmr_expire = exp_time;
                standby_ccb->reg.act_time = (int) time(NULL);
                sip_reg_sm_change_state(standby_ccb,
                                        SIP_REG_STATE_UNREGISTERING);
                (void) ccsip_register_send_msg(SIP_REG_CANCEL, standby_ccb->index);
                (void) sip_platform_register_expires_timer_start(standby_ccb->reg.tmr_expire * 1000,
                                                                 standby_ccb->index);
            }
        } else if (ccm_table_ptr ==
                   CCM_Active_Standby_Table.standby_ccm_entry) {
            /*
             * REGMGR - Update stats Got a 200OK for a expires 0
             * Keepalive msg to standby, update stats ?
             */
        }
    }


    config_get_value(CFGID_NAT_ENABLE, &nat_enable, sizeof(nat_enable));
    config_get_value(CFGID_NAT_RECEIVED_PROCESSING, &nat_rx_proc_enable,
                     sizeof(nat_rx_proc_enable));
    if (nat_enable == 1 && nat_rx_proc_enable == 1) {
        pViaHeaderStr = sippmh_get_cached_header_val(response, VIA);
        if (pViaHeaderStr != NULL) {
            via = sippmh_parse_via(pViaHeaderStr);
            if (via != NULL) {
                if (via->recd_host != NULL) {
                    cpr_ip_addr_t received_ip;

                    memset(&received_ip, 0, sizeof(cpr_ip_addr_t));
                    dns_err_code = dnsGetHostByName(via->recd_host,
                                                       &received_ip, 100, 1);
                    if (dns_err_code == 0) {
                        util_ntohl(&received_ip, &received_ip);
                    } else {
                        sip_config_get_nat_ipaddr(&received_ip);
                    }

                    if (util_compare_ip(&received_ip, &(ccb->src_addr)) == FALSE) {
                        /*
                         * The address the proxy/registration server received
                         * is not the same as the one that we think we have
                         * so we'll update our address and re-register with
                         * the proxy/registration server
                         */

                        ccb->reg.rereg_pending = 1;

                        // Make sure our running config has the correct
                        // IP address that we got from the proxy server
                        sip_config_set_nat_ipaddr(&received_ip);

                        if (ccsip_register_send_msg(SIP_REG_CANCEL, ccb->index)
                            != SIP_REG_OK) {
                            ccsip_register_cleanup(ccb, FALSE);
                        }

                        // Update the tel ccb that this reg line belongs to
                        ccb = sip_sm_get_ccb_by_index((line_t)(ccb->index - REG_CCB_START + 1));
                        if (ccb) {
                            ccb->src_addr = received_ip;
                        }
                    }
                }

                // Cleanup memory
                sippmh_free_via(via);
            }
        }
    }

    free_sip_message(response);
}

void
ccsip_handle_ev_3xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    static const char fname[] = "ccsip_handle_ev_3xx";
    sipMessage_t   *response = NULL;
    int             status_code = 0;
    char            status[LOG_MAX_LEN];
    char            user[MAX_LINE_NAME_SIZE];
    sipUrl_t       *url;
    const char     *contact = NULL;

    response = event->u.pSipMessage;
    clean_method_request_trx(ccb, sipMethodRegister, TRUE);

    if (sipGetResponseCode(response, &status_code) < 0) {
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE),
                          ccb->index, ccb->dn_line, fname);
        free_sip_message(response);
        return;
    }

    switch (status_code) {
    case SIP_RED_MULT_CHOICES /* 300 */:
    case SIP_RED_MOVED_PERM   /* 301 */:
    case SIP_RED_MOVED_TEMP   /* 302 */:
    case SIP_RED_USE_PROXY    /* 305 */:
        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY2),
                          ccb->index, ccb->dn_line, fname,
                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
                          "SIP", status_code);

        sip_sm_200and300_update(ccb, response, status_code);

        /*
         * Record the Contact header.  For 3xx messages we are recording
         * the Contact header here, not in the sipSPICheckResponse() header,
         * so that the ACK response to 3xx is sent to the original INVITE
         * destination (or to the Record-Route specified destination), rather
         * than to the new 302 Contact
         */
        contact = sippmh_get_cached_header_val(response, CONTACT);
        if (contact) {
            if (ccb->contact_info) {
                sippmh_free_contact(ccb->contact_info);
            }
            ccb->contact_info = sippmh_parse_contact(contact);
        }


        /*
         * Check the validity of Contact header
         */
        if (!ccb->contact_info) {
            CCSIP_DEBUG_ERROR("%s: Error: Invalid Contact field.  Aborting "
                              "REGISTER.\n", fname);
            free_sip_message(response);
            ccsip_register_cleanup(ccb, TRUE);
            return;
        }

        if (ccb->contact_info->locations[0]->genUrl->schema == URL_TYPE_SIP) {
            url = ccb->contact_info->locations[0]->genUrl->u.sipUrl;
        } else {
            CCSIP_DEBUG_ERROR("%s: Error: URL is not Sip.  Aborting "
                              "REGISTER.\n", fname);
            free_sip_message(response);
            ccsip_register_cleanup(ccb, TRUE);
            return;
        }

        sstrncpy(ccb->reg.proxy, url->host, MAX_IPADDR_STR_LEN);
        ccb->reg.port = url->port;

        if (ccb->contact_info) {
            sippmh_free_contact(ccb->contact_info);
            ccb->contact_info = NULL;
        }

        sip_util_get_new_call_id(ccb);

        /*
         * Send out a new REGISTER
         */
        ccb->authen.cred_type = 0;
        config_get_line_string(CFGID_LINE_NAME, user, ccb->dn_line,
                               sizeof(user));

        if (sipSPISendRegister(ccb, 0, user, ccb->reg.tmr_expire) != TRUE) {
            ccsip_register_cleanup(ccb, TRUE);
            free_sip_message(response);

            log_clear(LOG_REG_RED_MSG);
            log_msg(LOG_REG_RED_MSG);
        }
        return;

    default:
        free_sip_message(event->u.pSipMessage);

        snprintf(status, sizeof(status), "in %d, redirection", status_code);
        log_clear(LOG_REG_UNSUPPORTED);
        log_msg(LOG_REG_UNSUPPORTED, status);

        ccsip_register_cleanup(ccb, TRUE);
    }
}


void
ccsip_handle_ev_4xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    static const char fname[] = "ccsip_handle_ev_4xx";
    sipMessage_t   *response = NULL;
    int             status_code = 0;
    const char     *authenticate = NULL;
    char            user[MAX_LINE_NAME_SIZE];
    credentials_t   credentials;
    char            status[LOG_MAX_LEN];
    sip_authen_t   *sip_authen = NULL;
    char           *author_str = NULL;
    char            uri[MAX_IPADDR_STR_LEN + 10]; /* Proxy IP address string */
    char            tmp_str[STATUS_LINE_MAX_LEN];
    ti_config_table_t *ccm_table_ptr = NULL;

    ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;


    response = event->u.pSipMessage;
    clean_method_request_trx(ccb, sipMethodRegister, TRUE);

    if (sipGetResponseCode(response, &status_code) < 0) {
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE),
                          ccb->index, ccb->dn_line, fname);
        free_sip_message(response);
        return;
    }

    /*
     * Set reject flag if we get 4xx from publisher cucm.
     * Note: if non-prime dn is congfigured with #, such as 1234##,
     * then phone will get 404 for that DN, even though the prime DN is
     * registered.
     */
    if (ccm_table_ptr &&
        ccb->index != REG_BACKUP_CCB) {
    	DEF_DEBUG(DEB_F_PREFIX"Receive 4xx in ccm mode. set registration_reject to TRUE.\n",
            DEB_F_PREFIX_ARGS(SIP_REG, fname));
    	registration_reject = TRUE;
    }

    switch (status_code) {
    case SIP_CLI_ERR_UNAUTH:
    case SIP_CLI_ERR_PROXY_REQD:
        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY2),
                          ccb->index, ccb->dn_line, fname,
                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
                          "SIP", status_code);

        if (cred_get_credentials_r(ccb, &credentials) == FALSE) {
            CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"no more credentials, retry %d, %d\n",
                                  DEB_L_C_F_PREFIX_ARGS(SIP_CRED, ccb->index, ccb->dn_line, fname),
                                  ccb->authen.retries_401_407,
                                  MAX_RETRIES_401);


            if (ccb->cc_type == CC_CCM) {
                sip_regmgr_ev_failure_response(ccb, event);
                free_sip_message(response);
                return;
            }
            ccsip_register_cleanup(ccb, TRUE);
            free_sip_message(response);

            log_clear(LOG_REG_AUTH_NO_CRED);
            log_msg(LOG_REG_AUTH_NO_CRED);

            break;
        }

        authenticate = sippmh_get_header_val(response, AUTH_HDR(status_code),
                                             NULL);
        if (authenticate) {
            sip_authen = sippmh_parse_authenticate(authenticate);
        }

        if (sip_authen) {
            if (sip_authen->scheme == SIP_DIGEST) {
                if (authenticate != NULL) {
                    ccb->retx_counter = 0;
                    config_get_line_string(CFGID_LINE_NAME, user, ccb->dn_line,
                                           sizeof(user));
                    ccb->authen.cnonce[0] = '\0';
                    snprintf(uri, sizeof(uri), "sip:%s", ccb->reg.proxy); /* proxy */
                    if (sipSPIGenerateAuthorizationResponse(sip_authen, uri,
                                                            SIP_METHOD_REGISTER,
                                                            credentials.id,
                                                            credentials.pw,
                                                            &author_str,
                                                            &(ccb->authen.nc_count),
                                                            ccb)) {

                        if (author_str) {
                            if (ccb->authen.authorization != NULL) {
                                cpr_free(ccb->authen.authorization);
                                ccb->authen.authorization = NULL;
                            }

                            ccb->authen.authorization =
                                (char *) cpr_malloc(strlen(author_str) *
                                                    sizeof(char) + 1);

                            if (ccb->authen.authorization != NULL) {
                                sstrncpy(ccb->authen.authorization, author_str,
                                         strlen(author_str) * sizeof(char) + 1);
                                ccb->authen.status_code = status_code;
                            }
                            cpr_free(author_str);
                        }
                    } else {
                        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
                                          ccb->index, ccb->dn_line, fname,
                                          "sipSPIGenerateAuthorizationResponse");
                    }

                    if (!sipSPISendRegister(ccb, TRUE, user,
                                            ccb->reg.tmr_expire)){
                          ccsip_register_cleanup(ccb, TRUE);

                          log_clear(LOG_REG_AUTH_MSG);
                          log_msg(LOG_REG_AUTH_MSG);
                    }

                } else {
                    CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
                                      ccb->index, ccb->dn_line, fname,
                                      "sippmh_parse_authenticate");

                    ccsip_register_cleanup(ccb, TRUE);
                    log_clear(LOG_REG_AUTH_HDR_MSG);
                    log_msg(LOG_REG_AUTH_HDR_MSG);
                }
            } else {
                CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"auth scheme unsupported= %d\n",
                                      DEB_F_PREFIX_ARGS(SIP_AUTH, fname), sip_authen->scheme);

                ccsip_register_cleanup(ccb, TRUE);
                log_clear(LOG_REG_AUTH_SCH_MSG);
                log_msg(LOG_REG_AUTH_SCH_MSG);
            }
            sippmh_free_authen(sip_authen);
        }

        free_sip_message(response);
        return;

    case SIP_CLI_ERR_INTERVAL_TOO_SMALL:
        {
            int         new_expires = MAX_REG_EXPIRES;
            const char *msg_ptr = NULL;
            char        line_name[MAX_LINE_NAME_SIZE];

            msg_ptr = sippmh_get_header_val(response,
                                            (const char *)SIP_HEADER_MIN_EXPIRES,
                                            NULL);
            if (msg_ptr) {
                new_expires = strtoul(msg_ptr, NULL, 10);
            }

            //ensure new Min-Expires is > what we set before in Expires
            if (new_expires > ccb->reg.tmr_expire) {
                ccb->reg.tmr_expire = new_expires;
            } else {
                ccb->reg.tmr_expire = MAX_REG_EXPIRES;
            }

            //We have to make a fresh request
            sip_stop_ack_timer(ccb);
            sip_start_ack_timer(ccb);

            (void) sip_platform_register_expires_timer_stop(ccb->index);

            sip_util_get_new_call_id(ccb);

            ccb->authen.cred_type = 0;
            ccb->retx_counter = 0;
            config_get_line_string(CFGID_LINE_NAME, line_name, ccb->dn_line,
                                   sizeof(line_name));

            ccb->reg.act_time = (int) time(NULL);

                 if (sipSPISendRegister(ccb, 0, line_name, ccb->reg.tmr_expire) != TRUE) {
                     // set expire timer and cleanup ccb in non-ccm mode.
                     // do not change the expire timer value in ccm mode

                     if (ccb->cc_type != CC_CCM) {
                         ccsip_register_cleanup(ccb, TRUE);
                     }

                     log_clear(LOG_REG_MSG);
                     log_msg(LOG_REG_MSG);
                 }

            //no change in state
        }
        free_sip_message(response);
        return;


        /*
         * 404 (Not Found)
         * 413 (Request Entity Too Large)
         * 480 (Temporarily Unavailable)
         * 486 (Busy here)
         */
    case SIP_CLI_ERR_NOT_FOUND:
    case SIP_CLI_ERR_LARGE_MSG:
    case SIP_CLI_ERR_NOT_AVAIL:
    case SIP_CLI_ERR_BUSY_HERE:
        if (ccb->cc_type == CC_CCM) {
            if (ccb->state == (int) SIP_REG_STATE_TOKEN_WAIT) {
                clean_method_request_trx(ccb, sipMethodRefer, TRUE);
                sip_regmgr_ev_token_wait_4xx_n_5xx(ccb, event);
            } else {
                /*
                 * Phone sends a REGISTER message with expires=0 as a
                 * Keepalive msg. Proxy and CCM will respond with a
                 * 200 OK.
                 */
                if (ccb->index != REG_BACKUP_CCB) {
                    if (ccsip_register_get_register_state() != SIP_REG_UNREGISTERING) {
                        if (((ti_config_table_t *)(ccb->cc_cfg_table_entry)) ||
                            ccb->dn_line == PRIMARY_LINE) {
                            if (platGetPhraseText(STR_INDEX_REGISTRATION_REJECTED,
                                 (char *)tmp_str, STATUS_LINE_MAX_LEN - 1)== CPR_SUCCESS) {
                                ui_set_notification(CC_NO_LINE, CC_NO_CALL_ID, tmp_str, 15, FALSE,
                                                DEF_NOTIFY_PRI);
                            }
                        }
                        log_clear(STR_INDEX_REGISTRATION_REJECTED);
                        log_msg(STR_INDEX_REGISTRATION_REJECTED);
                    }
                }
                if (process_retry_after(ccb, response) == FALSE) {
                    sip_regmgr_ev_failure_response(ccb, event);
                }
            }
        } else {
            if (process_retry_after(ccb, response) == FALSE) {
                ccsip_register_cleanup(ccb, TRUE);
            }
        }
        free_sip_message(response);
        return;

    default:
        if (ccb->cc_type == CC_CCM) {
            if (ccb->state == (int) SIP_REG_STATE_TOKEN_WAIT) {
                clean_method_request_trx(ccb, sipMethodRefer, TRUE);
                sip_regmgr_ev_token_wait_4xx_n_5xx(ccb, event);
            } else {
                sip_regmgr_ev_failure_response(ccb, event);
            }
            free_sip_message(response);
            return;
        }
        CCSIP_DEBUG_STATE(get_debug_string(DEBUG_FUNCTION_ENTRY2),
                          ccb->index, ccb->dn_line, fname,
                          sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
                          "SIP BAD", status_code);

        snprintf(status, sizeof(status), "in %d, client error", status_code);
        ccsip_register_cleanup(ccb, TRUE);

        snprintf(status, sizeof(status), "in %d, request failure", status_code);
        log_clear(LOG_REG_UNSUPPORTED);
        log_msg(LOG_REG_UNSUPPORTED, status);
        free_sip_message(response);
        break;
    }

    if (ccb->reg.rereg_pending != 0) {
        ccb->reg.rereg_pending = 0;
        if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index) != SIP_REG_OK) {
            ccsip_register_cleanup(ccb, TRUE);
        }
    }
}


void
ccsip_handle_ev_failure_response (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    static const char fname[] = "ccsip_handle_ev_failure_response";
    sipMessage_t   *response = NULL;
    int             status_code = 0;
    char            status[LOG_MAX_LEN];
    sipStatusCodeClass_t code_class = codeClassInvalid;
    ti_config_table_t *ccm_table_ptr = NULL;

    /*
     * Set reject flag if we get 5xx/6xx from publisher cucm.
     */
    ccm_table_ptr = (ti_config_table_t *) ccb->cc_cfg_table_entry;
    if (ccm_table_ptr &&
        ccb->index != REG_BACKUP_CCB) {
        registration_reject = TRUE;
        DEF_DEBUG(DEB_F_PREFIX"registration has been rejected. Set registration_reject to TRUE.\n",
            DEB_F_PREFIX_ARGS(SIP_REG, fname));
    }

    response = event->u.pSipMessage;
    clean_method_request_trx(ccb, sipMethodRegister, TRUE);

    if (sipGetResponseCode(response, &status_code) < 0) {
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_CODE),
                          ccb->index, ccb->dn_line, fname);
        free_sip_message(response);
        ccsip_register_cleanup(ccb, TRUE);
        return;
    }

    code_class = sippmh_get_code_class((uint16_t)status_code);

    log_clear(LOG_REG_AUTH);

    switch (code_class) {
    case codeClass5xx:
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_FAILURE),
                          ccb->index, ccb->dn_line, fname, status_code);

        log_msg(LOG_REG_AUTH_SERVER_ERR, status_code);

        break;

    case codeClass6xx:
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_FAILURE),
                          ccb->index, ccb->dn_line, fname, status_code);

        log_msg(LOG_REG_AUTH_GLOBAL_ERR, status_code);

        break;

    default:
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_REG_SIP_RESP_FAILURE),
                          ccb->index, ccb->dn_line, fname, status_code);

        snprintf(status, sizeof(status), "in %d, ??? error", status_code);
        log_msg(LOG_REG_AUTH_UNKN_ERR, status_code);

        break;
    }

    if (ccb->cc_type == CC_CCM) {
        if (ccb->state == (int) SIP_REG_STATE_TOKEN_WAIT) {

            /* Handle only 503 error condition for other errors
             * use default state
             */
            if (status_code == SIP_SERV_ERR_UNAVAIL) {
                clean_method_request_trx(ccb, sipMethodRefer, TRUE);
                sip_regmgr_ev_token_wait_4xx_n_5xx(ccb, event);
                free_sip_message(response);
            } else {
                sip_regmgr_ev_default(ccb, event);
            }
            return;
        }

        /* retry registration using value in the Retry-After header
         * if response code is 503 with Retry-After
         */
        if ((status_code == SIP_SERV_ERR_UNAVAIL) &&
            (process_retry_after(ccb, response) == TRUE)) {
            free_sip_message(response);
            return;
        }
        /*
         * regmgr - Pass the event to the regmgr
         */
        sip_regmgr_ev_failure_response(ccb, event);
        free_sip_message(response);
    } else {
        /*
         * Assume CSPS for now
         */

        switch (status_code) {

            /*
             * 500 (Server Internal Error)
             * 503 (Service Unavailable)
             * 600 (Busy),
             * 603 (Declined
             */
        case SIP_SERV_ERR_INTERNAL:
        case SIP_SERV_ERR_UNAVAIL:

        case SIP_FAIL_BUSY:
        case SIP_FAIL_DECLINE:
            if (process_retry_after(ccb, response) == FALSE) {
                ccsip_register_cleanup(ccb, TRUE);
            }
            free_sip_message(response);
            return;

        default:
            break;
        }
        ccsip_register_cleanup(ccb, TRUE);
        free_sip_message(response);

        if (ccb->reg.rereg_pending != 0) {
            ccb->reg.rereg_pending = 0;
            if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index)
                    != SIP_REG_OK) {
                ccsip_register_cleanup(ccb, TRUE);
            }
        }
    }
}

/*
 * ccsip_handle_ev_tmr_retry()
 *
 * This function is called when registration re-try timer
 * pops or an ICMP unreachable is received after sending
 * out the registration. The ICMP unreachable code sets the
 * retx_counter to MAX to force the code to try the next
 * proxy in the list. Event.u.usrInfo is set to the IP
 * address that bounced or -1 if is a timeout.
 */
void
ccsip_handle_ev_tmr_retry (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    static const char fname[] = "ccsip_handle_ev_tmr_retry";
    boolean         start_timer;
    uint32_t        value;
    int             dns_err_code = DNS_ERR_HOST_UNAVAIL;
    cpr_ip_addr_t   ip_addr;

    CPR_IP_ADDR_INIT(ip_addr);

    start_timer = (ccsip_register_get_register_state() == SIP_REG_UNREGISTERING) ?
        (FALSE) : (TRUE);

    config_get_value(CFGID_SIP_RETX, &value, sizeof(value));
    if (value > MAX_NON_INVITE_RETRY_ATTEMPTS) {
        value = MAX_NON_INVITE_RETRY_ATTEMPTS;
    }
    if (ccb->retx_counter >= value) {
        if (ccb->cc_type == CC_CCM /* RAMC Some other state */) {
            /*
             * regmgr - Send event to the regmgr
             */
            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Reached here for ccb->index=%d \n",
                    DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname), ccb->index);
            sip_regmgr_ev_tmr_ack_retry(ccb, event);
            return;
        } else {
            /*
             * Assume CSPS for now
             */
            ccb->retx_counter = 0;

            /*
             * Did the registration msg bounce trying to reach the outbound
             * proxy (1st check) or did it timeout trying to reach the
             * outbound proxy (2nd check)?
             */
            util_ntohl(&ip_addr, &(event->u.UsrInfo));
            if (util_compare_ip(&ip_addr, &(ccb->outBoundProxyAddr)) ||
                ((event->u.UsrInfo.type == CPR_IP_ADDR_INVALID) &&
                util_check_if_ip_valid(&(ccb->outBoundProxyAddr)))) {
                /*
                 * If there are more records, reset address to force code
                 * to use next entry. If not then bail.
                 */
                ccb->outBoundProxyPort = 0;
                dns_err_code = DNS_OK;

                /*
                 * Trouble reaching the normal proxy. Get another proxy
                 * if records are available
                 */
            } else if (str2ip(ccb->reg.proxy, &ccb->reg.addr) != 0) {
                dns_err_code = sipTransportGetServerAddrPort(ccb->reg.proxy,
                                                             &ccb->reg.addr,
                                                             (uint16_t *) &ccb->reg.port,
                                                             &ccb->SRVhandle,
                                                             TRUE);
                if (dns_err_code == 0) {
                    util_ntohl(&(ccb->reg.addr), &(ccb->reg.addr));
                } else {
                    ccb->reg.addr = ip_addr_invalid;
                }

                /*
                 * Modify destination fields in call back timer struct
                 */
                (void) sip_platform_msg_timer_update_destination(ccb->index,
                                                                 &(ccb->reg.addr),
                                                                 ccb->reg.port);
            } else {
                /* No other proxy to try */
                dns_err_code = DNS_ERR_HOST_UNAVAIL;
            }

            /*
             * Cleanup if unable to find another proxy
             */
            if (dns_err_code != DNS_OK) {
                /*
                 * All retransmit attempts have been exhausted.
                 * Cleanup the call and move to the IDLE state
                 */
                ccsip_register_cleanup(ccb, start_timer);

                log_clear(LOG_REG_RETRY);
                log_msg(LOG_REG_RETRY);
                if ((ccb->index == REG_BACKUP_CCB) &&
                    (ccb->cc_type != CC_CCM)) {
                    log_clear(LOG_REG_BACKUP);
                    log_msg(LOG_REG_BACKUP);
                }
                if (ccb->reg.rereg_pending != 0) {
                    ccb->reg.rereg_pending = 0;
                    if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index)
                        != SIP_REG_OK) {
                        ccsip_register_cleanup(ccb, TRUE);
                    }
                }

                return;
            }
        }
    }

    if (ccb->state != (int) SIP_REG_STATE_REGISTERED) {
        /*
         * Handling Retry timer in REGISTERED state is not needed
         * the event has come due to race condition
         */
        ccb->retx_counter++;
        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"Resending message: #%d\n",
                              DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname), ccb->retx_counter);

        if (sipSPISendLastMessage(ccb) != TRUE) {
            if (ccb->cc_type == CC_CCM) {
                sip_regmgr_ev_tmr_ack_retry(ccb, event);
            } else {
                ccsip_register_cleanup(ccb, start_timer);
            }
            return;
        }

        ccsip_register_retry_timer_start(ccb);
    }

}


void
ccsip_handle_ev_reg_cancel (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    char user[MAX_LINE_NAME_SIZE];
    int  exp_time = 0;

    ccsip_register_clear_all_logs();

    sip_stop_ack_timer(ccb);
    sip_start_ack_timer(ccb);

    (void) sip_platform_register_expires_timer_stop(ccb->index);

    sip_util_get_new_call_id(ccb);

    ccb->authen.cred_type = 0;
    ccb->retx_counter     = 0;
    ccb->reg.tmr_expire   = 0;
    ccb->reg.act_time     = 0;
    config_get_line_string(CFGID_LINE_NAME, user, ccb->dn_line, sizeof(user));

    if (sipSPISendRegister(ccb, 0, user, exp_time) != TRUE) {
        log_clear(LOG_REG_CANCEL_MSG);
        log_msg(LOG_REG_CANCEL_MSG);
    } else if ((ccb->index == REG_BACKUP_CCB) && (ccb->cc_type != CC_CCM)) {
        log_clear(LOG_REG_BACKUP);
        log_msg(LOG_REG_BACKUP);
    }

    sip_reg_sm_change_state(ccb, SIP_REG_STATE_UNREGISTERING);
}


void
ccsip_handle_ev_unreg_2xx (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    static const char fname[] = "ccsip_handle_ev_unreg_2xx";
    sipMessage_t *response = event->u.pSipMessage;
    int           timeout;

    free_sip_message(response);

    ccb->reg.registered = 0;
    ccb->reg.tmr_expire = 0;
    ccb->reg.act_time = 0;
    clean_method_request_trx(ccb, sipMethodRegister, TRUE);

    if (ccb->cc_type == CC_CCM) {
        if (ccb->index == REG_BACKUP_CCB) {
            /*
             * Stay in unregistering state and wait for the
             * expires timer to pop to send the next keepalive.
             */
            sip_stop_ack_timer(ccb);
            if (new_standby_available) {
                sip_regmgr_replace_standby(ccb);
            }

            timeout = sip_config_get_keepalive_expires();

            CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Keep alive timer (%d sec)\n",
                    DEB_L_C_F_PREFIX_ARGS(SIP_TIMER, ccb->index, ccb->dn_line, fname), timeout);
            (void) sip_platform_standby_keepalive_timer_start(timeout * 1000);
        } else {
            sip_reg_sm_change_state(ccb, SIP_REG_STATE_IDLE);

            sip_stop_ack_timer(ccb);

            if (ccsip_register_all_unregistered() == TRUE) {
                ccsip_register_set_register_state(SIP_REG_IDLE);
                /*
                 * regmgr - FALLBACK: Pick the ccb of the ccm that has
                 * come up (falling back) and post the fallback event,
                 * now that the unregistering is done.
                 * Will commit change after integration with ccm.
                 */
            }
            platSetSISProtocolVer(1,0,0,NULL);
            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"set[1] the SIS protocol ver to 1.0.0\n",
                                  DEB_F_PREFIX_ARGS(SIP_REG, fname));
        }
    } else {
        sip_reg_sm_change_state(ccb, SIP_REG_STATE_IDLE);

        sip_stop_ack_timer(ccb);

        if (ccsip_register_all_unregistered() == TRUE) {
            ccsip_register_set_register_state(SIP_REG_IDLE);
        }

        /*
         * Our unregistration from the proxy has succeeded now we
         * can reregister again
         */
        if (ccb->reg.rereg_pending != 0) {
            ccb->reg.rereg_pending = 0;
            if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index)
                    != SIP_REG_OK) {
                ccsip_register_cleanup(ccb, TRUE);
            }
        }
        platSetSISProtocolVer(1,0,0,NULL);
        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"set[2] the SIS protocol ver to 1.0.0\n",
                              DEB_F_PREFIX_ARGS(SIP_REG, fname));
    }
}


void
ccsip_handle_ev_unreg_tmr_ack (ccsipCCB_t *ccb, sipSMEvent_t *event)
{
    log_clear(LOG_REG_AUTH_UNREG_TMR);
    log_msg(LOG_REG_AUTH_UNREG_TMR);

    if (ccb->cc_type == CC_CCM) {
        /*
         * regmgr - Send tmr ack event to the regmgr
         */
        sip_regmgr_ev_tmr_ack_retry(ccb, event);
        return;
    }
    ccsip_register_cleanup(ccb, FALSE);
    /*
     * We are unregistering ourselves from the proxy when our own registration
     * timer has expired.  This means that we do not need to complete our
     * unregistration from the proxy since it is being done automatically.
     * We can just send our new registration now
     */
    if (ccb->reg.rereg_pending != 0) {
        ccb->reg.rereg_pending = 0;
        if (ccsip_register_send_msg(SIP_REG_REQ, ccb->index) != SIP_REG_OK) {
            ccsip_register_cleanup(ccb, TRUE);
        }
    }
}

void
ccsip_handle_ev_standby_keepalive_tmr_expire (ccsipCCB_t *ccb,
                                              sipSMEvent_t *event)
{
    /*
     * regmgr - If there is a fallback ccb in TOKEN_WAIT
     * state waiting to fallback notify it so it can
     * continue with the fallback procedure.
     */
    if (ccb->cc_type == CC_CCM) {
        /*
         * REGMGR - CHECK if this event gets called.
         * Will get in here only for REG_BACKUP_CCB in
         * unregistering state i.e, the standby cc which
         * is periodically monitored.
         */
        /*
         * Stay in unregistering state and post a message
         * to send another keep-alive message.
         */
        (void) sip_platform_standby_keepalive_timer_stop();
        (void) ccsip_register_send_msg(SIP_REG_CANCEL, ccb->index);
    } else {
        ccsip_handle_ev_default(ccb, event);
    }
}

int
sip_reg_sm_process_event (sipSMEvent_t *pEvent)
{
    static const char fname[] = "sip_reg_sm_process_event";
    ccsipCCB_t *ccb = NULL;

    ccb = pEvent->ccb;
    if (!ccb) {
        CCSIP_DEBUG_ERROR("%s: Error: ccb is null. Unable to process event "
                          "<%d>\n", fname, pEvent->type);
        return (-1);
    }

    /* Unbind UDP ICMP response handler */
    if ((REG_CHECK_EVENT_SANITY((int) ccb->state, (int) pEvent->type)) &&
        (REG_EVENT_ACTION(ccb->state, (int) pEvent->type))) {
        if (dump_reg_msg == TRUE) {
            DEF_DEBUG(DEB_L_C_F_PREFIX"%s <- %s\n",
                    DEB_L_C_F_PREFIX_ARGS(SIP_REG_STATE, ccb->dn_line, ccb->index, fname),
                    sip_util_reg_state2string((sipRegSMStateType_t)ccb->state),
                    sip_util_reg_event2string((sipRegSMEventType_t)pEvent->type));
        }
        REG_EVENT_ACTION(ccb->state, pEvent->type) (ccb, pEvent);
    } else {
        /* Invalid State/Event pair */
        CCSIP_DEBUG_ERROR("%s: Error: illegal state/event pair: (%d <-- %d)\n",
                          fname, ccb->state, pEvent->type);
        return (-1);
    }

    return (0);
}


const char *
sip_util_reg_event2string (sipRegSMEventType_t event)
{
    switch (event) {
    case E_SIP_REG_REG_REQ:
        return ("E_SIP_REG_REG_REQ");

    case E_SIP_REG_CANCEL:
        return ("E_SIP_REG_CANCEL");

    case E_SIP_REG_1xx:
        return ("E_SIP_REG_1xx");

    case E_SIP_REG_2xx:
        return ("E_SIP_REG_2xx");

    case E_SIP_REG_3xx:
        return ("E_SIP_REG_3xx");

    case E_SIP_REG_4xx:
        return ("E_SIP_REG_4xx");

    case E_SIP_REG_FAILURE_RESPONSE:
        return ("E_SIP_REG_FAILURE_RESPONSE");

    case E_SIP_REG_TMR_EXPIRE:
        return ("E_SIP_REG_TMR_EXPIRE");

    case E_SIP_REG_TMR_ACK:
        return ("E_SIP_REG_TMR_ACK");

    case E_SIP_REG_TMR_WAIT:
        return ("E_SIP_REG_TMR_WAIT");

    case E_SIP_REG_TMR_RETRY:
        return ("E_SIP_REG_TMR_RETRY");

    case E_SIP_REG_CLEANUP:
        return ("E_SIP_REG_CLEANUP");

    default:
        return ("SIP_REG_STATE_UNKNOWN");

    }
}


char *
sip_util_reg_state2string (sipRegSMStateType_t state)
{
    switch (state) {
    case SIP_REG_STATE_NONE:
        return ("SIP_REG_STATE_NONE");

    case SIP_REG_STATE_IDLE:
        return ("SIP_REG_STATE_IDLE");

    case SIP_REG_STATE_REGISTERING:
        return ("SIP_REG_STATE_REGISTERING");

    case SIP_REG_STATE_REGISTERED:
        return ("SIP_REG_STATE_REGISTERED");

    case SIP_REG_STATE_UNREGISTERING:
        return ("SIP_REG_STATE_UNREGISTERING");

    case SIP_REG_STATE_IN_FALLBACK:
        return ("SIP_REG_STATE_IN_FALLBACK");

    case SIP_REG_STATE_STABILITY_CHECK:
        return ("SIP_REG_STATE_STABILITY_CHECK");

    case SIP_REG_STATE_TOKEN_WAIT:
        return ("SIP_REG_STATE_TOKEN_WAIT");

    default:
        return ("SIP_REG_STATE_UNKNOWN");

    }
}


sipRegSMEventType_t
ccsip_register_sip2sipreg_event (int sip_event)
{
    static const char fname[] = "ccsip_register_sip2sipreg";
    sipRegSMEventType_t reg_event = E_SIP_REG_NONE;

    switch (sip_event) {
    case E_SIP_1xx:
        reg_event = E_SIP_REG_1xx;
        break;

    case E_SIP_2xx:
        reg_event = E_SIP_REG_2xx;
        break;

    case E_SIP_3xx:
        reg_event = E_SIP_REG_3xx;
        break;

    case E_SIP_FAILURE_RESPONSE:
        reg_event = E_SIP_REG_4xx;
        break;

    default:
        CCSIP_DEBUG_ERROR("%s: Error: Unknown event.\n", fname);
        reg_event = E_SIP_REG_NONE;
        break;
    }
    return (reg_event);
}


void
ccsip_register_timeout_retry (void *data)
{
    static const char fname[] = "ccsip_register_timeout_retry";
    ccsipCCB_t *ccb;

    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));

    ccb = (ccsipCCB_t *) data;
    if (ccb == NULL) {
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_GENERAL_FUNCTIONCALL_FAILED),
                          fname, "ccsip_register_timeout_retry");
        return;
    }

    if (ccsip_register_send_msg(SIP_TMR_REG_RETRY, ccb->index) != SIP_REG_OK) {
        ccsip_register_cleanup(ccb, TRUE);
    }
}


/*
 * timer callback function
 *
 * The function will search for the line that set the timer and then
 * (1) send expire event to sm or
 * (2) set another 60s timer. The setting of another timer is necessary
 *     because the max ticks for an OS timer is 10m. The expire timer can
 *     be larger. Therefore, set x 1m timers and count down.
 */
int
ccsip_register_send_msg (uint32_t cmd, line_t ndx)
{
    static const char fname[] = "ccsip_register_send_msg";
    ccsip_registration_msg_t *register_msg;
    ccsipCCB_t *ccb = NULL;
    CCM_ID ccm_id = UNUSED_PARAM;
    ti_config_table_t * cc_cfg_table;

    /*
     * Get the ccm_id type from ccb corresponding to ndx.
     * Currently, only SIP_TMR_REG_RETRY msg in SIPTaskProcessListEvent is
     * making use of ccm_id field. No other reg msg make use of ccm_id. Also in
     * case of SIP_REG_UPDATE, ccm_id will not be determined below but remain
     * initialized to UNUSED_PARAM because, in case of SIP_REG_UPDATE, ndx is
     * not ccb->index but number of available lines when lkem is attached.
     */
    if (cmd != SIP_REG_UPDATE) {
        ccb = sip_sm_get_ccb_by_index(ndx);
        if (ccb != NULL) {
            cc_cfg_table =  (ti_config_table_t *)(ccb->cc_cfg_table_entry);
            if (cc_cfg_table != NULL) {
                ccm_id = cc_cfg_table->ti_specific.ti_ccm.ccm_id;
            }
            else {
                CCSIP_DEBUG_ERROR("%s: Error: cc_cfg_table is null.\n", fname);
                return SIP_ERROR;
            }
        }
        else {
            CCSIP_DEBUG_ERROR("%s: Error: ccb is null.\n", fname);
            return SIP_ERROR;
        }
    }

    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"cmd=%d=%s ccb->index=%d ccm_id=%s\n",
        DEB_F_PREFIX_ARGS(SIP_MSG_SEND, fname),
        cmd, REG_CMD_PRINT(cmd), ndx, CCM_ID_PRINT(ccm_id));

    register_msg = (ccsip_registration_msg_t *)
        SIPTaskGetBuffer(sizeof(ccsip_registration_msg_t));

    if (!register_msg) {
        CCSIP_DEBUG_ERROR("%s: Error: get buffer failed.\n", fname);
        return SIP_ERROR;
    }
    register_msg->ccb_index = ndx;
    register_msg->ccm_id = ccm_id;

    if (SIPTaskSendMsg(cmd, register_msg, sizeof(ccsip_registration_msg_t), NULL)
            == CPR_FAILURE) {
        cpr_free(register_msg);
        CCSIP_DEBUG_ERROR("%s: Error: send buffer failed.\n", fname);
        return SIP_ERROR;
    }

    return SIP_REG_OK;
}

void
ccsip_register_retry_timer_start (ccsipCCB_t *ccb)
{
    static const char fname[] = "ccsip_register_retry_timer_start";
    int timeout;
    int time_t1;
    int time_t2;

    /* Double the timeout value and do exponential time increases
     * The doubling is used because the invite timeout is too short
     */
    config_get_value(CFGID_TIMER_T1, &time_t1, sizeof(time_t1));
    timeout = time_t1 * (1 << ccb->retx_counter);
    config_get_value(CFGID_TIMER_T2, &time_t2, sizeof(time_t2));
    if (timeout > time_t2) {
        timeout = time_t2;
    }
    CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Starting reTx timer (%d msec)\n",
                          DEB_L_C_F_PREFIX_ARGS(SIP_TIMER, ccb->index, ccb->dn_line, fname), timeout);

    ccb->retx_flag = TRUE;
    if (sip_platform_msg_timer_start(timeout, (void *)ccb, ccb->index,
                                     sipPlatformUISMTimers[ccb->index].message_buffer,
                                     sipPlatformUISMTimers[ccb->index].message_buffer_len,
                                     sipMethodRegister,
                                     &(sipPlatformUISMTimers[ccb->index].ipaddr),
                                     sipPlatformUISMTimers[ccb->index].port,
                                     TRUE) != SIP_OK) {
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_FUNCTIONCALL_FAILED),
                          ccb->index, ccb->dn_line, fname,
                          "sip_platform_msg_timer_start");
        ccb->retx_flag = FALSE;
    }
}


void
ccsip_register_cleanup (ccsipCCB_t *ccb, boolean start)
{
    static const char fname[] = "ccsip_register_cleanup";
    int exp_time;

    config_get_value(CFGID_TIMER_REGISTER_EXPIRES, &exp_time, sizeof(exp_time));

    ccb->reg.registered = 0;

    if (ccb->index != REG_BACKUP_CCB) {
        ui_set_sip_registration_state(ccb->dn_line, FALSE);
    }

    sip_stop_ack_timer(ccb);

    if ((start) && (ccb->state != (int) SIP_REG_STATE_UNREGISTERING)) {
        if (ccb->index == REG_BACKUP_CCB) {
            ccb->reg.tmr_expire = (exp_time > 5) ? exp_time - 5 : exp_time;
        } else {
            ccb->reg.tmr_expire = 60;
        }
        ccb->reg.act_time = (int) time(NULL);

        CCSIP_DEBUG_STATE(DEB_L_C_F_PREFIX"Starting expires timer (%d "
                          "sec)\n", DEB_L_C_F_PREFIX_ARGS(SIP_TIMER, ccb->index, ccb->dn_line, fname),
                          ccb->reg.tmr_expire);
        (void) sip_platform_register_expires_timer_start(ccb->reg.tmr_expire * 1000,
                                                         ccb->index);
    }

    sip_reg_sm_change_state(ccb, SIP_REG_STATE_IDLE);

    if (ccsip_register_all_unregistered() == TRUE) {
        ccsip_register_set_register_state(SIP_REG_IDLE);
    }

    /*
     * Always check how sip_sm_call_cleanup is implemented.
     * The function has a tendency to change.
     */

    /* Cleanup and re-initialize CCB */
    sip_sm_call_cleanup(ccb);
}


cc_int32_t
ccsip_register_cmd (cc_int32_t argc, const char *argv[])
{
    /*
     * On 7970 phones the command is reg line [option] [line]
     * On 7940/60 the command is reg [option] [line]
     * Thus the location in the argument array is different
     * based on the phone type. OFFSET is used to get around
     * this difference while still having common code.
     */
#define OFFSET 1
    ccsipCCB_t *ccb = NULL;
    line_t ndx = 0;
    line_t temp_line = 0;
    char str_val[MAX_LINE_NAME_SIZE];
    const char *line_string;
    char *strtol_end;

    /*
     * check if need help
     */
    if ((argc == 2 + OFFSET) && (argv[1 + OFFSET][0] == '?')) {
        debugif_printf("reg [option] [line]\n");
        debugif_printf("    options = 0: unregister\n");
        debugif_printf("              1: register\n");
        debugif_printf("    line    = 1 through 6\n");
        debugif_printf("            = backup (line 1 to backup proxy)\n");
        debugif_printf("\n");
        return (0);
    }else if (cpr_strcasecmp(argv[0 + OFFSET], "line") == 0) {

       /*
        make sure the user entered a complete command
       */
        if (argc < 3 + OFFSET) {
            debugif_printf("Incomplete command\n");
            debugif_printf("reg [option] [line]\n");
            debugif_printf("    options = 0: unregister\n");
            debugif_printf("              1: register\n");
            debugif_printf("    line    = 1 through 6\n");
            debugif_printf("            = backup (line 1 to backup proxy)\n");
            debugif_printf("\n");
            return (0);
        }

        /* make sure we have at least one line configured */
        config_get_line_string(CFGID_LINE_NAME, str_val, 1, sizeof(str_val));
        if ((strcmp(str_val, UNPROVISIONED) == 0) || (str_val[0] == '\0')) {
            debugif_printf("Error:No lines configured\n");
            return (0);
        }

        line_string = argv[2 + OFFSET];
        if (cpr_strcasecmp(line_string, "backup") == 0) {
            temp_line = REG_BACKUP_LINE;
        } else {
            errno = 0;
            temp_line = (line_t) strtol(line_string, &strtol_end, 10);
            if (errno || line_string == strtol_end || !sip_config_check_line(temp_line)) {
                debugif_printf("Error:Invalid Line\n");
                return (0);
            }
        }


            ndx = SIP_REG_REGLINE2LINE(temp_line - 1);
            ccb = sip_sm_get_ccb_by_index(ndx);

        if (ccb == NULL) {
            debugif_printf("Unable to retrieve registration information for this line.\n");
            debugif_printf("Command aborted.\n");
            return (0);
        }

        if ((temp_line == REG_BACKUP_LINE) && util_check_if_ip_valid(&(ccb->dest_sip_addr)) == FALSE) {
            debugif_printf("Backup Proxy is invalid or not configured.\n");
            return (0);
        }

        switch (argv[1 + OFFSET][0]) {
        case '0':

            if (ccb->index != REG_BACKUP_CCB) {
                ui_set_sip_registration_state(ccb->dn_line, FALSE);
            }

            if (temp_line != REG_BACKUP_LINE) {
                debugif_printf("Unregistering line %d\n", temp_line);
            } else {
                debugif_printf("Unregistering line 1 to backup proxy\n");
            }
            (void) sip_platform_register_expires_timer_stop(ccb->index);
            sip_stop_ack_timer(ccb);

            if (ccsip_register_send_msg(SIP_REG_CANCEL, ndx) != SIP_REG_OK) {
                ccsip_register_cleanup(ccb, FALSE);
            }
            break;

        case '1':

            /* Cleanup and re-initialize CCB */
            sip_sm_call_cleanup(ccb);

            if (temp_line != REG_BACKUP_LINE) {
                debugif_printf("Registering line %d\n", temp_line);
            } else {
                debugif_printf("Registering line 1 to backup proxy\n");
            }
            (void) ccsip_register_send_msg(SIP_REG_REQ, ndx);
            break;

        default:
            debugif_printf("Invalid Option Value - please choose 1 or 0\n");
            return (0);
        }
    }
    return (0);
}

/*
 *  Function: build_reg_flags
 *
 *  Parameters:
 *
 *  Description: builds a 3 character string representing various flags
 *               for use by the show_reg command
 *
 *  Returns: none
 *
 */
static void
build_reg_flags (char *buf, int buf_size, int prox_reg, ccsipCCB_t *ccb,
                 boolean provisioned)
{
    sstrncpy(buf, "...", buf_size);
    if (!provisioned || !prox_reg) {
        return;
    }

    buf[1] = '1'; // Set the Provisioned bit

    if (ccb->authen.authorization != NULL) {
        buf[0] = '1';
    } else {
        if (ccb->authen.status_code != 0) {
            buf[0] = 'x';
        }
    }
    if (ccb->reg.registered) {
        buf[2] = '1';
    } else {
        buf[2] = 'x';
    }
}

/*
 *  Function: show_register_data
 *
 *  Parameters:
 *
 *  Description:  Internal print routine for show_register_cmd. Also
 *                called by reg command
 *
 *  Returns:
 *
 */
#define TMP_BUF_SIZE 8
static void
show_register_data (void)
{
    ccsipCCB_t *ccb;
    char       buf[TMP_BUF_SIZE];
    line_t     ndx = 0;
    int        proxy_register = 0;
    boolean    valid_line = FALSE;
    int        timer_count_down = 0;

    config_get_value(CFGID_PROXY_REGISTER, &proxy_register,
                     sizeof(proxy_register));

    debugif_printf("\nLINE REGISTRATION TABLE\nProxy Registration: ");
    if (proxy_register == 1) {
        debugif_printf("ENABLED");
    } else {
        debugif_printf("DISABLED");
    }
    debugif_printf(", state: %s\n",
                   ccsip_register_state_name(ccsip_register_get_register_state()));
    debugif_printf("line  APR  state          timer       expires     proxy:port\n");
    debugif_printf("----  ---  -------------  ----------  ----------  ----------------------------\n");
    for (ndx = REG_CCB_START; ndx <= REG_BACKUP_CCB; ndx++) {
        ccb = sip_sm_get_ccb_by_index(ndx);
        valid_line = FALSE;
        if (ndx == REG_BACKUP_CCB) {
            valid_line = TRUE;
        } else {
            if (sip_config_check_line((line_t)(ndx - TEL_CCB_END))) {
                valid_line = TRUE;
            }
        }
        // calc the time until registration expires
        if (ccb && valid_line) {
            if (!proxy_register || !valid_line) {
                timer_count_down = 0;
            } else {
                if (ccb->reg.act_time == 0) {
                    timer_count_down = 0;
                } else if ((sipRegSMStateType_t)ccb->state ==
                        SIP_REG_STATE_REGISTERING) {
                    timer_count_down =
                        (SIP_REG_TMR_ACK_TICKS / 1000) -
                        (((int) time(NULL)) - ccb->reg.act_time);
                } else {
                    timer_count_down = ccb->reg.tmr_expire -
                        (((int) time(NULL)) - ccb->reg.act_time);
                }
            }

	    /*
             * Ensure that the timer_count_down value is valid. There
             * are cases where the phone completes registration before an
             * SNTP/NTP response is received which makes the act_time and
             * time(NULL) values inconsistent.
             */
	    if ((timer_count_down < 0) ||
		(timer_count_down > ccb->reg.tmr_expire)) {

		timer_count_down = 0;
	    }
        }

        // build the flag string
        if (ccb) {
            build_reg_flags((char *)&buf, TMP_BUF_SIZE, proxy_register,
                            ccb, valid_line);
        }

        if (ndx != REG_BACKUP_CCB) {
            debugif_printf("%-4d", ndx - TEL_CCB_END);
        } else {
            debugif_printf("1-BU");
        }
        if (ccb && valid_line) {
            debugif_printf("  %-3s  %-13s  %-10d  %-10d  %s:%d\n",
                           buf,
                           ccsip_register_reg_state_name((sipRegSMStateType_t)ccb->state),
                           ccb->reg.tmr_expire,
                           timer_count_down,
                           ((ccb->reg.proxy[0] != '\0') ? (ccb->reg.proxy) : ("undefined")),
                           ccb->reg.port);
        } else {
            debugif_printf("  %-3s  %-13s  %-10d  %-10d  %s:%d\n",
                           buf, "NONE", 0, 0, "undefined", 0);
        }
    }
    debugif_printf("\nNote: APR is Authenticated, Provisioned, Registered\n");
}

/*
 *  Function: show_register_cmd
 *
 *  Parameters:  argc,argv
 *
 *  Description:  Shows the current registration table
 *
 *  Returns:
 *
 */
cc_int32_t
show_register_cmd (cc_int32_t argc, const char *argv[])
{

    /*
     * check if need help
     */
    if ((argc == 2) && (argv[1][0] == '?')) {
        debugif_printf("sh reg\n");
        return (0);
    }
    show_register_data();

    return (0);
}

void
ccsip_register_clear_all_logs (void)
{
    log_clear(LOG_REG_MSG);
    log_clear(LOG_REG_AUTH);
    log_clear(LOG_REG_RETRY);
    log_clear(LOG_REG_UNSUPPORTED);
}


void
ccsip_register_reset_proxy (void)
{
    ccsipCCB_t *ccb;
    line_t ndx;
    line_t line_end;

    /*
     * If this is a restart (phone is resetting) then reset the proxy
     * string for each line.  The proxy string is used to determine
     * what address to use when registering. The proxy remains the
     * same throughout a boot cycle and on restarts there is a new
     * boot cycle so the proxy address is reset.
     */
    line_end = 1;

    /*
     * REG CCBs start after the TEL CCBs.
     * EG., TEL CCBs are at 1 and 2 and REG CCBs are at 3 and 4.
     * So, line_end equals the number of lines and then add the TEL_CCB_END
     * to get the ending of the REG CCBs
     */
    line_end += TEL_CCB_END;

    //ccsip_register_lines = line_end - REG_CCB_START + 1;
    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
        if (sip_config_check_line((line_t)(ndx - TEL_CCB_END))) {
            ccb = sip_sm_get_ccb_by_index(ndx);
            if (ccb) {
                ccb->reg.proxy[0] = '\0';
            }
        }
    }

    //reset backup proxy registration;
    ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);
    if (ccb) {
        ccb->reg.proxy[0] = '\0';
    }
}

int
ccsip_register_init (void)
{
    static const char fname[] = "ccsip_register_init";
    static const char sipAckTimerName[] = "sipAck";
    int i;

    ccsip_register_set_register_state(SIP_REG_IDLE);

    /*
     * Create acknowledgement timers
     */
    for (i = 0; i < MAX_REG_LINES + 1; i++) {
        ack_tmrs[i] = cprCreateTimer(sipAckTimerName, SIP_ACK_TIMER,
                                     TIMER_EXPIRATION, sip_msgq);
        if (ack_tmrs[i] == NULL) {
            CCSIP_DEBUG_ERROR("%s: timer NOT created: %d\n",
                              fname, i);
            return SIP_ERROR;
        }
    }

    // Initialize date holder structure
    ccm_date.valid = FALSE;
    ccm_date.datestring[0] = '\0';
    start_standby_monitor = TRUE;

    return SIP_OK;
}


ccsip_register_states_t
ccsip_register_get_register_state (void)
{
    return (ccsip_register_state);
}


void
ccsip_register_set_register_state (ccsip_register_states_t state)
{
    ccsip_register_state = state;
}

void
ccsip_register_cancel (boolean cancel_reg, boolean backup_proxy)
{
    static const char fname[] = "ccsip_register_cancel";
    ccsipCCB_t *ccb;
    line_t     ndx;
    line_t     line_end;


    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));

    if (ccsip_register_get_register_state() == SIP_REG_IDLE) {
        return;
    }

    ccsip_register_set_register_state(SIP_REG_UNREGISTERING);

    line_end = 1;

    ui_set_sip_registration_state((line_t) (line_end + 1), FALSE);

    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"unregistering %d lines\n", DEB_F_PREFIX_ARGS(SIP_REG, fname), line_end);

    /*
     * REG CCBs start after the TEL CCBs.
     * EG., TEL CCBs are at 1 and 2 and REG CCBs are at 3 and 4.
     * So, line_end equals the number of lines and then add the TEL_CCB_END
     * to get the ending of the REG CCBs
     */
    line_end += TEL_CCB_END;

    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
        if (sip_config_check_line((line_t) (ndx - TEL_CCB_END))) {
            ccb = sip_sm_get_ccb_by_index(ndx);
            if (!ccb) {
                continue;
            }
            ui_set_sip_registration_state(ccb->dn_line, FALSE);

            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"cancelling timers, line= %d\n",
                                  DEB_F_PREFIX_ARGS(SIP_TIMER, fname), ccb->index);
            (void) sip_platform_register_expires_timer_stop(ccb->index);
            sip_stop_ack_timer(ccb);

            if (cancel_reg) {
                if (ccb->index == REG_CCB_START) {
                    ccb->send_reason_header = TRUE;
                }
                if (ccb->index == REG_CCB_START) {
                     ccb->send_reason_header = TRUE;
                }

                    if (ccsip_register_send_msg(SIP_REG_CANCEL, ndx)
                        != SIP_REG_OK) {
                        ccsip_register_cleanup(ccb, FALSE);
                    }

            } else {
                /* Cleanup and re-initialize CCB */
                sip_sm_call_cleanup(ccb);
                if (ndx == line_end) {
                    ccsip_register_set_register_state(SIP_REG_IDLE);
                }
            }
        }
    }

    if (backup_proxy == FALSE) {
        return;
    }

    /* cancel backup registration as well */
    ccb = sip_sm_get_ccb_by_index(REG_BACKUP_CCB);

    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"cancelling timers, line= %d\n",
                          DEB_F_PREFIX_ARGS(SIP_TIMER, fname), ccb->index);

    (void) sip_platform_register_expires_timer_stop(ccb->index);
    sip_stop_ack_timer(ccb);

    if (cancel_reg) {
        if (ccsip_register_send_msg(SIP_REG_CANCEL, REG_BACKUP_CCB)
                != SIP_REG_OK) {
            ccsip_register_cleanup(ccb, FALSE);
        }
    } else {
        /* Cleanup and re-initialize CCB */
        sip_sm_call_cleanup(ccb);
    }
}


void
ccsip_register_all_lines (void)
{
    static const char fname[] = "ccsip_register_all_lines";
    ccsipCCB_t     *ccb = 0;
    line_t          ndx;
    line_t          line;
    line_t          line_end = 1;
    int             value = 0;
    char            proxyaddress[MAX_IPADDR_STR_LEN];

    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));

    /* Do not register if
     * - no need to register
     * - and already registered
     */

    line_end = 1;
    config_get_value(CFGID_PROXY_REGISTER, &value, sizeof(value));
    if (value == 0) {
        CCSIP_DEBUG_REG_STATE(get_debug_string(DEBUG_REG_DISABLED), NULL,
                              NULL, fname);

        // mark all unregistered
        for (line = 1; line <= line_end; line++) {
            if (sip_config_check_line(line)) {
                ui_set_sip_registration_state(line, FALSE);
            }
        }
        ccsip_register_reset_proxy();
        return;
    } else if (ccsip_register_get_register_state() == SIP_REG_REGISTERED) {
        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"lines already registered\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));

        return;
    }


    ccsip_register_reset_proxy();
    ccsip_register_set_register_state(SIP_REG_REGISTERING);

    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"registering %d line%c\n",
                          DEB_F_PREFIX_ARGS(SIP_REG, fname), line_end, line_end > 1 ? 's' : ' ');

    /* register line 1 to the backup proxy.
     * Start with the backup as DNS lookups hold
     * the task meaning it could take a while to
     * register the six regular lines.
     */
    ndx = REG_BACKUP_CCB;
    ccb = sip_sm_get_ccb_by_index(ndx);
    /* Cleanup and re-initialize CCB */
    sip_sm_call_cleanup(ccb);
    if (ccb->cc_type == CC_CCM) {
        /*
         * regmgr - Set the Backup (standby ccm) info.
         */
        ti_config_table_t *standby_ccm =
            CCM_Active_Standby_Table.standby_ccm_entry;

        if (standby_ccm) {
            ti_ccm_t *ti_ccm = &standby_ccm->ti_specific.ti_ccm;

            sip_regmgr_setup_new_standby_ccb(ti_ccm->ccm_id);
        } else {
            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"ERROR: Standby ccm entry is NULL\n",
                                  DEB_F_PREFIX_ARGS(SIP_REG, fname));
        }
    } else {
        /*
         * Assume CSPS since only those two for now.
         */
        if (util_check_if_ip_valid(&(ccb->dest_sip_addr))) {
            CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"%d, 0x%x\n",
                                  DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname),
                                  ndx, ccb);

            ccb->reg.addr = ccb->dest_sip_addr;
            ccb->reg.port = (uint16_t) ccb->dest_sip_port;

            if (ccb->index == REG_CCB_START) {
                ccb->send_reason_header = TRUE;
            } else {
                ccb->send_reason_header = FALSE;
            }

            if (ccsip_register_send_msg(SIP_REG_REQ, ndx) != SIP_REG_OK) {
                ccsip_register_cleanup(ccb, TRUE);
            }
        } else {
            CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"%d: Backup Proxy not configured\n",
                                  DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname), ndx);
        }
    }

    /*
     * REG CCBs start after the TEL CCBs.
     * EG., TEL CCBs are at 1 and 2 and REG CCBs are at 3 and 4.
     * So, line_end equals the number of lines and then add the TEL_CCB_END to
     * get the ending of the REG CCBs
     */
    line_end += TEL_CCB_END;

    DEF_DEBUG(DEB_F_PREFIX"Disabling mass reg state", DEB_F_PREFIX_ARGS(SIP_REG, fname));
    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
        if (sip_config_check_line((line_t)(ndx - TEL_CCB_END))) {
            ccb = sip_sm_get_ccb_by_index(ndx);
            if (!ccb) {
                continue;
            }

            CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"%d, 0x%x\n",
                                  DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname),
                                  ndx, ccb);


            /*
             * Clean up and re-initialize the proxy address.
             * It is possible that the REGISTER can be redirected
             * and this address needs to be reused for later register attempts.
             * But, on restarts and re-enabling of registering we reset back
             * to the original configured proxy
             */
            if (ndx == REG_CCB_START || ndx == (line_end)) {
                g_disable_mass_reg_debug_print = FALSE;
            } else {
                g_disable_mass_reg_debug_print = TRUE;
            }
            sip_sm_call_cleanup(ccb);

            /*
             *  Look up the name of the proxy
             */
            sipTransportGetPrimServerAddress(ccb->dn_line, proxyaddress);

            sstrncpy(ccb->reg.proxy, proxyaddress, MAX_IPADDR_STR_LEN);

            ccb->reg.addr = ccb->dest_sip_addr;
            ccb->reg.port = (uint16_t) ccb->dest_sip_port;

            if (ccb->index == REG_CCB_START) {
                ccb->send_reason_header = TRUE;
            } else {
                ccb->send_reason_header = FALSE;
            }

                ui_set_sip_registration_state(ccb->dn_line, FALSE);
                if (ccsip_register_send_msg(SIP_REG_REQ, ndx) != SIP_REG_OK) {
                    ccsip_register_cleanup(ccb, TRUE);
                }

        }
    }
    g_disable_mass_reg_debug_print = FALSE;

    sip_platform_cc_mode_notify();
}


boolean
ccsip_register_all_registered (void)
{
    ccsipCCB_t *ccb;
    line_t ndx;
    line_t line_end;

    line_end = 1;

    /*
     * REG CCBs start after the TEL CCBs.
     * EG., TEL CCBs are at 1 and 2 and REG CCBs are at 3 and 4.
     * So, line_end equals the number of lines and then add the TEL_CCB_END
     * to get the ending of the REG CCBs
     */
    line_end += TEL_CCB_END;

    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
        if (sip_config_check_line((line_t)(ndx - TEL_CCB_END))) {
            ccb = sip_sm_get_ccb_by_index(ndx);

            if (ccb && (ccb->reg.registered == 0)) {
                return (FALSE);
            }
        }
    }
    return (TRUE);
}


boolean
ccsip_register_all_unregistered (void)
{
    ccsipCCB_t *ccb;
    line_t ndx;
    line_t line_end;

    line_end = 1;

    /*
     * REG CCBs start after the TEL CCBs.
     * EG., TEL CCBs are at 0 and 1 and REG CCBs are at 6 and 7.
     * So, line_end equals the number of lines and then add the TEL_CCB_END
     * to get the ending of the REG CCBs
     */
    line_end += TEL_CCB_END;

    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
        if (sip_config_check_line((line_t)(ndx - TEL_CCB_END))) {
            ccb = sip_sm_get_ccb_by_index(ndx);

            if (ccb && (ccb->reg.registered == 1)) {
                return (FALSE);
            }
        }
    }
    return (TRUE);
}


void
ccsip_register_commit (void)
{
    static const char fname[] = "ccsip_register_commit";
    int register_get;

    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));

    /*
     * Determine if lines need to register or unregister
     *
     * register if
     * - proxy_register was changed from 0 to 1
     *
     * unregister if
     * - proxy_register was changed from 1 to 0 and already registered
     */
    config_get_value(CFGID_PROXY_REGISTER, &register_get,
                     sizeof(register_get));

    switch (register_get) {
    case 0:
        if (ccsip_register_get_register_state() != SIP_REG_IDLE) {
            ccsip_register_cancel(TRUE, TRUE);
        } else {
            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"lines already unregistered.\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
        }

        break;

    case 1:
        if (ccsip_register_get_register_state() != SIP_REG_REGISTERED) {
            ccsip_register_cancel(FALSE, TRUE);
            ccsip_register_all_lines();
        } else {
            CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"lines already registered.\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));
        }
        break;

    default:
        CCSIP_DEBUG_ERROR(DEB_F_PREFIX"Error: invalid register_get= %d\n", DEB_F_PREFIX_ARGS(SIP_REG, fname),
                          register_get);
    }
}

void
ccsip_backup_register_commit (void)
{
    static const char fname[] = "ccsip_backup_register_commit";
    line_t ndx;
    ccsipCCB_t *ccb = 0;

    CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"\n", DEB_F_PREFIX_ARGS(SIP_REG, fname));

    ndx = REG_BACKUP_CCB;
    ccb = sip_sm_get_ccb_by_index(ndx);

    /* cancel an existing registration if it exists */
    if (util_check_if_ip_valid(&(ccb->reg.addr))) {
        CCSIP_DEBUG_REG_STATE(DEB_F_PREFIX"cancelling registration, line= %d\n",
                              DEB_F_PREFIX_ARGS(SIP_REG, fname), ccb->index);
        if (ccsip_register_send_msg(SIP_REG_CANCEL, ndx) != SIP_REG_OK) {
            ccsip_register_cleanup(ccb, FALSE);
        }
    }

    /* Cleanup and re-initialize CCB */
    sip_sm_call_cleanup(ccb);

    if (util_check_if_ip_valid(&(ccb->dest_sip_addr))) {
        CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"%d, 0x%x\n",
                              DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname),
                              ndx, ccb);
        ccb->reg.addr = ccb->dest_sip_addr;
        ccb->reg.port = (uint16_t) ccb->dest_sip_port;

        if (ccsip_register_send_msg(SIP_REG_REQ, ndx) != SIP_REG_OK) {
            ccsip_register_cleanup(ccb, TRUE);
        }
    } else {
        log_clear(LOG_REG_BACKUP);
    }
}


void
cred_get_line_credentials (line_t line, credentials_t *pcredentials,
                           int id_len, int pw_len)
{
    config_get_line_string(CFGID_LINE_AUTHNAME, pcredentials->id, line,
                           id_len);

    if ((strcmp(pcredentials->id, "") == 0) ||
        (strcmp(pcredentials->id, UNPROVISIONED) == 0)) {
        config_get_line_string((CFGID_LINE_AUTHNAME), pcredentials->id, 1,
                               id_len);
    }

    config_get_line_string(CFGID_LINE_PASSWORD, pcredentials->pw, line,
                           pw_len);
    if ((strcmp(pcredentials->pw, "") == 0) ||
        (strcmp(pcredentials->pw, UNPROVISIONED) == 0)) {
        config_get_line_string(CFGID_LINE_PASSWORD, pcredentials->pw, 1,
                               pw_len);
    }
}


boolean
cred_get_credentials_r (ccsipCCB_t *ccb, credentials_t *pcredentials)
{
    if (!(ccb->authen.cred_type & CRED_LINE) ||
        (ccb->authen.retries_401_407 < MAX_RETRIES_401)) {
        ccb->authen.cred_type |= CRED_LINE;
        ccb->authen.retries_401_407++;
        cred_get_line_credentials(ccb->dn_line, pcredentials,
                                  sizeof(pcredentials->id),
                                  sizeof(pcredentials->pw));
        return (TRUE);
    }

    return (FALSE);
}


const char *
ccsip_register_state_name (ccsip_register_states_t state)
{
    if ((state < SIP_REG_IDLE) || (state >= SIP_REG_NO_REGISTER)) {
        return ("UNDEFINED");
    }

    return (ccsip_register_state_names[state]);
}


const char *
ccsip_register_reg_state_name (sipRegSMStateType_t state)
{
    if ((state < SIP_REG_STATE_NONE) || (state >= SIP_REG_STATE_UNREGISTERING)) {
        return ("UNDEFINED");
    }

    return (ccsip_register_reg_state_names[state]);
}

void
ccsip_register_shutdown ()
{
    int i;

    for (i = 0; i < MAX_REG_LINES + 1; i++) {
        (void) cprCancelTimer(ack_tmrs[i]);
        (void) cprDestroyTimer(ack_tmrs[i]);
        ack_tmrs[i] = NULL;
    }
}

boolean
ccsip_get_ccm_date (char *date_value)
{
    if (date_value) {
        if (ccm_date.valid) {
            sstrncpy(date_value, ccm_date.datestring,
                     sizeof(ccm_date.datestring));
            return (TRUE);
        } else {
            date_value[0] = '\0';
        }
    }
    return (FALSE);
}

/*
 *  Function: ccsip_is_line_registered
 *
 *  Parameters:
 *      dn_line - line number.
 *
 *  Description:  The function determines whether the current line
 *      is registered or not.
 *
 *  Returns:
 *      TRUE if the line is registered.
 *      FALSE if the line is not registered or the given line is invalid.
 *
 */
boolean
ccsip_is_line_registered (line_t dn_line)
{
    line_t ndx;
    line_t line_end;
    ccsipCCB_t *ccb;

    /* Get the number of lines configured */
    line_end = 1;
    /*
     * REG CCBs start after the TEL CCBs.
     * So, line_end equals the number of lines and then add the TEL_CCB_END
     * to get the ending of the REG CCBs
     */
    line_end += TEL_CCB_END;

    for (ndx = REG_CCB_START; ndx <= line_end; ndx++) {
        ccb = sip_sm_get_ccb_by_index(ndx);
        if ((ccb != NULL) && (ccb->dn_line == dn_line)) {
            /* found the requested REG ccb */
            if (ccb->state == (int) SIP_REG_STATE_REGISTERED) {
                return (TRUE);
            } else {
                /* the line is not in registered state */
                break;
            }
        }
    }

    /* Found no matching REG ccb or the line is not in registered state */
    return (FALSE);
}

/*
 *  Function: process_retry_after
 *
 *  Parameters:
 *      ccsipCCB_t *ccb - The reg ccb.
 *      sipMessage_t *response - The received response.
 *
 *  Description:  The function processes the Retry-After header that may
 *       be present in the response.
 *
 *  Returns:
 *      TRUE if a valid Retry-After header was present and processed correctly.
 *      FALSE if a valid Retry-After header was not present and therefore
 *            was not processed.
 *
 */
boolean
process_retry_after (ccsipCCB_t *ccb, sipMessage_t *response)
{
    const char *msg_ptr = NULL;
    int32_t    retry_after = 0;
    static const char fname[] = "process_retry_after";

    msg_ptr = sippmh_get_header_val(response,
                                    (const char *)SIP_HEADER_RETRY_AFTER,
                                    NULL);

    if (msg_ptr) {
        retry_after = strtoul(msg_ptr, NULL, 10);

    } else {
        return (FALSE);
    }

    if (retry_after > 0) {
        sip_stop_ack_timer(ccb);
        (void) sip_platform_register_expires_timer_start(retry_after * 1000,
                                                         ccb->index);
        CCSIP_DEBUG_REG_STATE(DEB_L_C_F_PREFIX"Retrying after %d\n",
                              DEB_L_C_F_PREFIX_ARGS(SIP_REG, ccb->index, ccb->dn_line, fname), retry_after);
        return (TRUE);
    } else {
        CCSIP_DEBUG_ERROR("REG %d/%d: %-35s: Error: invalid Retry-After "
                          "header in response.\n",
                          ccb->index, ccb->dn_line, fname);
        return (FALSE);
    }
}


