/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "cpr_types.h"
#include "cpr_stdio.h"

#include "ccsip_core.h"
#include "ccsip_callinfo.h"
#include "ccsip_messaging.h"
#include "uiapi.h"
#include "lsm.h"
#include "fsm.h"
#include "vcm.h"
#include "phone_debug.h"
#include "singly_link_list.h"
#include "ccapi.h"

extern int httpish_strncasecmp(const char *s1, const char *s2, size_t len);

typedef unsigned int info_index_t;
typedef unsigned int type_index_t;

typedef struct {
    info_package_handler_t handler;
    info_index_t info_index;    // the index of the info_package in g_registered_info array
                                // i.e., the value returned by find_info_index()
    type_index_t type_index;    // the index of the content_type in g_registered_type array
                                // i.e., the value returned by find_type_index()
} handler_record_t;

/* Info Package handler registry */
static sll_handle_t s_handler_registry = NULL;

#define INDEX_NOT_FOUND     ((unsigned int)-1)

/*
 * g_registered_info[] contains the Info Package strings (such as
 * "conference") for the registered handlers.
 */
char *g_registered_info[MAX_INFO_HANDLER];

static char *s_registered_type[MAX_INFO_HANDLER];

static sll_match_e is_matching_type(void *find_by_p, void *data_p);
static info_index_t find_info_index(const char *info_package);
static info_index_t find_next_available_info_index(void);
static type_index_t find_type_index(const char *type_package);
static type_index_t find_next_available_type_index(void);
static handler_record_t *find_handler_record(info_index_t info_index,
                                             type_index_t type_index);
static boolean is_info_package_registered(info_index_t info_index);
static boolean is_content_type_registered(type_index_t type_index);
static void update_recv_info_list(const char *header_field_value,
                                  string_t *info_packages);
static void media_control_info_package_handler(line_t line, callid_t call_id,
                                               const char *info_package,
                                               const char *content_type,
                                               const char *message_body);
#ifdef _CONF_ROSTER_
static void conf_info_package_handler(line_t line, callid_t call_id,
                                           const char *info_package,
                                           const char *content_type,
                                           const char *message_body);
#endif



/*
 *  Function: find_info_index
 *
 *  Parameters:
 *      info_package - the Info Package to find
 *
 *  Description:
 *      Finds the Info Package in g_registered_info array.
 *
 *  Return:
 *      info_index_t - the index of the Info Package in the array
 *      INDEX_NOT_FOUND - the Info Package was not found in the array
 */
static info_index_t
find_info_index(const char *info_package)
{
    info_index_t info_index;

    for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) {
        if (g_registered_info[info_index] &&
            httpish_strncasecmp(info_package,
                                g_registered_info[info_index],
                                strlen(g_registered_info[info_index])) == 0) {
            return info_index;
        }
    }

    return INDEX_NOT_FOUND;
}

/*
 *  Function: find_next_available_info_index
 *
 *  Parameters:
 *      None
 *
 *  Description:
 *      Finds an empty slot in g_registered_info array.
 *
 *  Return:
 *      info_index_t - the index of the empty slot in the array
 *      INDEX_NOT_FOUND - the array is full
 */
static info_index_t
find_next_available_info_index(void)
{
    info_index_t info_index;

    for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) {
        if (g_registered_info[info_index] == NULL) {
            return info_index;
        }
    }

    return INDEX_NOT_FOUND;
}

/*
 *  Function: find_type_index
 *
 *  Parameters:
 *      content_type - the Content Type to find
 *
 *  Description:
 *      Finds the Content Type in s_registered_type array.
 *
 *  Return:
 *      type_index_t - the index of the Content Type in the array
 *      INDEX_NOT_FOUND - the Content Type was not found in the array
 */
static type_index_t
find_type_index(const char *content_type)
{
    type_index_t type_index;

    for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) {
        if (s_registered_type[type_index] &&
            httpish_strncasecmp(content_type,
                                s_registered_type[type_index],
                                strlen(s_registered_type[type_index])) == 0) {
            return type_index;
        }
    }

    return INDEX_NOT_FOUND;
}

/*
 *  Function: find_next_available_type_index
 *
 *  Parameters:
 *      None
 *
 *  Description:
 *      Finds an empty slot in s_registered_type array.
 *
 *  Return:
 *      type_index_t - the index of the empty slot in the array
 *      INDEX_NOT_FOUND - the array is full
 */
