OpenSolaris_b135/lib/libsip/common/sip_headers.c

Compare this file to the similar file:
Show the results in this format:

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

#pragma ident	"%Z%%M%	%I%	%E% SMI"

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

#include "sip_msg.h"
#include "sip_miscdefs.h"
#include "sip_parse_generic.h"

sip_methods_t sip_methods[MAX_SIP_METHODS] = {
	{"UNKNOWN", 7},
	{"INVITE", 6},
	{"ACK", 3},
	{"OPTIONS", 7},
	{"BYE", 3},
	{"CANCEL", 6},
	{"REGISTER", 8},
	{"REFER", 5},
	{"INFO", 4},
	{"SUBSCRIBE", 9},
	{"NOTIFY", 6},
	{"PRACK", 5}
};

/*
 * Built-In Header function table
 */
sip_header_function_t sip_header_function_table[] = {
	{"Unknown", NULL, sip_parse_unknown_header, NULL, NULL, NULL},
	{"CONTACT", "m", sip_parse_cftr_header, NULL, NULL,
	sip_free_cftr_header},
	{"FROM", "F", sip_parse_cftr_header, NULL, NULL, sip_free_cftr_header},
	{"TO", "T", sip_parse_cftr_header, NULL, NULL, sip_free_cftr_header},
	{"CONTENT-LENGTH", "l", sip_parse_clen_header, NULL, NULL,
	sip_free_phdr},
	{"CONTENT-TYPE", "c", sip_parse_ctype_header, NULL, NULL,
	sip_free_phdr},
	{"CALL-ID", "i", sip_parse_cid_header, NULL, NULL, sip_free_phdr},
	{"CSEQ", NULL, sip_parse_cseq_header, NULL, NULL, sip_free_phdr},
	{"VIA", "v", sip_parse_via_header, NULL, NULL, sip_free_phdr},
	{"Max-Forwards", NULL, sip_parse_maxf_header, NULL, NULL,
	sip_free_phdr},
	{"RECORD-ROUTE", NULL, sip_parse_cftr_header, NULL, NULL,
	sip_free_cftr_header},
	{"ROUTE", NULL, sip_parse_cftr_header, NULL, NULL,
	sip_free_cftr_header},
	{"ACCEPT", NULL, sip_parse_acpt_header, NULL, NULL, sip_free_phdr},
	{"ACCEPT-ENCODING", NULL, sip_parse_acpt_encode_header, NULL, NULL,
	sip_free_phdr},
	{"ACCEPT-LANGUAGE", NULL, sip_parse_acpt_lang_header, NULL, NULL,
	sip_free_phdr},
	{"ALERT-INFO", NULL, sip_parse_alert_header, NULL, NULL, sip_free_phdr},
	{"ALLOW", NULL, sip_parse_allow_header, NULL, NULL, sip_free_phdr},
	{"CALL-INFO", NULL, sip_parse_callinfo_header, NULL, NULL,
	sip_free_phdr},
	{"CONTENT-DISPOSITION", NULL, sip_parse_contentdis_header, NULL, NULL,
	sip_free_phdr},
	{"CONTENT-ENCODING", "e", sip_parse_contentencode_header, NULL, NULL,
	sip_free_phdr},
	{"CONTENT-LANGUAGE", NULL, sip_parse_contentlang_header, NULL, NULL,
	sip_free_phdr},
	{"DATE", NULL, sip_parse_date_header, NULL, NULL, sip_free_phdr},
	{"ERROR-INFO", NULL, sip_parse_errorinfo_header, NULL, NULL,
	sip_free_phdr},
	{"EXPIRES", NULL, sip_parse_expire_header, NULL, NULL, sip_free_phdr},
	{"IN-REPLY-TO", NULL, sip_parse_inreplyto_header, NULL, NULL,
	sip_free_phdr},
	{"MIN-EXPIRES", NULL, sip_parse_minexpire_header, NULL, NULL,
	sip_free_phdr},
	{"MIME-VERSION", NULL, sip_parse_mimeversion_header, NULL, NULL,
	sip_free_phdr},
	{"ORGANIZATION", NULL, sip_parse_org_header, NULL, NULL, sip_free_phdr},
	{"PRIORITY", NULL, sip_parse_priority_header, NULL, NULL,
	sip_free_phdr},
	{"REQUIRE", NULL, sip_parse_require_header, NULL, NULL, sip_free_phdr},
	{"REPLY-TO", NULL, sip_parse_replyto_header, NULL, NULL, sip_free_phdr},
	{"RETRY-AFTER", NULL, sip_parse_retryaft_header, NULL, NULL,
	sip_free_phdr},
	{"SERVER", NULL, sip_parse_server_header, NULL, NULL, sip_free_phdr},
	{"SUBJECT", "s", sip_parse_subject_header, NULL, NULL, sip_free_phdr},
	{"TIMESTAMP", NULL, sip_parse_timestamp_header, NULL, NULL,
	sip_free_phdr},
	{"UNSUPPORTED", NULL, sip_parse_usupport_header, NULL, NULL,
	sip_free_phdr},
	{"SUPPORTED", "k", sip_parse_support_header, NULL, NULL, sip_free_phdr},
	{"USER-AGENT", NULL, sip_parse_useragt_header, NULL, NULL,
	sip_free_phdr},
	{"WARNING", NULL, sip_parse_warn_header, NULL, NULL, sip_free_phdr},
	{"ALLOW-EVENTS", "u", sip_parse_allow_events_header, NULL, NULL,
	sip_free_phdr},
	{"EVENT", "o", sip_parse_event_header, NULL, NULL, sip_free_phdr},
	{"SUBSCRIPTION-STATE", NULL, sip_parse_substate_header, NULL, NULL,
	sip_free_phdr},
	{"AUTHORIZATION", NULL, sip_parse_author_header, NULL, NULL,
	sip_free_phdr},
	{"AUTHENTICATION-INFO", NULL, sip_parse_ainfo_header, NULL, NULL,
	sip_free_phdr},
	{"PROXY-AUTHORIZATION", NULL, sip_parse_pauthor_header, NULL, NULL,
	sip_free_phdr},
	{"PROXY-AUTHENTICATE", NULL, sip_parse_pauthen_header, NULL, NULL,
	sip_free_phdr},
	{"PROXY-REQUIRE", NULL, sip_parse_preq_header, NULL, NULL,
	sip_free_phdr},
	{"WWW-AUTHENTICATE", NULL, sip_parse_wauthen_header, NULL, NULL,
	sip_free_phdr},
	{"RSEQ", NULL, sip_parse_rseq, NULL, NULL, sip_free_phdr},
	{"RACK", NULL, sip_parse_rack, NULL, NULL, sip_free_phdr},
	{"P-ASSERTED-IDENTITY", NULL, sip_parse_passertedid, NULL, NULL,
	sip_free_phdr},
	{"P-PREFERRED-IDENTITY", NULL, sip_parse_ppreferredid, NULL, NULL,
	sip_free_phdr},
	{"PRIVACY", NULL, sip_parse_privacy_header, NULL, NULL, sip_free_phdr},
	{NULL, NULL, NULL, NULL, NULL, NULL},
};

