root/usr/src/lib/libsip/common/sip_ui.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <strings.h>
#include <pthread.h>
#include <sip.h>

#include "sip_msg.h"
#include "sip_miscdefs.h"
#include "sip_parse_uri.h"
#include "sip_xaction.h"

#define SIP_BUF_SIZE    128

/*
 * Find the header named header, consecutive calls with old_header
 * passed in will return next header of the same type.
 * If no name is passed the first header is returned. consectutive calls
 * with no name but an old header will return the next header.
 */
const struct sip_header *
sip_get_header(sip_msg_t sip_msg, char *header_name, sip_header_t old_header,
    int *error)
{
        _sip_msg_t              *_sip_msg;
        const struct sip_header *sip_hdr;

        if (error != NULL)
                *error = 0;
        if (sip_msg == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        _sip_msg = (_sip_msg_t *)sip_msg;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        sip_hdr = (sip_header_t)sip_search_for_header((_sip_msg_t *)sip_msg,
            header_name, (_sip_header_t *)old_header);
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
        if (sip_hdr == NULL && error != NULL)
                *error = EINVAL;
        return (sip_hdr);
}

/*
 * Return the request line as a string. Caller releases the returned string.
 */
char *
sip_reqline_to_str(sip_msg_t sip_msg, int *error)
{
        char    *reqstr;

        if (error != NULL)
                *error = 0;
        if (sip_msg == NULL || !sip_msg_is_request(sip_msg, error)) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        reqstr = _sip_startline_to_str((_sip_msg_t *)sip_msg, error);
        return (reqstr);
}

/*
 * Return the response line as a string. Caller releases the returned string.
 */
char *
sip_respline_to_str(sip_msg_t sip_msg, int *error)
{
        char    *respstr;

        if (error != NULL)
                *error = 0;
        if (sip_msg == NULL || sip_msg_is_request(sip_msg, error)) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        respstr = _sip_startline_to_str((_sip_msg_t *)sip_msg, error);
        return (respstr);
}

/*
 * return the first value of the header
 */
const struct sip_value *
sip_get_header_value(const struct sip_header *sip_header, int *error)
{
        _sip_header_t           *_sip_header;
        sip_parsed_header_t     *sip_parsed_header;
        int                     ret = 0;
        const struct sip_value  *value;

        if (error != NULL)
                *error = 0;
        if (sip_header == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        _sip_header = (_sip_header_t *)sip_header;
        if (_sip_header->sip_hdr_sipmsg != NULL) {
                (void) pthread_mutex_lock(
                    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
        }
        if (_sip_header->sip_header_state == SIP_HEADER_DELETED) {
                if (_sip_header->sip_hdr_sipmsg != NULL) {
                        (void) pthread_mutex_unlock(
                            &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
                }
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        ret = _sip_header->sip_header_functions->header_parse_func(
            _sip_header, &sip_parsed_header);
        if (_sip_header->sip_hdr_sipmsg != NULL) {
                (void) pthread_mutex_unlock
                    (&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
        }
        if (error != NULL)
                *error = ret;

        if (ret != 0)
                return (NULL);
        value = (sip_header_value_t)sip_parsed_header->value;
        while (value != NULL && value->value_state == SIP_VALUE_DELETED)
                value = value->next;
        if (value != NULL && value->value_state == SIP_VALUE_BAD &&
            error != NULL) {
                *error = EPROTO;
        }
        return ((sip_header_value_t)value);
}

/*
 * Return the next value of the header.
 */
const struct sip_value *
sip_get_next_value(sip_header_value_t old_value, int *error)
{
        const struct sip_value *value;

        if (error != NULL)
                *error = 0;
        if (old_value == NULL || old_value->next == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        /*
         * We never free the deleted values so no need to hold a lock.
         */
        value = (sip_header_value_t)old_value->next;
        while (value != NULL && value->value_state == SIP_VALUE_DELETED)
                value = value->next;
        if (value != NULL && value->value_state == SIP_VALUE_BAD &&
            error != NULL) {
                *error = EPROTO;
        }
        return ((sip_header_value_t)value);
}

/*
 * Given a SIP message, delete the header "header_name".
 */
int
sip_delete_header_by_name(sip_msg_t msg, char *header_name)
{
        _sip_msg_t      *_msg = (_sip_msg_t *)msg;
        sip_header_t    sip_hdr;
        _sip_header_t   *_sip_hdr;

        if (_msg == NULL || header_name == NULL)
                return (EINVAL);
        (void) pthread_mutex_lock(&_msg->sip_msg_mutex);
        if (_msg->sip_msg_cannot_be_modified) {
                (void) pthread_mutex_unlock(&_msg->sip_msg_mutex);
                return (EPERM);
        }
        sip_hdr = (sip_header_t)sip_search_for_header(_msg, header_name, NULL);
        if (sip_hdr == NULL) {
                (void) pthread_mutex_unlock(&_msg->sip_msg_mutex);
                return (EINVAL);
        }
        _sip_hdr = (_sip_header_t *)sip_hdr;
        _sip_hdr->sip_header_state = SIP_HEADER_DELETED;
        _sip_hdr->sip_hdr_sipmsg->sip_msg_len -= _sip_hdr->sip_hdr_end -
            _sip_hdr->sip_hdr_start;
        assert(_sip_hdr->sip_hdr_sipmsg->sip_msg_len >= 0);
        if (_msg->sip_msg_buf != NULL)
                _msg->sip_msg_modified = B_TRUE;
        (void) pthread_mutex_unlock(&_msg->sip_msg_mutex);

        return (0);
}

/*
 * Mark the header as deleted.
 */
int
sip_delete_header(sip_header_t sip_header)
{
        _sip_header_t   *_sip_header;

        if (sip_header == NULL)
                return (EINVAL);
        _sip_header = (_sip_header_t *)sip_header;
        (void) pthread_mutex_lock(&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
        if (_sip_header->sip_hdr_sipmsg->sip_msg_cannot_be_modified) {
                (void) pthread_mutex_unlock
                    (&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
                return (EPERM);
        }
        if (_sip_header->sip_header_state == SIP_HEADER_DELETED) {
                (void) pthread_mutex_unlock(
                    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
                return (EINVAL);
        }
        _sip_header->sip_header_state = SIP_HEADER_DELETED;
        _sip_header->sip_hdr_sipmsg->sip_msg_len -= _sip_header->sip_hdr_end -
            _sip_header->sip_hdr_start;
        assert(_sip_header->sip_hdr_sipmsg->sip_msg_len >= 0);
        if (_sip_header->sip_hdr_sipmsg->sip_msg_buf != NULL)
                _sip_header->sip_hdr_sipmsg->sip_msg_modified = B_TRUE;
        (void) pthread_mutex_unlock
            (&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
        return (0);
}

/*
 * Mark the value as deleted.
 */
int
sip_delete_value(sip_header_t sip_header, sip_header_value_t sip_header_value)
{
        _sip_header_t   *_sip_header;
        sip_value_t     *_sip_header_value;
        int             vlen;
        char            *c;

        if (sip_header == NULL || sip_header_value == NULL)
                return (EINVAL);
        _sip_header = (_sip_header_t *)sip_header;
        (void) pthread_mutex_lock(&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
        if (_sip_header->sip_hdr_sipmsg->sip_msg_cannot_be_modified) {
                (void) pthread_mutex_unlock(&_sip_header->
                    sip_hdr_sipmsg->sip_msg_mutex);
                return (EPERM);
        }
        if (_sip_header->sip_header_state == SIP_HEADER_DELETED) {
                (void) pthread_mutex_unlock(
                    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
                return (EINVAL);
        }
        _sip_header_value = (sip_value_t *)sip_header_value;
        if (_sip_header_value->value_state == SIP_VALUE_DELETED) {
                (void) pthread_mutex_unlock(
                    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
                return (EINVAL);
        }
        _sip_header->sip_header_state = SIP_HEADER_DELETED_VAL;
        _sip_header_value->value_state = SIP_VALUE_DELETED;
        vlen = _sip_header_value->value_end - _sip_header_value->value_start;
        if (_sip_header->sip_hdr_parsed->value == _sip_header_value) {
                c = _sip_header_value->value_start;
                while (*c-- != SIP_HCOLON)
                        vlen++;
        } else {
                c = _sip_header_value->value_start;
                while (*c-- != SIP_COMMA)
                        vlen++;
        }
        if (_sip_header_value->next == NULL) {
                sip_value_t     *value = _sip_header->sip_hdr_parsed->value;
                boolean_t       crlf_present =  B_FALSE;
                char            *s;

                while (value != NULL && value != _sip_header_value) {
                        crlf_present = B_FALSE;

                        if (value->value_state == SIP_VALUE_DELETED) {
                                value = value->next;
                                continue;
                        }
                        s = value->value_end;
                        while (s != value->value_start) {
                                if (*s == '\r' && strncmp(s, SIP_CRLF,
                                    strlen(SIP_CRLF)) == 0) {
                                        crlf_present = B_TRUE;
                                        break;
                                }
                                s--;
                        }
                        value = value->next;
                }
                if (!crlf_present) {
                        c = _sip_header_value->value_end;
                        while (*c-- != '\r')
                                vlen--;
                        assert(vlen > 0);
                }
        }
        _sip_header->sip_hdr_sipmsg->sip_msg_len -= vlen;
        if (_sip_header->sip_hdr_sipmsg->sip_msg_buf != NULL)
                _sip_header->sip_hdr_sipmsg->sip_msg_modified = B_TRUE;
        (void) pthread_mutex_unlock
            (&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
        return (0);
}

/*
 * Given a param list, check if a param name exists.
 */
boolean_t
sip_is_param_present(const sip_param_t *param_list, char *param_name,
    int param_len)
{
        const sip_param_t       *param = param_list;

        while (param != NULL) {
                if (param->param_name.sip_str_len == param_len &&
                    strncasecmp(param->param_name.sip_str_ptr, param_name,
                        param_len) == 0) {
                        return (B_TRUE);
                }
                param = param->param_next;
        }
        return (B_FALSE);
}


/*
 * Given a value header return the value of the named param.
 */
const sip_str_t *
sip_get_param_value(sip_header_value_t header_value, char *param_name,
    int *error)
{
        sip_value_t     *_sip_header_value;
        sip_param_t     *sip_param;

        if (error != NULL)
                *error = 0;
        if (header_value == NULL || param_name == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        _sip_header_value = (sip_value_t *)header_value;
        if (_sip_header_value->value_state == SIP_VALUE_DELETED) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        if (_sip_header_value->param_list == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        sip_param = sip_get_param_from_list(_sip_header_value->param_list,
            param_name);
        if (sip_param != NULL)
                return (&sip_param->param_value);
        return (NULL);
}

/*
 * Return the list of params in the header
 */
const sip_param_t *
sip_get_params(sip_header_value_t header_value, int *error)
{
        sip_value_t     *sip_header_value;

        if (error != NULL)
                *error = 0;
        if (header_value == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        sip_header_value = (sip_value_t *)header_value;
        if (sip_header_value->value_state == SIP_VALUE_DELETED) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        return (sip_header_value->param_list);
}

/*
 * Return true if this is a SIP request
 */
boolean_t
sip_msg_is_request(sip_msg_t sip_msg, int *error)
{
        _sip_msg_t              *_sip_msg;
        sip_message_type_t      *sip_msg_info;
        boolean_t               ret;

        if (error != NULL)
                *error = 0;
        if (sip_msg == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (B_FALSE);
        }
        _sip_msg = (_sip_msg_t *)sip_msg;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        if (_sip_msg->sip_msg_req_res == NULL) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                if (error != NULL)
                        *error = EINVAL;
                return (B_FALSE);
        }
        sip_msg_info = _sip_msg->sip_msg_req_res;
        ret = sip_msg_info->is_request;
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
        return (ret);
}

/*
 * Return true if this is a SIP response
 */
boolean_t
sip_msg_is_response(sip_msg_t sip_msg, int *error)
{
        boolean_t               is_resp;
        _sip_msg_t              *_sip_msg;
        sip_message_type_t      *sip_msg_info;

        if (error != NULL)
                *error = 0;
        if (sip_msg == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (B_FALSE);
        }
        _sip_msg = (_sip_msg_t *)sip_msg;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        if (_sip_msg->sip_msg_req_res == NULL) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                if (error != NULL)
                        *error = EINVAL;
                return (B_FALSE);
        }
        sip_msg_info = _sip_msg->sip_msg_req_res;
        is_resp = !sip_msg_info->is_request;
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
        return (is_resp);
}

/*
 * Return the method in the request line
 */
sip_method_t
sip_get_request_method(sip_msg_t sip_msg, int *error)
{
        _sip_msg_t              *_sip_msg;
        sip_message_type_t      *sip_msg_info;
        sip_method_t            ret = -1;

        if (error != NULL)
                *error = 0;
        if (sip_msg == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (ret);
        }
        _sip_msg = (_sip_msg_t *)sip_msg;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        sip_msg_info = _sip_msg->sip_msg_req_res;
        if (_sip_msg->sip_msg_req_res == NULL) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                if (error != NULL)
                        *error = EINVAL;
                return (ret);
        }
        if (sip_msg_info->is_request)
                ret = sip_msg_info->sip_req_method;
        else if (error != NULL)
                *error = EINVAL;
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
        return (ret);
}

/*
 * Return the URI from the request line
 */
const sip_str_t *
sip_get_request_uri_str(sip_msg_t sip_msg, int *error)
{
        _sip_msg_t              *_sip_msg;
        sip_message_type_t      *sip_msg_info;
        sip_str_t               *ret = NULL;
        struct sip_uri          *parsed_uri;

        if (error != NULL)
                *error = 0;
        if (sip_msg == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        _sip_msg = (_sip_msg_t *)sip_msg;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        if (_sip_msg->sip_msg_req_res == NULL) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        sip_msg_info = _sip_msg->sip_msg_req_res;
        if (sip_msg_info->is_request)
                ret = &sip_msg_info->sip_req_uri;
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);

        /*
         * If the error is required, check the validity of the URI via
         * sip_uri_parse().
         */
        if (error != NULL) {
                parsed_uri = sip_parse_uri(ret, error);
                if (parsed_uri != NULL)
                        sip_free_parsed_uri((sip_uri_t)parsed_uri);
        }
        return (ret);
}

/*
 * Return the response code
 */
int
sip_get_response_code(sip_msg_t sip_msg, int *error)
{
        _sip_msg_t              *_sip_msg;
        sip_message_type_t      *sip_msg_info;
        int                     ret = -1;

        if (error != NULL)
                *error = 0;
        if (sip_msg == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (ret);
        }
        _sip_msg = (_sip_msg_t *)sip_msg;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        if (_sip_msg->sip_msg_req_res == NULL) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                if (error != NULL)
                        *error = EINVAL;
                return (ret);
        }
        sip_msg_info = _sip_msg->sip_msg_req_res;
        if (!sip_msg_info->is_request)
                ret = sip_msg_info->sip_resp_code;
        else if (error != NULL)
                *error = EINVAL;
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
        return (ret);
}

/*
 * Get the response phrase
 */
const sip_str_t *
sip_get_response_phrase(sip_msg_t sip_msg, int *error)
{
        _sip_msg_t              *_sip_msg;
        sip_message_type_t      *sip_msg_info;
        sip_str_t               *ret = NULL;

        if (error != NULL)
                *error = 0;
        if (sip_msg == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (ret);
        }
        _sip_msg = (_sip_msg_t *)sip_msg;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        if (_sip_msg->sip_msg_req_res == NULL) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                if (error != NULL)
                        *error = EINVAL;
                return (ret);
        }
        sip_msg_info = _sip_msg->sip_msg_req_res;
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
        if (!sip_msg_info->is_request) {
                if (sip_msg_info->sip_resp_phrase_len == 0)
                        ret = NULL;
                else
                        ret = &sip_msg_info->sip_resp_phrase;
        } else if (error != NULL) {
                *error = EINVAL;
        }
        return (ret);
}

/*
 * Get the SIP version string
 */
const sip_str_t *
sip_get_sip_version(sip_msg_t sip_msg, int *error)
{
        _sip_msg_t              *_sip_msg;
        sip_message_type_t      *sip_msg_info;
        sip_str_t               *ret = NULL;

        if (error != NULL)
                *error = 0;
        if (sip_msg == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (ret);
        }
        _sip_msg = (_sip_msg_t *)sip_msg;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        if (_sip_msg->sip_msg_req_res == NULL) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                if (error != NULL)
                        *error = EINVAL;
                return (ret);
        }
        sip_msg_info = _sip_msg->sip_msg_req_res;
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
        ret = &sip_msg_info->sip_proto_version.version;
        return (ret);
}

/*
 * Return the length of the SIP message
 */
int
sip_get_msg_len(sip_msg_t sip_msg, int *error)
{
        _sip_msg_t      *_sip_msg;

        if (error != NULL)
                *error = 0;
        if (sip_msg == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (-1);
        }
        _sip_msg = (_sip_msg_t *)sip_msg;

        return (_sip_msg->sip_msg_len);
}

/*
 * Get content as a string. Caller frees the string
 */
char *
sip_get_content(sip_msg_t sip_msg, int *error)
{
        _sip_msg_t      *_sip_msg;
        sip_content_t   *sip_content;
        char            *content;
        int             len;
        char            *p;

        if (error != NULL)
                *error = 0;

        if (sip_msg == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        _sip_msg = (_sip_msg_t *)sip_msg;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        if (_sip_msg->sip_msg_content == NULL) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        content = malloc(_sip_msg->sip_msg_content_len + 1);
        if (content == NULL) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                if (error != NULL)
                        *error = ENOMEM;
                return (NULL);
        }
        p = content;
        sip_content = _sip_msg->sip_msg_content;
        while (sip_content != NULL) {
                len =  sip_content->sip_content_end -
                    sip_content->sip_content_start;
                (void) strncpy(p, sip_content->sip_content_start, len);
                p += len;
                sip_content = sip_content->sip_content_next;
        }
        content[_sip_msg->sip_msg_content_len] = '\0';
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
        return (content);
}

/*
 * copy sip_header with param, if any, to sip_msg
 */
int
sip_copy_header(sip_msg_t sip_msg, sip_header_t sip_header, char *param)
{
        _sip_msg_t      *_sip_msg;
        _sip_header_t   *_sip_header;
        int             ret;

        if (sip_msg == NULL || sip_header == NULL)
                return (EINVAL);
        _sip_msg = (_sip_msg_t *)sip_msg;
        _sip_header = (_sip_header_t *)sip_header;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        if (_sip_msg->sip_msg_cannot_be_modified) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                return (EPERM);
        }
        if (_sip_header->sip_header_state == SIP_HEADER_DELETED) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                return (EINVAL);
        }

        ret = _sip_copy_header(_sip_msg, _sip_header, param, B_TRUE);
        if (_sip_msg->sip_msg_buf != NULL)
                _sip_msg->sip_msg_modified = B_TRUE;
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
        return (ret);
}

/*
 * copy the header specified by header_name, with param, if any
 */
int
sip_copy_header_by_name(sip_msg_t old_msg, sip_msg_t new_msg,
    char *header_name, char *param)
{
        int             ret;
        _sip_msg_t      *_old_msg = (_sip_msg_t *)old_msg;
        _sip_msg_t      *_new_msg = (_sip_msg_t *)new_msg;

        if (_old_msg == NULL || _new_msg == NULL || header_name == NULL ||
            _old_msg == _new_msg) {
                return (EINVAL);
        }
        (void) pthread_mutex_lock(&_new_msg->sip_msg_mutex);
        if (_new_msg->sip_msg_cannot_be_modified) {
                (void) pthread_mutex_unlock(&_new_msg->sip_msg_mutex);
                return (EPERM);
        }

        (void) pthread_mutex_lock(&_old_msg->sip_msg_mutex);
        ret = _sip_find_and_copy_header(_old_msg, _new_msg, header_name, param,
            B_FALSE);
        (void) pthread_mutex_unlock(&_old_msg->sip_msg_mutex);
        if (_new_msg->sip_msg_buf != NULL)
                _new_msg->sip_msg_modified = B_TRUE;
        (void) pthread_mutex_unlock(&_new_msg->sip_msg_mutex);
        return (ret);
}

/*
 * add the given header to sip_message
 */
int
sip_add_header(sip_msg_t sip_msg, char *header_string)
{
        int             header_size;
        _sip_header_t   *new_header;
        _sip_msg_t      *_sip_msg;

        if (sip_msg == NULL || header_string == NULL)
                return (EINVAL);
        _sip_msg = (_sip_msg_t *)sip_msg;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        if (_sip_msg->sip_msg_cannot_be_modified) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                return (EPERM);
        }
        header_size = strlen(header_string) + strlen(SIP_CRLF);
        new_header = sip_new_header(header_size);
        if (new_header == NULL) {
                (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
                return (ENOMEM);
        }

        (void) snprintf(new_header->sip_hdr_start, header_size + 1, "%s%s",
            header_string, SIP_CRLF);
        _sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
        if (_sip_msg->sip_msg_buf != NULL)
                _sip_msg->sip_msg_modified = B_TRUE;
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);
        return (0);
}

/*
 * add the given param to the sip_header. create a new header with the param
 * and mark the old header as deleted.
 */
sip_header_t
sip_add_param(sip_header_t sip_header, char *param, int *error)
{
        _sip_header_t   *_sip_header;
        _sip_header_t   *new_header;
        int             hdrlen;
        _sip_msg_t      *_sip_msg;
        int             param_len;
        char            *tmp_ptr;

        if (error != NULL)
                *error = 0;

        if (param == NULL || sip_header == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }

        _sip_header = (_sip_header_t *)sip_header;

        (void) pthread_mutex_lock(&_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
        if (_sip_header->sip_hdr_sipmsg->sip_msg_cannot_be_modified) {
                if (error != NULL)
                        *error = EPERM;
                (void) pthread_mutex_unlock(
                    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
                return (NULL);
        }
        if (_sip_header->sip_header_state == SIP_HEADER_DELETED) {
                if (error != NULL)
                        *error = EINVAL;
                (void) pthread_mutex_unlock(
                    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
                return (NULL);
        }

        param_len = SIP_SPACE_LEN + sizeof (char) + SIP_SPACE_LEN +
            strlen(param);
        hdrlen = _sip_header->sip_hdr_end - _sip_header->sip_hdr_start;
        new_header = sip_new_header(hdrlen + param_len);
        if (new_header == NULL) {
                if (error != NULL)
                        *error = ENOMEM;
                (void) pthread_mutex_unlock(
                    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
                return (NULL);
        }
        (void) memcpy(new_header->sip_hdr_start, _sip_header->sip_hdr_start,
            hdrlen);
        new_header->sip_hdr_end = new_header->sip_hdr_start + hdrlen;
        hdrlen = param_len + 1;
        /*
         * Find CRLF
         */
        tmp_ptr = new_header->sip_hdr_end;
        while (*tmp_ptr-- != '\n') {
                hdrlen++;
                if (tmp_ptr == new_header->sip_hdr_start) {
                        sip_free_header(new_header);
                        if (error != NULL)
                                *error = EINVAL;
                        (void) pthread_mutex_unlock(
                            &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
                        return (NULL);
                }
        }
        (void) snprintf(tmp_ptr, hdrlen + 1,
            " %c %s%s", SIP_SEMI, param, SIP_CRLF);
        new_header->sip_hdr_end += param_len;
        new_header->sip_header_functions = _sip_header->sip_header_functions;
        _sip_msg = _sip_header->sip_hdr_sipmsg;
        _sip_add_header(_sip_msg, new_header, B_TRUE, B_FALSE, NULL);
        if (_sip_header->sip_hdr_sipmsg->sip_msg_buf != NULL)
                _sip_header->sip_hdr_sipmsg->sip_msg_modified = B_TRUE;
        (void) pthread_mutex_unlock(&new_header->sip_hdr_sipmsg->sip_msg_mutex);
        (void) sip_delete_header(sip_header);
        return ((sip_header_t)new_header);
}

/*
 * Get Request URI
 */
const struct sip_uri *
sip_get_request_uri(sip_msg_t sip_msg, int *error)
{
        _sip_msg_t              *_sip_msg;
        sip_message_type_t      *sip_msg_info;
        const struct sip_uri    *ret = NULL;

        if (error != NULL)
                *error = 0;

        if (sip_msg == NULL) {
                if (error != NULL)
                        *error = EINVAL;
                return (NULL);
        }
        _sip_msg = (_sip_msg_t *)sip_msg;
        (void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
        sip_msg_info = _sip_msg->sip_msg_req_res;
        if (sip_msg_info != NULL && sip_msg_info->is_request) {
                ret = sip_msg_info->sip_req_parse_uri;
        } else {
                if (error != NULL)
                        *error = EINVAL;
        }
        (void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);

        if (ret != NULL) {
                if (ret->sip_uri_scheme.sip_str_len == 0 ||
                    ret->sip_uri_scheme.sip_str_ptr == NULL) {
                        ret = NULL;
                        if (error != NULL)
                                *error = EINVAL;
                } else if (ret->sip_uri_errflags != 0 && error != NULL) {
                        *error = EINVAL;
                }
        }
        return ((sip_uri_t)ret);
}

/*
 * returns a comma separated string of all the sent-by values registered by
 * the UA.
 */
char *
sip_sent_by_to_str(int *error)
{
        sent_by_list_t  *sb;
        int             sb_len = 0;
        int             slen;
        char            *sb_str;
        char            *p;
        int             count = 0;
        int             cnt = 0;

        if (error != NULL)
                *error = 0;

        (void) pthread_mutex_lock(&sip_sent_by_lock);
        if (sip_sent_by == NULL) {
                (void) pthread_mutex_unlock(&sip_sent_by_lock);
                return (NULL);
        }
        sb = sip_sent_by;
        for (cnt = 0; cnt < sip_sent_by_count; cnt++) {
                sb_len += strlen(sb->sb_val);
                sb = sb->sb_next;
        }
        /*
         * for the commas
         */
        sb_len += sip_sent_by_count - 1;
        sb_str = malloc(sb_len + 1);
        if (sb_str == NULL) {
                if (error != NULL)
                        *error = ENOMEM;
                (void) pthread_mutex_unlock(&sip_sent_by_lock);
                return (NULL);
        }
        sb = sip_sent_by;
        p = sb_str;
        slen = sb_len + 1;
        for (cnt = 0; cnt < sip_sent_by_count; cnt++) {
                if (cnt == 0) {
                        count = snprintf(p, slen, "%s", sb->sb_val);
                } else {
                        count = snprintf(p, slen, "%c%s", SIP_COMMA,
                            sb->sb_val);
                }
                p += count;
                slen -= count;
                sb = sb->sb_next;
        }
        sb_str[sb_len] = '\0';
        (void) pthread_mutex_unlock(&sip_sent_by_lock);
        return (sb_str);
}

/*
 * A comma separated list of sent-by values.
 */
int
sip_register_sent_by(char *val)
{
        sent_by_list_t  *sb = NULL;
        sent_by_list_t  *sb_tail = NULL;
        char            *str;
        int             count = 0;

        if (val == NULL)
                return (EINVAL);
        str = strtok(val, ",");
        while (str != NULL) {
                int     slen;
                char    *start = str;
                char    *end = str + strlen(str) - 1;

                while (isspace(*start))
                        start++;
                while (isspace(*end))
                        end--;
                if (end <= start)
                        goto err_ret;
                slen = end - start + 1;
                sb_tail = (sent_by_list_t *)malloc(sizeof (*sb_tail));
                if (sb_tail == NULL)
                        goto err_ret;
                sb_tail->sb_next = sb_tail->sb_prev = NULL;
                if ((sb_tail->sb_val = (char *)malloc(slen + 1)) == NULL) {
                        free(sb_tail);
                        goto err_ret;
                }
                (void) strncpy(sb_tail->sb_val, start, slen);
                sb_tail->sb_val[slen] = '\0';
                if (sb == NULL) {
                        sb = sb_tail;
                } else {
                        sb_tail->sb_next = sb;
                        sb->sb_prev = sb_tail;
                        sb = sb_tail;
                }
                count++;
                str = strtok(NULL, ",");
        }
        sb_tail = sb;
        while (sb_tail->sb_next != NULL)
                sb_tail = sb_tail->sb_next;
        (void) pthread_mutex_lock(&sip_sent_by_lock);
        if (sip_sent_by != NULL) {
                sb_tail->sb_next = sip_sent_by;
                sip_sent_by->sb_prev = sb_tail;
        }
        sip_sent_by = sb;
        sip_sent_by_count += count;
        (void) pthread_mutex_unlock(&sip_sent_by_lock);
        return (0);
err_ret:
        sb_tail = sb;
        for (; count > 0; count--) {
                sb = sb_tail->sb_next;
                free(sb_tail->sb_val);
                sb_tail->sb_next = NULL;
                sb_tail->sb_prev = NULL;
                free(sb_tail);
                sb_tail = sb;
        }
        return (EINVAL);
}

/*
 * Un-register sent-by values; 'val' contains a comma separated list
 */
void
sip_unregister_sent_by(char *val)
{
        sent_by_list_t  *sb;
        char            *str;
        int             count = 0;

        (void) pthread_mutex_lock(&sip_sent_by_lock);
        str = strtok(val, ",");
        while (str != NULL) {
                sb = sip_sent_by;
                for (count = 0; count < sip_sent_by_count; count++) {
                        if (strncmp(sb->sb_val, str, strlen(str)) == 0) {
                                if (sb == sip_sent_by) {
                                        if (sb->sb_next != NULL)
                                                sip_sent_by = sb->sb_next;
                                        else
                                                sip_sent_by = NULL;
                                } else if (sb->sb_next == NULL) {
                                        sb->sb_prev->sb_next = NULL;
                                } else {
                                        sb->sb_prev->sb_next = sb->sb_next;
                                        sb->sb_next->sb_prev = sb->sb_prev;
                                }
                                sip_sent_by_count--;
                                sb->sb_next = NULL;
                                sb->sb_prev = NULL;
                                free(sb->sb_val);
                                free(sb);
                                break;
                        }
                        sb = sb->sb_next;
                }
                str = strtok(NULL, ",");
        }
        (void) pthread_mutex_unlock(&sip_sent_by_lock);
}

/*
 * Un-register all the sent-by values
 */
void
sip_unregister_all_sent_by()
{
        sent_by_list_t  *sb;
        int             count;

        (void) pthread_mutex_lock(&sip_sent_by_lock);
        sb = sip_sent_by;
        for (count = 0; count < sip_sent_by_count; count++) {
                sip_sent_by = sb->sb_next;
                free(sb->sb_val);
                sb->sb_next = NULL;
                sb->sb_prev = NULL;
                free(sb);
                sb = sip_sent_by;
        }
        sip_sent_by = NULL;
        sip_sent_by_count = 0;
        (void) pthread_mutex_unlock(&sip_sent_by_lock);
}

/*
 * Given a response code, return the corresponding phrase
 */
char *
sip_get_resp_desc(int resp_code)
{
        switch (resp_code) {
        case SIP_TRYING:
                return ("TRYING");
        case SIP_RINGING:
                return ("RINGING");
        case SIP_CALL_IS_BEING_FORWARDED:
                return ("CALL_IS_BEING_FORWARDED");
        case SIP_QUEUED:
                return ("QUEUED");
        case SIP_SESSION_PROGRESS:
                return ("SESSION_PROGRESS");
        case SIP_OK:
                return ("OK");
        case SIP_ACCEPTED:
                return ("ACCEPTED");
        case SIP_MULTIPLE_CHOICES:
                return ("MULTIPLE_CHOICES");
        case SIP_MOVED_PERMANENTLY:
                return ("MOVED_PERMANENTLY");
        case SIP_MOVED_TEMPORARILY:
                return ("MOVED_TEMPORARILY");
        case SIP_USE_PROXY:
                return ("USE_PROXY");
        case SIP_ALTERNATIVE_SERVICE:
                return ("ALTERNATIVE_SERVICE");
        case SIP_BAD_REQUEST:
                return ("BAD_REQUEST");
        case SIP_UNAUTHORIZED:
                return ("UNAUTHORIZED");
        case SIP_PAYMENT_REQUIRED:
                return ("PAYMENT_REQUIRED");
        case SIP_FORBIDDEN:
                return ("FORBIDDEN");
        case SIP_NOT_FOUND:
                return ("NOT_FOUND");
        case SIP_METHOD_NOT_ALLOWED:
                return ("METHOD_NOT_ALLOWED");
        case SIP_NOT_ACCEPTABLE:
                return ("NOT_ACCEPTABLE");
        case SIP_PROXY_AUTH_REQUIRED:
                return ("PROXY_AUTH_REQUIRED");
        case SIP_REQUEST_TIMEOUT:
                return ("REQUEST_TIMEOUT");
        case SIP_GONE:
                return ("GONE");
        case SIP_REQUEST_ENTITY_2_LARGE:
                return ("REQUEST_ENTITY_2_LARGE");
        case SIP_REQUEST_URI_2_LONG:
                return ("REQUEST_URI_2_LONG");
        case SIP_UNSUPPORTED_MEDIA_TYPE:
                return ("UNSUPPORTED_MEDIA_TYPE");
        case SIP_UNSUPPORTED_URI_SCHEME:
                return ("UNSUPPORTED_URI_SCHEME");
        case SIP_BAD_EXTENSION:
                return ("BAD_EXTENSION");
        case SIP_EXTENSION_REQUIRED:
                return ("EXTENSION_REQUIRED");
        case SIP_INTERVAL_2_BRIEF:
                return ("INTERVAL_2_BRIEF");
        case SIP_TEMPORARILY_UNAVAIL:
                return ("TEMPORARILY_UNAVAIL");
        case SIP_CALL_NON_EXISTANT:
                return ("CALL_NON_EXISTANT");
        case SIP_LOOP_DETECTED:
                return ("LOOP_DETECTED");
        case SIP_TOO_MANY_HOOPS:
                return ("TOO_MANY_HOOPS");
        case SIP_ADDRESS_INCOMPLETE:
                return ("ADDRESS_INCOMPLETE");
        case SIP_AMBIGUOUS:
                return ("AMBIGUOUS");
        case SIP_BUSY_HERE:
                return ("BUSY_HERE");
        case SIP_REQUEST_TERMINATED:
                return ("REQUEST_TERMINATED");
        case SIP_NOT_ACCEPTABLE_HERE:
                return ("NOT_ACCEPTABLE_HERE");
        case SIP_BAD_EVENT:
                return ("BAD_EVENT");
        case SIP_REQUEST_PENDING:
                return ("REQUEST_PENDING");
        case SIP_UNDECIPHERABLE:
                return ("UNDECIPHERABLE");
        case SIP_SERVER_INTERNAL_ERROR:
                return ("SERVER_INTERNAL_ERROR");
        case SIP_NOT_IMPLEMENTED:
                return ("NOT_IMPLEMENTED");
        case SIP_BAD_GATEWAY:
                return ("BAD_GATEWAY");
        case SIP_SERVICE_UNAVAILABLE:
                return ("SERVICE_UNAVAILABLE");
        case SIP_SERVER_TIMEOUT:
                return ("SERVER_TIMEOUT");
        case SIP_VERSION_NOT_SUPPORTED:
                return ("VERSION_NOT_SUPPORTED");
        case SIP_MESSAGE_2_LARGE:
                return ("MESSAGE_2_LARGE");
        case SIP_BUSY_EVERYWHERE:
                return ("BUSY_EVERYWHERE");
        case SIP_DECLINE:
                return ("DECLINE");
        case SIP_DOES_NOT_EXIST_ANYWHERE:
                return ("DOES_NOT_EXIST_ANYWHERE");
        case SIP_NOT_ACCEPTABLE_ANYWHERE:
                return ("NOT_ACCEPTABLE_ANYWHERE");
        default:
                return ("UNKNOWN");
        }
}

/*
 * The following three fns initialize and destroy the private library
 * data in sip_conn_object_t. The assumption is that the 1st member
 * of sip_conn_object_t is reserved for library use. The private data
 * is used only for byte-stream protocols such as TCP to accumulate
 * a complete SIP message, based on the CONTENT-LENGTH value, before
 * processing it.
 */
int
sip_init_conn_object(sip_conn_object_t obj)
{
        void                    **obj_val;
        sip_conn_obj_pvt_t      *pvt_data;

        if (obj == NULL)
                return (EINVAL);
        pvt_data =  malloc(sizeof (sip_conn_obj_pvt_t));
        if (pvt_data == NULL)
                return (ENOMEM);
        pvt_data->sip_conn_obj_cache = NULL;
        pvt_data->sip_conn_obj_reass = malloc(sizeof (sip_reass_entry_t));
        if (pvt_data->sip_conn_obj_reass == NULL) {
                free(pvt_data);
                return (ENOMEM);
        }
        bzero(pvt_data->sip_conn_obj_reass, sizeof (sip_reass_entry_t));
        (void) pthread_mutex_init(&pvt_data->sip_conn_obj_reass_lock, NULL);
        (void) pthread_mutex_init(&pvt_data->sip_conn_obj_cache_lock, NULL);
        sip_refhold_conn(obj);
        obj_val = (void *)obj;
        *obj_val = (void *)pvt_data;

        return (0);
}

/*
 * Clear private date, if any
 */
void
sip_clear_stale_data(sip_conn_object_t obj)
{
        void                    **obj_val;
        sip_conn_obj_pvt_t      *pvt_data;
        sip_reass_entry_t       *reass;

        if (obj == NULL)
                return;
        obj_val = (void *)obj;
        pvt_data = (sip_conn_obj_pvt_t *)*obj_val;
        (void) pthread_mutex_lock(&pvt_data->sip_conn_obj_reass_lock);
        reass = pvt_data->sip_conn_obj_reass;
        if (reass->sip_reass_msg != NULL) {
                assert(reass->sip_reass_msglen > 0);
                free(reass->sip_reass_msg);
                reass->sip_reass_msglen = 0;
        }
        assert(reass->sip_reass_msglen == 0);
        (void) pthread_mutex_unlock(&pvt_data->sip_conn_obj_reass_lock);
}

/*
 * Walk through all the transactions, remove if this obj has been cached
 * by any.
 */
void
sip_conn_destroyed(sip_conn_object_t obj)
{
        void                    **obj_val;
        sip_conn_obj_pvt_t      *pvt_data;

        if (obj == NULL)
                return;
        obj_val = (void *)obj;
        pvt_data = (sip_conn_obj_pvt_t *)*obj_val;

        sip_clear_stale_data(obj);
        free(pvt_data->sip_conn_obj_reass);
        pvt_data->sip_conn_obj_reass = NULL;
        (void) pthread_mutex_destroy(&pvt_data->sip_conn_obj_reass_lock);

        sip_del_conn_obj_cache(obj, NULL);
        (void) pthread_mutex_destroy(&pvt_data->sip_conn_obj_cache_lock);

        free(pvt_data);
        *obj_val = NULL;
        sip_refrele_conn(obj);
}