static type_index_t
find_next_available_type_index(void)
{
    type_index_t type_index;

    for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) {
        if (s_registered_type[type_index] == NULL) {
            return type_index;
        }
    }

    return INDEX_NOT_FOUND;
}

/*
 *  Function: is_matching_type
 *
 *  Parameters:
 *      find_by_p - searching criteria
 *      data_p - a node
 *
 *  Description:
 *      Checks to see if the node matches the searching criteria.
 *
 *  Return:
 *      SLL_MATCH_FOUND - the node is a match
 *      SLL_MATCH_NOT_FOUND - the node is not a match
 */
static sll_match_e
is_matching_type(void *find_by_p, void *data_p)
{
    handler_record_t *tuple = (handler_record_t *)find_by_p;
    handler_record_t *node = (handler_record_t *)data_p;

    if ((node->info_index == tuple->info_index) &&
        (node->type_index == tuple->type_index)) {
        return SLL_MATCH_FOUND;
    }
    return SLL_MATCH_NOT_FOUND;
}

/*
 *  Function: find_handler_record
 *
 *  Parameters:
 *      info_index - the index of the Info Package in g_registered_info array
 *      type_index - the index of the Content Type in s_registered_type array
 *
 *  Description:
 *      Finds the Info Package handler registered for the Info Package/Content
 *      Type pair.
 *
 *  Return:
 *      handler_record_t * - the registered handler record
 *      NULL - otherwise
 */
static handler_record_t *
find_handler_record(info_index_t info_index, type_index_t type_index)
{
    handler_record_t tuple;

    tuple.info_index = info_index;
    tuple.type_index = type_index;

    return (handler_record_t *)sll_find(s_handler_registry, &tuple);
}

/*
 *  Function: is_info_package_registered
 *
 *  Parameters:
 *      info_index - the index of the Info Package in g_registered_info array
 *
 *  Description:
 *      Checks to see if a handler was registered for the Info Package.
 *
 *  Return:
 *      TRUE - a handler was registered for the Info Package
 *      FALSE - otherwise
 */
static boolean
is_info_package_registered(info_index_t info_index)
{
    handler_record_t *record;

    for (record = (handler_record_t *)sll_next(s_handler_registry, NULL);
         record != NULL;
         record = (handler_record_t *)sll_next(s_handler_registry, record)) {
        if (record->info_index == info_index) {
            return TRUE;
        }
    }

    return FALSE;
}

/*
 *  Function: is_content_type_registered
 *
 *  Parameters:
 *      type_index - the index of the Content Type in s_registered_type array
 *
 *  Description:
 *      Checks to see if a handler was registered for the Content Type.
 *
 *  Return:
 *      TRUE - a handler was registered for the Content Type
 *      FALSE - otherwise
 */
static boolean
is_content_type_registered(type_index_t type_index)
{
    handler_record_t *record;

    for (record = (handler_record_t *)sll_next(s_handler_registry, NULL);
         record != NULL;
         record = (handler_record_t *)sll_next(s_handler_registry, record)) {
        if (record->type_index == type_index) {
            return TRUE;
        }
    }

    return FALSE;
}

/*
 *  Function: ccsip_register_info_package_handler
 *
 *  Parameters:
 *      info_package - the Info Package for the handler
 *      content_type - the Content Type for the handler
 *      handler - the handler
 *
 *  Description:
 *      Registers the handler for the Info Package/Content Type pair.
 *
 *  Return:
 *      SIP_OK - the handler was registered successfully
 *      SIP_ERROR - otherwise
 */