#define	MAX_SIP_HEADERS	\
	sizeof (sip_header_function_table) / sizeof (sip_header_function_t)

/*
 * External/application provided function table
 */
sip_header_function_t *sip_header_function_table_external = NULL;

/*
 * Free parameter list
 */
static void
sip_free_params(sip_param_t *param_list)
{
	sip_param_t *param, *next_param;

	param = param_list;

	while (param != NULL) {
		next_param = param->param_next;
		free(param);
		param = next_param;
	}
}

/*
 * Common header free routine
 */
void
sip_free_phdr(sip_parsed_header_t *header)
{
	sip_hdr_value_t	*value;
	sip_hdr_value_t	*next_value;

	if (header == NULL)
		return;
	value = (sip_hdr_value_t *)header->value;
	while (value != NULL) {
		sip_free_params(value->sip_param_list);
		next_value = value->sip_next_value;
		free(value);
		value = next_value;
	}
	free(header);
}

/*
 * Free Contact/From/To header
 */
void
sip_free_cftr_header(sip_parsed_header_t *header)
{
	sip_hdr_value_t	*value;
	sip_hdr_value_t	*next_value;

	if (header == NULL)
		return;
	value = (sip_hdr_value_t *)header->value;
	while (value != NULL) {
		next_value = value->sip_next_value;
		sip_free_params(value->sip_param_list);
		if (value->cftr_name != NULL)
			free(value->cftr_name);
		if (value->sip_value_parsed_uri != NULL) {
			sip_free_parsed_uri(value->sip_value_parsed_uri);
			value->sip_value_parsed_uri = NULL;
		}
		free(value);
		value = next_value;
	}
	free(header);
}