int
ccsip_register_info_package_handler(const char *info_package,
                                    const char *content_type,
                                    info_package_handler_t handler)
{
    static const char *fname = "ccsip_register_info_package_handler";
    info_index_t info_index;
    type_index_t type_index;
    char *tmp_info = NULL;
    char *tmp_type = NULL;
    handler_record_t *record;

    if (s_handler_registry == NULL) {
        CCSIP_DEBUG_TASK("%s: Info Package handler was not initialized\n", fname);
        return SIP_ERROR;
    }

    if ((info_package == NULL) || (content_type == NULL) || (handler == NULL)) {
        CCSIP_DEBUG_ERROR("%s: invalid parameter\n", fname);
        return SIP_ERROR;
    }

    /* Find the info_index for the info_package */
    info_index = find_info_index(info_package);

    if (info_index == INDEX_NOT_FOUND) {
        /* Find the first available slot */
        info_index = find_next_available_info_index();

        if (info_index == INDEX_NOT_FOUND) {
            CCSIP_DEBUG_ERROR("%s: maximum reached\n", fname);
            return SIP_ERROR;
        }

        tmp_info = cpr_strdup(info_package);
        if (tmp_info == NULL) {
            CCSIP_DEBUG_ERROR("%s: failed to duplicate info_package string\n", fname);
            return SIP_ERROR;
        }
    }

    /* Find the type_index for the content_type */
    type_index = find_type_index(content_type);

    if (type_index == INDEX_NOT_FOUND) {
        /* Find the first available slot */
        type_index = find_next_available_type_index();

        if (type_index == INDEX_NOT_FOUND) {
            CCSIP_DEBUG_ERROR("%s: maximum reached\n", fname);
            if (tmp_info != NULL) {
                cpr_free(tmp_info);
            }
            return SIP_ERROR;
        }

        tmp_type = cpr_strdup(content_type);
        if (tmp_type == NULL) {
            CCSIP_DEBUG_ERROR("%s: failed to duplicate info_package string\n", fname);
            if (tmp_info != NULL) {
                cpr_free(tmp_info);
            }
            return SIP_ERROR;
        }
    }

    /* Check to see if the info/type tuple has been registered before */
    if (find_handler_record(info_index, type_index) != NULL) {
        CCSIP_DEBUG_ERROR("%s: Info Package handler already registered\n", fname);
        return SIP_ERROR;
    }

    /*
     * At this point, info_index points to the slot in g_registered_info where
     * either:
     *
     * 1) the info_package is residing, or
     * 2) the info_package (a copy of which is pointed to by *tmp_info) will be
     *    copied to before the function returns
     *
     * type_index is similar.
     */

    record = (handler_record_t *)cpr_malloc(sizeof(handler_record_t));
    if (record == NULL) {
        if (tmp_type != NULL) {
            cpr_free(tmp_type);
        }
        if (tmp_info != NULL) {
            cpr_free(tmp_info);
        }
        CCSIP_DEBUG_ERROR("%s: failed to allocate info handler record\n", fname);
        return SIP_ERROR;
    }

    record->handler = handler;
    record->info_index = info_index;
    record->type_index = type_index;

    if (sll_append(s_handler_registry, record) != SLL_RET_SUCCESS) {
        cpr_free(record);
        if (tmp_type != NULL) {
            cpr_free(tmp_type);
        }
        if (tmp_info != NULL) {
            cpr_free(tmp_info);
        }
        CCSIP_DEBUG_ERROR("%s: failed to insert to the registry\n", fname);
        return SIP_ERROR;
    }

    if (tmp_info != NULL) {
        g_registered_info[info_index] = tmp_info;
    }
    if (tmp_type != NULL) {
        s_registered_type[type_index] = tmp_type;
    }

    return SIP_OK;
}

/*
 *  Function: ccsip_deregister_info_package_handler
 *
 *  Parameters:
 *      info_package - the Info Package for the handler
 *      content_type - the Content Type for the handler
 *      handler - the handler
 *
 *  Description:
 *      Deregisters the handler for the Info Package/Content Type pair.
 *
 *  Return:
 *      SIP_OK - the handler was registered successfully
 *      SIP_ERROR - otherwise
 */
int
ccsip_deregister_info_package_handler(const char *info_package,
                                      const char *content_type,
                                      info_package_handler_t handler)
{
    static const char *fname = "ccsip_deregister_info_package_handler";
    info_index_t info_index;
    type_index_t type_index;
    handler_record_t *record;

    if (s_handler_registry == NULL) {
        CCSIP_DEBUG_TASK("%s: Info Package handler was not initialized\n", fname);
        return SIP_ERROR;
    }

    /* Find the info_index for the info_package */
    info_index = find_info_index(info_package);
    if (info_index == INDEX_NOT_FOUND) {
        CCSIP_DEBUG_ERROR("%s: handler was not registered (%s)\n",
                          fname, info_package);
        return SIP_ERROR;
    }

    /* Find the type_index for the content_type */
    type_index = find_type_index(content_type);
    if (type_index == INDEX_NOT_FOUND) {
        CCSIP_DEBUG_ERROR("%s: handler was not registered (%s)\n",
                          fname, content_type);
        return SIP_ERROR;
    }

    /* Find the handler record */
    record = find_handler_record(info_index, type_index);
    if ((record == NULL) || (record->handler != handler)) {
        CCSIP_DEBUG_ERROR("%s: handler was not registered (%p)\n",
                          fname, handler);
        return SIP_ERROR;
    }

    (void)sll_remove(s_handler_registry, record);

    cpr_free(record);

    if (!is_info_package_registered(info_index)) {
        /* The info_package was not found in the registry, meaning we're
         * the last one who registered for this particular info_package */
        cpr_free(g_registered_info[info_index]);
        g_registered_info[info_index] = NULL;
    }

    if (!is_content_type_registered(type_index)) {
        /* The content_type was not found in the registry, meaning we're
         * the last one who registered for this particular content_type */
        cpr_free(s_registered_type[type_index]);
        s_registered_type[type_index] = NULL;
    }

    return SIP_OK;
}