/*
 * Return new header
 */
_sip_header_t *
sip_new_header(int header_size)
{
	_sip_header_t *new_header;

	new_header = calloc(1, sizeof (_sip_header_t));
	if (new_header == NULL)
		return (NULL);

	/*
	 * We are using snprintf which adds a null character
	 * so allocate an extra byte which is not part of
	 * the message header
	 */
	new_header->sip_hdr_start = calloc(1, header_size + 1);
	if (new_header->sip_hdr_start == NULL) {
		free(new_header);
		return (NULL);
	}
	new_header->sip_hdr_end = new_header->sip_hdr_start + header_size;
	new_header->sip_hdr_current = new_header->sip_hdr_start;
	new_header->sip_hdr_allocated = B_TRUE;
	return (new_header);
}

/*
 * Free the given header
 */
void
sip_free_header(_sip_header_t *sip_header)
{
	if (sip_header->sip_hdr_allocated) {
		assert(sip_header->sip_hdr_start != NULL);
		free(sip_header->sip_hdr_start);
	}
	if (sip_header->sip_hdr_parsed != NULL) {
		assert(sip_header->sip_header_functions != NULL);
		if (sip_header->sip_header_functions->header_free != NULL) {
			sip_header->sip_header_functions->header_free(
			    sip_header->sip_hdr_parsed);
		}
	}
	free(sip_header);
}

/*
 * Return a copy of the header passed in.
 */
_sip_header_t *
sip_dup_header(_sip_header_t *from)
{
	size_t		hdr_size;
	_sip_header_t	*to;

	hdr_size = from->sip_hdr_end - from->sip_hdr_start;
	to = sip_new_header(hdr_size);
	if (to == NULL)
		return (NULL);
	if (from->sip_header_state == SIP_HEADER_DELETED_VAL) {
		to->sip_hdr_end = to->sip_hdr_start +
		    sip_copy_values(to->sip_hdr_start, from);
	} else {
		(void) memcpy(to->sip_hdr_start, from->sip_hdr_start, hdr_size);
		to->sip_hdr_end = to->sip_hdr_start + hdr_size;
	}
	to->sip_header_functions = from->sip_header_functions;
	return (to);
}

/*
 * Copy header with extra_param, if any, to sip_msg
 */
int
_sip_copy_header(_sip_msg_t *sip_msg, _sip_header_t *header, char *extra_param,
    boolean_t skip_crlf)
{
	_sip_header_t	*new_header;
	int		hdrlen;
	int		extra_len = 0;
	int		ncrlf = 0;
	char		*p;

#ifdef	__solaris__
	assert(mutex_held(&sip_msg->sip_msg_mutex));
#endif
	if (extra_param != NULL) {
		extra_len = SIP_SPACE_LEN + sizeof (char) + SIP_SPACE_LEN +
		    strlen(extra_param);
	}
	/*
	 * Just take one if there are more, i.e. if this is the last header
	 * before the content.
	 */
	if (skip_crlf) {
		if (header->sip_hdr_end - strlen(SIP_CRLF) <=
		    header->sip_hdr_start) {
			goto proceed;
		}
		p = header->sip_hdr_end - strlen(SIP_CRLF);
		while (strncmp(SIP_CRLF, p, strlen(SIP_CRLF)) == 0) {
			ncrlf++;
			if (p - strlen(SIP_CRLF) < header->sip_hdr_start)
				break;
			p -= strlen(SIP_CRLF);
		}
		/*
		 * Take one CRLF.
		 */
		ncrlf = (ncrlf - 1) * strlen(SIP_CRLF);
	}
proceed:
	hdrlen = header->sip_hdr_end - header->sip_hdr_start - ncrlf;
	new_header = sip_new_header(hdrlen + extra_len);
	if (new_header == NULL)
		return (ENOMEM);
	if (header->sip_header_state == SIP_HEADER_DELETED_VAL) {
		int	len;

		len = sip_copy_values(new_header->sip_hdr_start, header);
		new_header->sip_hdr_end = new_header->sip_hdr_start + len;
		hdrlen = hdrlen - len + extra_len;
	} else {
		(void) memcpy(new_header->sip_hdr_start, header->sip_hdr_start,
		    hdrlen);
		new_header->sip_hdr_end = new_header->sip_hdr_start + hdrlen;
		hdrlen = extra_len;
	}
	if (extra_param != NULL) {
		/*
		 * Find CR
		 */
		if (sip_find_cr(new_header) != 0) {
			sip_free_header(new_header);
			return (EINVAL);
		}
		hdrlen += new_header->sip_hdr_end - new_header->sip_hdr_current;
		(void) snprintf(new_header->sip_hdr_current, hdrlen + 1,
		    " %c %s%s", SIP_SEMI, extra_param, SIP_CRLF);
	}

	new_header->sip_hdr_end += extra_len;
	new_header->sip_header_functions = header->sip_header_functions;
	_sip_add_header(sip_msg, new_header, B_TRUE, B_FALSE, NULL);
	return (0);
}