/*
 *  Function:  update_recv_info_list
 *
 *  Parameters:
 *      header_field_value - the header field value to match (e.g.,
 *      "conference")
 *      info_packages - the Info Packages string to append the header
 *      field value to
 *
 *  Description:
 *      Checks to see if a handler is registered for the header field value
 *      (e.g., "conference"), if so, append the header field value to
 *      the end of info_packages.
 *
 *  Returns:
 *      None
 */
static void
update_recv_info_list(const char *header_field_value, string_t *info_packages)
{
    static const char *fname = "update_recv_info_list";
    info_index_t info_index;

    if ((header_field_value == NULL) || (info_packages == NULL) ||
        (*info_packages == NULL)) {
        CCSIP_DEBUG_ERROR("%s: invalid parameter\n", fname);
        return;
    }

    info_index = find_info_index(header_field_value);
    if (info_index != INDEX_NOT_FOUND) {
        /* Info-Package is supported */
        if (**info_packages == '\0') {
            *info_packages = strlib_update(*info_packages,
                                           g_registered_info[info_index]);
        } else {
            *info_packages = strlib_append(*info_packages, ", ");
            *info_packages = strlib_append(*info_packages,
                                           g_registered_info[info_index]);
        }
    }
}

/*
 *  Function:  ccsip_parse_send_info_header
 *
 *  Parameters:
 *      ccb         - the SIP CCB
 *      pSipMessage - the SIP message
 *
 *  Description:
 *      Checks the Send-Info header (if exists) to see if a handler was
 *      registered for the Info Package.  If so, append the Info Package
 *      to the end of recv_info_list.
 *
 *  Returns:
 *      None
 */
void
ccsip_parse_send_info_header(sipMessage_t *pSipMessage, string_t *recv_info_list)
{
    char *send_info[MAX_INFO_HANDLER];
    int count;
    int i;
    char *header_field_values;
    char *header_field_value;
    char *separator;

    // leading white spaces are trimmed, but not trailing ones
    count = sippmh_get_num_particular_headers(pSipMessage,
                                              SIP_HEADER_SEND_INFO,
                                              NULL,
                                              send_info,
                                              MAX_INFO_HANDLER);

    if (count == 0) {
        return;
    }

    for (i = 0; (i < count) && (i < MAX_INFO_HANDLER); i++) {
        header_field_values = cpr_strdup(send_info[i]);
        if (header_field_values == NULL) {
            return;
        }
        header_field_value = header_field_values;

        while ((separator = strchr(header_field_value, COMMA)) != NULL) {
            *separator++ = '\0';
            update_recv_info_list(header_field_value, recv_info_list);
            header_field_value = separator;
            SKIP_WHITE_SPACE(header_field_value);
        }
        update_recv_info_list(header_field_value, recv_info_list);

        cpr_free(header_field_values);
    }
}

/*
 *  Function: ccsip_handle_info_package
 *
 *  Parameters:
 *      ccb         - the SIP CCB
 *      pSipMessage - the SIP message
 *
 *  Description:
 *      Handles an incoming unsolicited Info Package message.
 *      XXX Currently this function only handles the first part in a
 *          multi-part Info Package message.
 *
 *  Return:
 *      SIP_OK - if request processed.
 *      SIP_ERROR - if there is error
 */