/*
 * Copy all "header_name" headers from _old_msg to _new_msg
 */
int
_sip_find_and_copy_all_header(_sip_msg_t *_old_msg, _sip_msg_t *_new_msg,
    char *header_name)
{
	_sip_header_t	*header;
	int		ret = 0;

	if (_old_msg == NULL || _new_msg == NULL)
		return (EINVAL);
#ifdef	__solaris__
	assert(mutex_held(&_old_msg->sip_msg_mutex));
#endif
	if (_old_msg != _new_msg)
		(void) pthread_mutex_lock(&_new_msg->sip_msg_mutex);
	header = sip_search_for_header(_old_msg, header_name, NULL);
	while (header != NULL) {
		ret = _sip_copy_header(_new_msg, header, NULL, B_TRUE);
		if (ret != 0)
			break;
		header = sip_search_for_header(_old_msg, header_name, header);
	}
	if (_old_msg != _new_msg)
		(void) pthread_mutex_unlock(&_new_msg->sip_msg_mutex);
	return (ret);
}

/*
 * Copy header_name from _old_msg to _new_msg with extra_parm.
 */
int
_sip_find_and_copy_header(sip_msg_t _old_msg, sip_msg_t _new_msg,
    char *header_name, char *extra_param, boolean_t lock_newmsg)
{
	_sip_header_t	*header;
	int		ret;

	if (_old_msg == NULL || _new_msg == NULL)
		return (EINVAL);
#ifdef	__solaris__
	assert(mutex_held(&_old_msg->sip_msg_mutex));
#endif
	header = sip_search_for_header(_old_msg, header_name, NULL);
	if (header == NULL)
		return (EINVAL);
	if (lock_newmsg)
		(void) pthread_mutex_lock(&_new_msg->sip_msg_mutex);
	ret = _sip_copy_header(_new_msg, header, extra_param, B_TRUE);
	if (lock_newmsg)
		(void) pthread_mutex_unlock(&_new_msg->sip_msg_mutex);
	return (ret);
}

/*
 * Copy all headers from old_msg to new_msg
 */
int
sip_copy_all_headers(sip_msg_t old_msg, sip_msg_t new_msg)
{
	_sip_header_t	*header;
	_sip_msg_t	*_old_msg;
	_sip_msg_t	*_new_msg;
	int		ret = 0;

	if (old_msg == NULL || new_msg == NULL)
		return (EINVAL);
	_old_msg = (_sip_msg_t *)old_msg;
	_new_msg = (_sip_msg_t *)new_msg;

	(void) pthread_mutex_lock(&_old_msg->sip_msg_mutex);
	(void) pthread_mutex_lock(&_new_msg->sip_msg_mutex);
	header = sip_search_for_header(_old_msg, NULL, NULL);
	while (header != NULL) {
		ret = _sip_copy_header(_new_msg, header, NULL, B_FALSE);
		if (ret != 0)
			goto done;
		header = sip_search_for_header(_old_msg, NULL, header);
	}
done:
	(void) pthread_mutex_unlock(&_new_msg->sip_msg_mutex);
	(void) pthread_mutex_unlock(&_old_msg->sip_msg_mutex);
	return (ret);
}

/*
 * Copy start line from msg to sip_msg
 */
int
sip_copy_start_line(sip_msg_t msg, sip_msg_t sip_msg)
{
	int		len;
	_sip_header_t	*new_header;
	_sip_msg_t	*_old_msg;
	_sip_msg_t	*_sip_msg;

	if (msg == NULL || sip_msg == NULL)
		return (EINVAL);
	_old_msg = (_sip_msg_t *)msg;
	_sip_msg = (_sip_msg_t *)sip_msg;

	(void) pthread_mutex_lock(&_old_msg->sip_msg_mutex);
	if (_old_msg->sip_msg_start_line == NULL) {
		(void) pthread_mutex_unlock(&_old_msg->sip_msg_mutex);
		return (EINVAL);
	}
	len = _old_msg->sip_msg_start_line->sip_hdr_end -
	    _old_msg->sip_msg_start_line->sip_hdr_start;
	new_header = sip_new_header(len);
	if (new_header == NULL) {
		(void) pthread_mutex_unlock(&_old_msg->sip_msg_mutex);
		return (ENOMEM);
	}
	new_header->sip_hdr_sipmsg = _sip_msg;
	(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
	_sip_msg->sip_msg_start_line = new_header;
	_sip_msg->sip_msg_len = len;
	(void) strncpy(_sip_msg->sip_msg_start_line->sip_hdr_start,
	    _old_msg->sip_msg_start_line->sip_hdr_start, len);
	(void) sip_parse_first_line(_sip_msg->sip_msg_start_line,
	    &_sip_msg->sip_msg_req_res);
	(void) pthread_mutex_unlock(&_old_msg->sip_msg_mutex);
	(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
	return (0);
}

/*
 * Delete start line from sip_msg
 */
int
sip_delete_start_line_locked(_sip_msg_t *_sip_msg)
{
	_sip_header_t	*header;
	_sip_header_t	*next_header;

	if (_sip_msg->sip_msg_start_line == NULL)
		return (EINVAL);

	header = _sip_msg->sip_msg_start_line;
	while (header != NULL) {
		next_header = header->sip_hdr_next;
		_sip_msg->sip_msg_len -= (header->sip_hdr_end -
		    header->sip_hdr_start);
		sip_free_header(header);
		header = next_header;
	}
	_sip_msg->sip_msg_start_line = NULL;

	/*
	 * Also delete the sip_msg_req_res info since we don't have a start
	 * line.
	 */
	while (_sip_msg->sip_msg_req_res != NULL) {
		sip_message_type_t	*sip_msg_type_ptr;

		sip_msg_type_ptr = _sip_msg->sip_msg_req_res->sip_next;
		if (_sip_msg->sip_msg_req_res->is_request) {
			sip_request_t	*reqline;

			reqline = &_sip_msg->sip_msg_req_res->U.sip_request;
			if (reqline->sip_parse_uri != NULL) {
				sip_free_parsed_uri(reqline->sip_parse_uri);
				reqline->sip_parse_uri = NULL;
			}
		}
		free(_sip_msg->sip_msg_req_res);
		_sip_msg->sip_msg_req_res = sip_msg_type_ptr;
	}
	return (0);
}


/*
 * Delete start line from sip_msg
 */
int
sip_delete_start_line(sip_msg_t sip_msg)
{
	_sip_msg_t	*_sip_msg;
	int		ret;

	if (sip_msg == NULL)
		return (EINVAL);

	_sip_msg = (_sip_msg_t *)sip_msg;
	(void) pthread_mutex_lock(&_sip_msg->sip_msg_mutex);
	ret = sip_delete_start_line_locked(_sip_msg);
	(void) pthread_mutex_unlock(&_sip_msg->sip_msg_mutex);

	return (ret);
}

/*
 * Delete all headers from _sip_msg
 */
void
sip_delete_all_headers(_sip_msg_t *_sip_msg)
{
	_sip_header_t *header;

#ifdef	__solaris__
	assert(mutex_held(&_sip_msg->sip_msg_mutex));
#endif

	header = _sip_msg->sip_msg_headers_start;
	while (header != NULL) {
		_sip_header_t *next_header;
		next_header = header->sip_hdr_next;
		sip_free_header(header);
		header = next_header;
	}
	_sip_msg->sip_msg_headers_start = NULL;
	_sip_msg->sip_msg_headers_end = NULL;
}

/*
 * Delete and free the named header. If header_name is null
 * free all headers.
 */
void
sip_delete_headers(sip_msg_t sip_msg, char *header_name)
{
	_sip_header_t *header;
	_sip_msg_t *_sip_msg;

	_sip_msg = (_sip_msg_t *)sip_msg;
#ifdef	__solaris__
	assert(mutex_held(&_sip_msg->sip_msg_mutex));
#endif
	header = sip_search_for_header(_sip_msg, header_name, NULL);
	if (header == NULL)
		return;
	while (header != NULL) {
		if (_sip_msg->sip_msg_headers_start == header) {
			_sip_msg->sip_msg_headers_start = header->sip_hdr_next;
		} else {
			header->sip_hdr_prev->sip_hdr_next =
			    header->sip_hdr_next;
		}
		if (_sip_msg->sip_msg_headers_end == header) {
			_sip_msg->sip_msg_headers_end = header->sip_hdr_prev;
		} else {
			header->sip_hdr_next->sip_hdr_prev =
			    header->sip_hdr_prev;
		}
		sip_free_header(header);
		if (header_name != NULL)
			return;
		else
			header = sip_search_for_header(_sip_msg, NULL, NULL);
	}
}

/*
 * Add a header to sip_msg. If header_name is provided then the new header
 * is added before that header, if first is set, or after. If append is
 * set, then the header is added to the end of the header list.
 */
void
_sip_add_header(_sip_msg_t *sip_msg, _sip_header_t *new_header,
    boolean_t append, boolean_t first, char *header_name)
{
	_sip_header_t	*header = NULL;

	if (sip_msg == NULL || new_header == NULL)
		return;
#ifdef	__solaris__
	assert(mutex_held(&sip_msg->sip_msg_mutex));
#endif
	new_header->sip_hdr_sipmsg = sip_msg;
	if (header_name != NULL) {
		_sip_header_t	*header_tmp;

		header = sip_search_for_header(sip_msg, header_name, NULL);
		header_tmp = header;
		if (!first) {
			while (header != NULL) {
				header_tmp = header;
				header = sip_search_for_header(sip_msg,
				    header_name, header);
			}
		}
		header = header_tmp;
		if (header == NULL)
			append =  B_TRUE;
	}

	if (header != NULL) {
		if (append) {
			new_header->sip_hdr_prev = header;
			if (sip_msg->sip_msg_headers_end == header) {
				sip_msg->sip_msg_headers_end = new_header;
				new_header->sip_hdr_next = NULL;
			} else {
				header->sip_hdr_next->sip_hdr_prev = new_header;
				new_header->sip_hdr_next = header->sip_hdr_next;
			}
			header->sip_hdr_next = new_header;
		} else {
			new_header->sip_hdr_next = header;
			if (sip_msg->sip_msg_headers_start == header) {
				sip_msg->sip_msg_headers_start = new_header;
				new_header->sip_hdr_prev = NULL;
			} else {
				header->sip_hdr_prev->sip_hdr_next = new_header;
				new_header->sip_hdr_prev = header->sip_hdr_prev;
			}
			header->sip_hdr_prev = new_header;
		}
	} else {
		if (append) {
			if (sip_msg->sip_msg_headers_end != NULL) {
				sip_msg->sip_msg_headers_end->sip_hdr_next =
				    new_header;
			} else {
				sip_msg->sip_msg_headers_start = new_header;
			}
			new_header->sip_hdr_prev =
			    sip_msg->sip_msg_headers_end;
			new_header->sip_hdr_next = NULL;
			sip_msg->sip_msg_headers_end = new_header;
		} else {
			if (sip_msg->sip_msg_headers_start != NULL) {
				sip_msg->sip_msg_headers_start->sip_hdr_prev =
				    new_header;
			} else {
				sip_msg->sip_msg_headers_end = new_header;
			}
			new_header->sip_hdr_next =
			    sip_msg->sip_msg_headers_start;
			new_header->sip_hdr_prev = NULL;
			sip_msg->sip_msg_headers_start = new_header;
		}
	}
	sip_msg->sip_msg_len += new_header->sip_hdr_end -
	    new_header->sip_hdr_start;
}

/*
 * Scan through the function table and return the entry for the given header
 * type.
 */
sip_header_function_t *
_sip_get_header_functions(sip_header_function_t *sip_header_function_table,
    _sip_header_t *sip_header, char *header_name)
{
	int	len;
	int	i = 0;

	if (sip_header == NULL && header_name == NULL)
		return (NULL);

	/*
	 * If header_name is NULL we first have to locate the name
	 */
	if (header_name == NULL) {
		if (sip_skip_white_space(sip_header) != 0) {
			return (NULL);
		}
		header_name = sip_header->sip_hdr_current;
		if (sip_find_separator(sip_header, SIP_HCOLON, (char)NULL,
		    (char)NULL, B_FALSE) != 0) {
			return (NULL);
		}
		len = sip_header->sip_hdr_current - header_name;
	} else {
		len = strlen(header_name);
	}

	if (len > 0) {
		while (sip_header_function_table[i].header_name != NULL ||
		    sip_header_function_table[i].header_short_name != NULL) {
			if (sip_header_function_table[i].header_name != NULL &&
			    len ==
			    strlen(sip_header_function_table[i].header_name)) {
				if (strncasecmp(header_name,
				    sip_header_function_table[i].
				    header_name, len) == 0) {
					break;
				}
			} else if (sip_header_function_table[i].
			    header_short_name != NULL && len ==
			    strlen(sip_header_function_table[i].
			    header_short_name)) {
				if (strncasecmp(header_name,
				    sip_header_function_table[i].
				    header_short_name, len) == 0) {
					break;
				}
			}
			i++;
		}
	}

	if (sip_header != NULL)
		sip_header->sip_hdr_current = sip_header->sip_hdr_start;
	if (sip_header_function_table[i].header_name == NULL)
		return (NULL);
	return (&sip_header_function_table[i]);
}

/*
 * Return the entry from the function table for the given header
 */
sip_header_function_t *
sip_get_header_functions(_sip_header_t *sip_header, char *header_name)
{
	sip_header_function_t	*func;
	sip_header_function_t	*header_f_table = NULL;

	if (sip_header_function_table_external != NULL) {
		header_f_table = _sip_get_header_functions(
		    sip_header_function_table_external,
		    sip_header, header_name);
		if (header_f_table != NULL)
			return (header_f_table);
	}
	func = _sip_get_header_functions(sip_header_function_table, sip_header,
	    header_name);
	return (func);
}

/*
 * Search for the header name passed in.
 */
_sip_header_t *
sip_search_for_header(_sip_msg_t *sip_msg, char *header_name,
    _sip_header_t *old_header)
{
	int			len = 0;
	int			full_len = 0;
	int			compact_len = 0;
	_sip_header_t		*header = NULL;
	char			*compact_name = NULL;
	char			*full_name = NULL;
	sip_header_function_t	*header_f_table = NULL;

	if (sip_msg == NULL)
		return (NULL);
#ifdef	__solaris__
	assert(mutex_held(&sip_msg->sip_msg_mutex));
#endif

	if (header_name != NULL) {
		header_f_table = sip_get_header_functions(NULL, header_name);
		if (header_f_table != NULL) {
			full_name = header_f_table->header_name;
			compact_name = header_f_table->header_short_name;
			if (full_name != NULL)
				full_len = strlen(full_name);
			if (compact_name != NULL)
				compact_len = strlen(compact_name);
		} else {
			header_f_table = &sip_header_function_table[0];
			full_name = header_name;
			full_len  = strlen(full_name);
		}
	}

	if (old_header != NULL)
		header = old_header->sip_hdr_next;
	else
		header = sip_msg->sip_msg_headers_start;

	while (header != NULL) {

		if (header->sip_header_state == SIP_HEADER_DELETED) {
			header = header->sip_hdr_next;
			continue;
		}

		if (compact_len == 0 && full_len == 0)
			break;

		header->sip_hdr_current = header->sip_hdr_start;

		if (sip_skip_white_space(header)) {
			header = header->sip_hdr_next;
			continue;
		}

		len = header->sip_hdr_end - header->sip_hdr_current;

		if (full_name != NULL && (full_len <= len) &&
		    strncasecmp(header->sip_hdr_current, full_name,
		    full_len) == 0) {
			header->sip_hdr_current += full_len;
			if (sip_skip_white_space(header)) {
				header = header->sip_hdr_next;
				continue;
			}

			if (*header->sip_hdr_current == SIP_HCOLON) {
				header_name = full_name;
				break;
			}
		}

		if (compact_name != NULL && (compact_len <= len) &&
		    strncasecmp(header->sip_hdr_current, compact_name,
		    compact_len) == 0) {
			header->sip_hdr_current += compact_len;
			if (sip_skip_white_space(header)) {
				header = header->sip_hdr_next;
				continue;
			}
			if (*header->sip_hdr_current == SIP_HCOLON) {
				header_name = compact_name;
				break;
			}
		}
		header = header->sip_hdr_next;
	}

	if (header != NULL) {
		header->sip_hdr_current = header->sip_hdr_start;
		if (header_f_table == NULL) {
			header_f_table =
			    sip_get_header_functions(header, header_name);
			if (header_f_table == NULL)
				header_f_table = &sip_header_function_table[0];
		}

		header->sip_header_functions = header_f_table;
	}
	return (header);
}

/*
 * Return the start line as a string. Caller frees string
 */
char *
_sip_startline_to_str(_sip_msg_t *sip_msg, int *error)
{
	char		*slstr;
	int		len;

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

	if (sip_msg == NULL || sip_msg->sip_msg_start_line == NULL) {
		if (error != NULL)
			*error = EINVAL;
		return (NULL);
	}
	(void) pthread_mutex_lock(&sip_msg->sip_msg_mutex);
	len = sip_msg->sip_msg_start_line->sip_hdr_end -
	    sip_msg->sip_msg_start_line->sip_hdr_start - 2;
	if ((slstr = malloc(len + 1)) == NULL) {
		(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
		if (error != NULL)
			*error = ENOMEM;
		return (NULL);
	}
	(void) strncpy(slstr, sip_msg->sip_msg_start_line->sip_hdr_start, len);
	(void) pthread_mutex_unlock(&sip_msg->sip_msg_mutex);
	slstr[len] = '\0';
	return (slstr);
}

/*
 * Return the given header as a string. Caller frees string
 */
char *
sip_hdr_to_str(sip_header_t sip_header, int *error)
{
	char		*hdrstr;
	char		*tmpptr;
	_sip_header_t	*_sip_header;
	int		len;

	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_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);
	}
	if (_sip_header->sip_hdr_sipmsg != NULL) {
		(void) pthread_mutex_lock(
		    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
	}
	len = _sip_header->sip_hdr_end - _sip_header->sip_hdr_start;
	hdrstr = malloc(len);
	if (hdrstr == NULL) {
		if (_sip_header->sip_hdr_sipmsg != NULL) {
			(void) pthread_mutex_unlock(
			    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
		}
		if (error != NULL)
			*error = ENOMEM;
		return (NULL);
	}
	if (_sip_header->sip_header_state == SIP_HEADER_DELETED_VAL) {
		len = sip_copy_values(hdrstr, _sip_header);
	} else {
		(void) strncpy(hdrstr, _sip_header->sip_hdr_start, len);
	}
	if (_sip_header->sip_hdr_sipmsg != NULL) {
		(void) pthread_mutex_unlock(
		    &_sip_header->sip_hdr_sipmsg->sip_msg_mutex);
	}
	tmpptr = hdrstr + len;
	while (*tmpptr-- != '\n') {
		if (tmpptr == _sip_header->sip_hdr_start) {
			free(hdrstr);
			if (error != NULL)
				*error = EINVAL;
			return (NULL);
		}
	}
	*tmpptr = '\0';
	return (hdrstr);
}

/*
 * Given a param list find the named parameter.
 * Returns a pointer to the value or NULL.
 */
sip_param_t *
sip_get_param_from_list(sip_param_t *param_list, char *param_name)
{
	while (param_list != NULL) {
		if (param_list->param_name.sip_str_len == strlen(param_name) &&
		    strncasecmp(param_list->param_name.sip_str_ptr, param_name,
		    strlen(param_name)) == 0) {
			return (param_list);
		}
		param_list = param_list->param_next;
	}
	return (NULL);
}