int
ccsip_handle_info_package(ccsipCCB_t *ccb, sipMessage_t *pSipMessage)
{
    static const char *fname = "ccsip_handle_info_package";
    const char  *info_package;
    const char  *content_type;
    info_index_t info_index;
    type_index_t type_index;
    handler_record_t *record;
    uint16_t     status_code;
    const char  *reason_phrase;
    int          return_code = SIP_ERROR;

    /* FIXME Media Control currently does not follow the IETF draft
             draft-ietf-sip-info-events-01, so short-circuit here and
             bypass all the Info Package related stuff below. */
    // leading white spaces are trimmed, but not trailing ones
    content_type = sippmh_get_cached_header_val(pSipMessage,
                                                CONTENT_TYPE);
    if (content_type &&
        httpish_strncasecmp(content_type,
                            SIP_CONTENT_TYPE_MEDIA_CONTROL,
                            strlen(SIP_CONTENT_TYPE_MEDIA_CONTROL)) == 0) {

        media_control_info_package_handler(ccb->dn_line, ccb->gsm_id,
                                           "",  // legacy mode, no Info Package
                                           SIP_CONTENT_TYPE_MEDIA_CONTROL,
                                           pSipMessage->mesg_body[0].msgBody);

        if (sipSPISendErrorResponse(pSipMessage, 200, SIP_SUCCESS_SETUP_PHRASE,
                                    0, NULL, NULL) != TRUE) {
            CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
                              fname, SIP_SUCCESS_SETUP_PHRASE);
            return SIP_ERROR;
        }

        return SIP_OK;
    }

    /*
     * Parse the Info-Package header
     */
    // leading white spaces are trimmed, but not trailing ones
    info_package = sippmh_get_header_val(pSipMessage,
                                         SIP_HEADER_INFO_PACKAGE,
                                         NULL);

    if (info_package == NULL) {
        /* No Info-Package header */
        CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Missing Info-Package header\n",
                            DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));

        if (pSipMessage->num_body_parts == 0) {
            /* No Info-Package header, and no body poarts */
            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Missing message body\n",
                                DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
            /* Send 200 OK for legacy UA support */
            status_code = 200;
            reason_phrase = SIP_SUCCESS_SETUP_PHRASE;
            return_code = SIP_OK;
        } else {
            /* No Info-Package header, but with body part(s) */
            if (pSipMessage->num_body_parts > 1) {
                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Multipart Info Package"
                                    DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
            }

            type_index = find_type_index(pSipMessage->mesg_body[0].msgContentType);
            if (type_index == INDEX_NOT_FOUND) {
                /* No Info-Package header, and Content-Type is not supported */
                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unsupported Content Type\n",
                                    DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
                /* Send 415 Unsupported Media Type */
                status_code = SIP_CLI_ERR_MEDIA;
                reason_phrase = SIP_CLI_ERR_MEDIA_PHRASE;
            } else {
                /* No Info-Package header, but Conent-Type is supported */
                /* Send 200 OK for legacy UA support */
                status_code = 200;
                reason_phrase = SIP_SUCCESS_SETUP_PHRASE;
                return_code = SIP_OK;
            }
        }
    } else {
        /* With Info-Package header */
        if (pSipMessage->num_body_parts == 0) {
            /* With Info-Package header, but no body parts */
            CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Missing message body\n",
                                DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));

            /* ? */
            /* Send 489 Bad Event */
            status_code = SIP_CLI_ERR_BAD_EVENT;
            reason_phrase = SIP_CLI_ERR_BAD_EVENT_PHRASE;

        } else {
            /* With Info-Package header and body part(s) */
            if (pSipMessage->num_body_parts > 1) {
                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Multipart Info Package "
                                    "(only the first part is processed)\n",
                                    DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
            }

            info_index = find_info_index(info_package);
            if (info_index == INDEX_NOT_FOUND) {
                /* Info-Package is not supported */
                CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unsupported Info Package\n",
                                    DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));

                /* Send 489 Bad Event */
                status_code = SIP_CLI_ERR_BAD_EVENT;
                reason_phrase = SIP_CLI_ERR_BAD_EVENT_PHRASE;

            } else {
                /* Info-Package is supported */
                type_index = find_type_index(pSipMessage->mesg_body[0].msgContentType);
                record = find_handler_record(info_index, type_index);
                if (record == NULL) {
                    /* Info-Package header is supported, but Content-Type is not */
                    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unsupported Content Type\n",
                                        DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname));
                    /* Send 415 Unsupported Media Type */
                    status_code = SIP_CLI_ERR_MEDIA;
                    reason_phrase = SIP_CLI_ERR_MEDIA_PHRASE;
                } else {
                    /* a handler is registered */
                    (*record->handler)(ccb->dn_line, ccb->gsm_id,
                                       g_registered_info[record->info_index],
                                       s_registered_type[record->type_index],
                                       pSipMessage->mesg_body[0].msgBody);
                    /* Send 200 OK */
                    status_code = 200;
                    reason_phrase = SIP_SUCCESS_SETUP_PHRASE;
                    return_code = SIP_OK;
                }
            }
        }
    }

    if (sipSPISendErrorResponse(pSipMessage, status_code, reason_phrase,
                                0, NULL, NULL) != TRUE) {
        CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR),
                          fname, reason_phrase);
        return SIP_ERROR;
    }

    return return_code;
}

static void
media_control_info_package_handler(line_t line, callid_t call_id,
                                   const char *info_package,
                                   const char *content_type,
                                   const char *message_body)
{
}

#ifdef _CONF_ROSTER_
static void
conf_info_package_handler(line_t line, callid_t call_id,
                               const char *info_package,
                               const char *content_type,
                               const char *message_body)
{
    static const char *fname = "conf_info_package_handler";

    CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"info_package: %s content_type: %s\n",
                        DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname),
                        info_package, content_type);

    ui_info_received(line, lsm_get_ui_id(call_id), info_package, content_type,
                     message_body);
}
#endif

/*
 *  Function: ccsip_info_package_handler_init
 *
 *  Parameters:
 *      None
 *
 *  Description:
 *      Initializes the Info Package handler framework.
 *
 *  Return:
 *      SIP_OK - Info Package handler framework initialized successfully
 *      SIP_ERROR - otherwise
 */
int
ccsip_info_package_handler_init(void)
{
    static const char *fname = "ccsip_info_package_handler_init";
    info_index_t info_index;
    type_index_t type_index;

    if (s_handler_registry != NULL) {
        // Is this considered an error?
        CCSIP_DEBUG_TASK("%s: Info Package handler already initialized\n", fname);
        return SIP_OK;
    }

    /* Create the SLL */
    s_handler_registry = sll_create(is_matching_type);
    if (s_handler_registry == NULL) {
        CCSIP_DEBUG_ERROR("%s: failed to create the registry\n", fname);
        return SIP_ERROR;
    }

    for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) {
        g_registered_info[info_index] = NULL;
    }

    for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) {
        s_registered_type[type_index] = NULL;
    }

    // XXX Where is the best place to register the application-specific handler?
#ifdef _CONF_ROSTER_
    /* Register the handler for conference & x-cisco-conference Info Packages */
    ccsip_register_info_package_handler(INFO_PACKAGE_CONFERENCE,
                                        CONTENT_TYPE_CONFERENCE_INFO,
                                        conf_info_package_handler);
    ccsip_register_info_package_handler(INFO_PACKAGE_CISCO_CONFERENCE,
                                        CONTENT_TYPE_CONFERENCE_INFO,
                                        conf_info_package_handler);
#endif
    return SIP_OK;
}

/*
 *  Function: ccsip_info_package_handler_shutdown
 *
 *  Parameters:
 *      None
 *
 *  Description:
 *      Shuts down the Info Package handler framework.
 *
 *  Return:
 *      None
 */
void
ccsip_info_package_handler_shutdown(void)
{
    static const char *fname = "ccsip_info_package_handler_shutdown";
    info_index_t info_index;
    type_index_t type_index;
    handler_record_t *record;

    if (s_handler_registry == NULL) {
        // Is this considered an error?
        CCSIP_DEBUG_TASK("%s: Info Package handler was not initialized\n", fname);
        return;
    }

    for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) {
        if (s_registered_type[type_index] != NULL) {
            cpr_free(s_registered_type[type_index]);
            s_registered_type[type_index] = NULL;
        }
    }

    for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) {
        if (g_registered_info[info_index] != NULL) {
            cpr_free(g_registered_info[info_index]);
            g_registered_info[info_index] = NULL;
        }
    }

    /* Deregister each Info Package handler */
    for (record = (handler_record_t *)sll_next(s_handler_registry, NULL);
         record != NULL;
         record = (handler_record_t *)sll_next(s_handler_registry, record)) {
        cpr_free(record);
    }

    /* Destroy the SLL */
    sll_destroy(s_handler_registry);
    s_handler_registry = NULL;
}
