/*
 * This file is part of the Sofia-SIP package
 *
 * Copyright (C) 2005 Nokia Corporation.
 *
 * Contact: Pekka Pessi <pekka.pessi@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

/**@CFILE sip_extra.c
 * @brief Non-critical SIP headers
 * 
 * This file contains implementation of @CallInfo, @ErrorInfo,
 * @Organization, @Priority, @RetryAfter, @Server, @Subject,
 * @Timestamp, and @UserAgent headers.
 * 
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>.
 * 
 * @date Created: Tue Jun 13 02:57:51 2000 ppessi
 */

#include "config.h"

/* Avoid casting sip_t to msg_pub_t and sip_header_t to msg_header_t */
#define MSG_PUB_T       struct sip_s
#define MSG_HDR_T       union sip_header_u

#include "sofia-sip/sip_parser.h"
#include "sofia-sip/sip_extra.h"

#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>

#include <assert.h>

/* ====================================================================== */

static issize_t sip_info_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen);

static isize_t sip_info_dup_xtra(sip_header_t const *h, isize_t offset);
static char *sip_info_dup_one(sip_header_t *dst,
			      sip_header_t const *src,
			      char *b,
			      isize_t xtra);

#define sip_info_update NULL

/* ====================================================================== */

/**@SIP_HEADER sip_call_info Call-Info Header
 * 
 * The Call-Info header provides additional information about the caller or
 * callee. Its syntax is defined in @RFC3261 as follows:
 * 
 * @code
 *    Call-Info   =  "Call-Info" HCOLON info *(COMMA info)
 *    info        =  LAQUOT absoluteURI RAQUOT *( SEMI info-param)
 *    info-param  =  ( "purpose" EQUAL ( "icon" / "info"
 *                   / "card" / token ) ) / generic-param
 * @endcode
 * 
 *
 * The parsed Call-Info header is stored in #sip_call_info_t structure.
 */

/**@ingroup sip_call_info
 * @typedef struct sip_call_info_s sip_call_info_t;
 *
 * The structure #sip_call_info_t contains representation of an 
 * @CallInfo header.
 *
 * The #sip_call_info_t is defined as follows:
 * @code
 * struct sip_call_info_s
 * {
 *   sip_common_t        ci_common[1]; // Common fragment info
 *   sip_call_info_t    *ci_next;      // Link to next @CallInfo
 *   url_t               ci_url[1];    // URI to call info
 *   msg_param_t const  *ci_params;    // List of parameters
 *   char const         *ci_purpose;   // Value of @b purpose parameter
 * };
 * @endcode
 */

#define sip_call_info_dup_xtra  sip_info_dup_xtra
#define sip_call_info_dup_one   sip_info_dup_one
static msg_update_f sip_call_info_update;

msg_hclass_t sip_call_info_class[] =
SIP_HEADER_CLASS(call_info, "Call-Info", "",
		 ci_params, append, call_info);

issize_t sip_call_info_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  issize_t retval = sip_info_d(home, h, s, slen);

  if (retval == 0)
    for (;h; h = h->sh_next)
      msg_header_update_params(h->sh_common, 0);

  return retval;
}

issize_t sip_call_info_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  sip_call_info_t *ci = (sip_call_info_t *)h;

  assert(sip_call_info_p(h));

  return sip_name_addr_e(b, bsiz, f, NULL, 1, ci->ci_url, ci->ci_params, NULL);
}

/** @internal
 * Update parameter in a @CallInfo object.
 * 
 */
static
int sip_call_info_update(msg_common_t *h, 
			  char const *name, isize_t namelen,
			  char const *value)
{
  sip_call_info_t *ci = (sip_call_info_t *)h;

  if (name == NULL) {
    ci->ci_purpose = NULL;
  }
  else if (namelen == strlen("purpose") && 
	   !strncasecmp(name, "purpose", namelen)) {
    ci->ci_purpose = value;
  }

  return 0;
}

/* ====================================================================== */

/**@SIP_HEADER sip_error_info Error-Info Header
 * 
 * The Error-Info header provides a pointer to additional information about
 * the error status response. Its syntax is defined in @RFC3261 as follows:
 * 
 * @code
 *    Error-Info  =  "Error-Info" HCOLON error-uri *(COMMA error-uri)
 *    error-uri   =  LAQUOT absoluteURI RAQUOT *( SEMI generic-param )
 * @endcode
 * 
 *
 * The parsed Error-Info header is stored in #sip_error_info_t structure.
 */

/**@ingroup sip_error_info
 * @typedef struct sip_error_info_s sip_error_info_t;
 *
 * The structure #sip_error_info_t contains representation of an 
 * @ErrorInfo header.
 *
 * The #sip_error_info_t is defined as follows:
 * @code
 * struct sip_error_info_s
 * {
 *   sip_common_t        ei_common[1]; // Common fragment info
 *   sip_error_info_t   *ei_next;      // Link to next @ErrorInfo
 *   url_t               ei_url[1];    // URI to error info
 *   msg_param_t const  *ei_params;    // List of parameters
 * };
 * @endcode
 */

msg_hclass_t sip_error_info_class[] = 
SIP_HEADER_CLASS(error_info, "Error-Info", "",
		 ei_params, append, info);

issize_t sip_error_info_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  return sip_info_d(home, h, s, slen);
}

issize_t sip_error_info_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  sip_error_info_t const *ei = h->sh_error_info;

  assert(sip_error_info_p(h));

  return sip_name_addr_e(b, bsiz, f,
			 NULL, 1, ei->ei_url, ei->ei_params, NULL);
}

/* ====================================================================== */

/**@SIP_HEADER sip_alert_info Alert-Info Header
 *
 * When present in an INVITE request, the Alert-Info header field
 * specifies an alternative ring tone to the UAS.  When present in a 180
 * (Ringing) response, the Alert-Info header field specifies an
 * alternative ringback tone to the UAC.  A typical usage is for a proxy
 * to insert this header field to provide a distinctive ring feature.
 *
 * @code
 *    Alert-Info   =  "Alert-Info" HCOLON alert-param *(COMMA alert-param)
 *    alert-param  =  LAQUOT absoluteURI RAQUOT *(SEMI generic-param)
 * @endcode
 *
 * The parsed Alert-Info header is stored in #sip_alert_info_t structure.
 *
 * @NEW_1_12_7. In order to use @b Alert-Info header, 
 * initialize the SIP parser with, e.g.,
 * sip_update_default_mclass(sip_extend_mclass(NULL))
 */

/**@ingroup sip_alert_info
 * @typedef struct sip_alert_info_s sip_alert_info_t;
 *
 * The structure #sip_alert_info_t contains representation of an
 * @AlertInfo header.
 *
 * The #sip_alert_info_t is defined as follows:
 * @code
 * struct sip_alert_info_s
 * {
 *   sip_common_t        ai_common[1]; // Common fragment info
 *   sip_alert_info_t   *ai_next;      // Link to next @AlertInfo
 *   url_t               ai_url[1];    // URI to alert info
 *   msg_param_t const  *ai_params;    // List of optional parameters
 * };
 * @endcode
 *
 * @NEW_1_12_7.
 */

msg_hclass_t sip_alert_info_class[] =
SIP_HEADER_CLASS(alert_info, "Alert-Info", "",
		 ai_params, append, info);

issize_t sip_alert_info_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  return sip_info_d(home, h, s, slen);
}

issize_t sip_alert_info_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  sip_alert_info_t *ai = (sip_alert_info_t *)h;
  return sip_name_addr_e(b, bsiz, f, NULL, 1, ai->ai_url, ai->ai_params, NULL);
}

/* ====================================================================== */

/**@SIP_HEADER sip_reply_to Reply-To Header
 *
 * The @b Reply-To header field contains a logical return URI that may be
 * different from the @From header field. For example, the URI MAY be used to
 * return missed calls or unestablished sessions. If the user wished to
 * remain anonymous, the header field SHOULD either be omitted from the
 * request or populated in such a way that does not reveal any private
 * information. Its syntax is defined in @RFC3261 as follows:
 *
 * @code
 *   Reply-To      =  "Reply-To" HCOLON rplyto-spec
 *   rplyto-spec   =  ( name-addr / addr-spec )
 *                   *( SEMI rplyto-param )
 *   rplyto-param  =  generic-param
 * @endcode
 *
 * The parsed Reply-To header is stored in #sip_reply_to_t structure.
 *
 * @sa sip_update_default_mclass()
 *
 * @NEW_1_12_7. In order to use @b Reply-To header,
 * initialize the SIP parser with, e.g.,
 * sip_update_default_mclass(sip_extend_mclass(NULL)).
 */

/**@ingroup sip_reply_to
 * @typedef struct msg_list_s sip_reply_to_t;
 *
 * The structure #sip_reply_to_t contains representation of SIP
 * @ReplyTo header.
 *
 * The #sip_reply_to_t is defined as follows:
 * @code
 * struct sip_reply_to_s
 * {
 *   sip_common_t       rplyto_common[1]; // Common fragment info

 *   sip_error_t       *rplyto_next;	 // Dummy link to next header
 *   char const        *rplyto_display;	 // Display name
 *   url_t              rplyto_url[1];	 // Return URI
 *   msg_param_t const *rplyto_params;	 // List of optional parameters
 * };
 * @endcode
 */

static isize_t sip_reply_to_dup_xtra(sip_header_t const *h, isize_t offset);
static char *sip_reply_to_dup_one(sip_header_t *dst,
				  sip_header_t const *src,
				  char *b,
				  isize_t xtra);
#define sip_reply_to_update NULL

msg_hclass_t sip_reply_to_class[] =
  SIP_HEADER_CLASS(reply_to, "Reply-To", "", rplyto_params, single, reply_to);

issize_t sip_reply_to_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  sip_reply_to_t *rplyto = (sip_reply_to_t *)h;

  return sip_name_addr_d(home,
			 &s,
			 &rplyto->rplyto_display,
			 rplyto->rplyto_url,
			 &rplyto->rplyto_params,
			 NULL);
}

issize_t sip_reply_to_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  sip_reply_to_t *rplyto = (sip_reply_to_t *)h;

  return sip_name_addr_e(b, bsiz,
			 flags,
			 rplyto->rplyto_display,
			 MSG_IS_CANONIC(flags), rplyto->rplyto_url,
			 rplyto->rplyto_params,
			 NULL);
}

static isize_t sip_reply_to_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_reply_to_t const *rplyto = (sip_reply_to_t const *)h;

  return sip_name_addr_xtra(rplyto->rplyto_display,
			    rplyto->rplyto_url,
			    rplyto->rplyto_params,
			    offset);
}

/**@internal Duplicate one sip_reply_to_t object. */
static char *sip_reply_to_dup_one(sip_header_t *dst, sip_header_t const *src,
				  char *b, isize_t xtra)
{
  sip_reply_to_t *rplyto = (sip_reply_to_t *)dst;
  sip_reply_to_t const *o = (sip_reply_to_t *)src;

  return sip_name_addr_dup(&rplyto->rplyto_display, o->rplyto_display,
			   rplyto->rplyto_url, o->rplyto_url,
			   &rplyto->rplyto_params, o->rplyto_params,
			   b, xtra);
}

/* ====================================================================== */

/**@SIP_HEADER sip_in_reply_to In-Reply-To Header
 * 
 * The @b In-Reply-To request header field enumerates the 
 * @ref sip_call_id "Call-IDs" that this call references or returns.
 * Its syntax is defined in @RFC3261 as follows:
 * 
 * @code
 *    In-Reply-To  =  "In-Reply-To" HCOLON callid *(COMMA callid)
 * @endcode
 *
 * The parsed In-Reply-To header is stored in #sip_in_reply_to_t structure.
 */

/**@ingroup sip_in_reply_to
 * @typedef struct msg_list_s sip_in_reply_to_t;
 *
 * The structure #sip_in_reply_to_t contains representation of SIP 
 * @InReplyTo header. 
 *
 * The #sip_in_reply_to_t is defined as follows:
 * @code
 * typedef struct msg_list_s
 * {
 *   msg_common_t       k_common[1];  // Common fragment info
 *   msg_list_t        *k_next;       // Link to next header
 *   msg_param_t       *k_items;      // List of call ids
 * } sip_in_reply_to_t;
 * @endcode
 */

msg_hclass_t sip_in_reply_to_class[] = 
SIP_HEADER_CLASS_LIST(in_reply_to, "In-Reply-To", "", list);

issize_t sip_in_reply_to_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  return msg_list_d(home, h, s, slen);
}

issize_t sip_in_reply_to_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  assert(sip_in_reply_to_p(h));
  return msg_list_e(b, bsiz, h, f);
}

/* ====================================================================== */

/**@SIP_HEADER sip_organization Organization Header
 * 
 * The Organization header field conveys the name of the organization to
 * which the entity issuing the request or response belongs. Its syntax is
 * defined in @RFC3261 as follows:
 * 
 * @code
 *    Organization  =  "Organization" HCOLON [TEXT-UTF8-TRIM]
 * @endcode
 * 
 *
 * The parsed Organization header is stored in #sip_organization_t structure.
 */

/**@ingroup sip_organization
 * @typedef struct msg_generic_s sip_organization_t; 
 *
 * The structure #sip_organization_t contains representation of a SIP 
 * @Organization header.
 *
 * The #sip_organization_t is defined as follows:
 * @code
 * typedef struct msg_generic_s
 * {
 *   msg_common_t   g_common[1];    // Common fragment info
 *   msg_generic_t *g_next;	    // Link to next header
 *   char const    *g_string;	    // Organization text
 * } sip_organization_t;
 * @endcode
 */

msg_hclass_t sip_organization_class[] = 
SIP_HEADER_CLASS_G(organization, "Organization", "", single);

issize_t sip_organization_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  return sip_generic_d(home, h, s, slen);
}

issize_t sip_organization_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  assert(sip_organization_p(h));
  return sip_generic_e(b, bsiz, h, f);
}

/* ====================================================================== */

/**@SIP_HEADER sip_priority Priority Header
 * 
 * The Priority request-header field indicates the urgency of the request as
 * perceived by the client. Its syntax is defined in @RFC3261 as follows:
 * 
 * @code
 *    Priority        =  "Priority" HCOLON priority-value
 *    priority-value  =  "emergency" / "urgent" / "normal"
 *                       / "non-urgent" / other-priority
 *    other-priority  =  token
 * @endcode
 * 
 *
 * The parsed Priority header is stored in #sip_priority_t structure.
 */

/**@ingroup sip_priority
 * @typedef struct msg_generic_s sip_priority_t; 
 *
 * The structure #sip_priority_t contains representation of a SIP 
 * @Priority header.
 *
 * The #sip_priority_t is defined as follows:
 * @code
 * typedef struct msg_generic_s
 * {
 *   msg_common_t   g_common[1];    // Common fragment info
 *   msg_generic_t *g_next;	    // Dummy link to next header
 *   char const    *g_string;	    // Priority token
 * } sip_priority_t;
 * @endcode
 */

msg_hclass_t sip_priority_class[] = 
SIP_HEADER_CLASS_G(priority, "Priority", "", single);

issize_t sip_priority_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  sip_priority_t *priority = (sip_priority_t *)h;
  
  if (msg_token_d(&s, &priority->g_string) < 0)
    return -1;

  if (*s && !IS_LWS(*s))	/* Something extra after priority token? */
    return -1;

  return 0;
}

issize_t sip_priority_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  assert(sip_priority_p(h));
  return sip_generic_e(b, bsiz, h, f);
}

/* ====================================================================== */

/**@SIP_HEADER sip_server Server Header
 * 
 * The Server response-header field contains information about the software
 * used by the user agent server to handle the request. Its syntax is
 * defined in @RFC2616 section 14.38 and @RFC3261 as follows:
 * 
 * @code
 *    Server           =  "Server" HCOLON server-val *(LWS server-val)
 *    server-val       =  product / comment
 *    product          =  token [SLASH product-version]
 *    product-version  =  token
 * @endcode
 *
 * The parsed Server header is stored in #sip_server_t structure.
 */

/**@ingroup sip_server
 * @typedef struct msg_generic_s sip_server_t; 
 *
 * The structure #sip_server_t contains representation of a SIP 
 * @Server header.
 *
 * The #sip_server_t is defined as follows:
 * @code
 * typedef struct msg_generic_s
 * {
 *   msg_common_t   g_common[1];    // Common fragment info
 *   msg_generic_t *g_next;	    // Link to next header
 *   char const    *g_string;	    // Server tokens
 * } sip_server_t;
 * @endcode
 */

msg_hclass_t sip_server_class[] = 
SIP_HEADER_CLASS_G(server, "Server", "", single);

issize_t sip_server_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  return sip_generic_d(home, h, s, slen);
}

issize_t sip_server_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  assert(sip_server_p(h));
  return sip_generic_e(b, bsiz, h, f);
}

/* ====================================================================== */

/**@SIP_HEADER sip_subject Subject Header
 * 
 * The Subject header provides a summary or indicates the nature of the
 * request. Its syntax is defined in @RFC3261 as follows:
 * 
 * @code
 *    Subject  =  ( "Subject" / "s" ) HCOLON [TEXT-UTF8-TRIM]
 * @endcode
 * 
 * The parsed Subject header is stored in #sip_subject_t structure.
 */

/**@ingroup sip_subject
 * @typedef struct msg_generic_s sip_subject_t; 
 *
 * The structure #sip_subject_t contains representation of a SIP 
 * @Subject header.
 *
 * The #sip_subject_t is defined as follows:
 * @code
 * typedef struct msg_generic_s
 * {
 *   msg_common_t   g_common[1];    // Common fragment info
 *   msg_generic_t *g_next;	    // Link to next header
 *   char const    *g_string;	    // Subject text
 * } sip_subject_t;
 * @endcode
 */

msg_hclass_t sip_subject_class[] = 
SIP_HEADER_CLASS_G(subject, "Subject", "s", single);

issize_t sip_subject_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  return sip_generic_d(home, h, s, slen);
}

issize_t sip_subject_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  assert(sip_subject_p(h));
  return sip_generic_e(b, bsiz, h, f);
}

/* ====================================================================== */

/**@SIP_HEADER sip_timestamp Timestamp Header
 * 
 * The @b Timestamp header describes when the client sent the request to the
 * server, and it is used by the client to adjust its retransmission
 * intervals. Its syntax is defined in @RFC3261 as follows:
 * 
 * @code
 *    Timestamp  =  "Timestamp" HCOLON 1*(DIGIT)
 *                   [ "." *(DIGIT) ] [ LWS delay ]
 *    delay      =  *(DIGIT) [ "." *(DIGIT) ]
 * @endcode
 * 
 * The parsed Timestamp header is stored in #sip_timestamp_t structure.
 */

/**@ingroup sip_timestamp
 * @typedef struct sip_timestamp_s sip_timestamp_t; 
 *
 * The structure #sip_timestamp_t contains representation of a SIP 
 * @Timestamp header.
 *
 * The #sip_timestamp_t is defined as follows:
 * @code
 * typedef struct sip_timestamp_s
 * {
 *   sip_common_t        ts_common[1]; // Common fragment info
 *   sip_error_t        *ts_next;      // Dummy link
 *   char const         *ts_stamp;     // Original timestamp
 *   char const         *ts_delay;     // Delay at UAS
 * } sip_timestamp_t;
 * @endcode
 */

static isize_t sip_timestamp_dup_xtra(sip_header_t const *h, isize_t offset);
static char *sip_timestamp_dup_one(sip_header_t *dst,
				   sip_header_t const *src,
				   char *b,
				   isize_t xtra);
#define sip_timestamp_update NULL

msg_hclass_t sip_timestamp_class[] = 
SIP_HEADER_CLASS(timestamp, "Timestamp", "", ts_common, single,
		 timestamp);

issize_t sip_timestamp_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  sip_timestamp_t *ts = (sip_timestamp_t*)h;

  ts->ts_stamp = s;
  s += span_digit(s); 
  if (s == ts->ts_stamp)
    return -1;
  if (*s == '.') { s += span_digit(s + 1) + 1; }

  if (IS_LWS(*s)) {
    *s = '\0';
    s += span_lws(s + 1) + 1;
    ts->ts_delay = s;
    s += span_digit(s); if (*s == '.') { s += span_digit(s + 1) + 1; }
  }

  if (!*s || IS_LWS(*s))
    *s++ = '\0';
  else
    return -1;
  
  return 0;
}

issize_t sip_timestamp_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  sip_timestamp_t const *ts = h->sh_timestamp;
  char *end = b + bsiz, *b0 = b;

  assert(sip_timestamp_p(h));

  MSG_STRING_E(b, end, ts->ts_stamp);
  if (ts->ts_delay) {
    MSG_CHAR_E(b, end, ' ');
    MSG_STRING_E(b, end, ts->ts_delay);
  }

  MSG_TERM_E(b, end);
    
  return b - b0;
}

static
isize_t sip_timestamp_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_timestamp_t const *ts = h->sh_timestamp;

  offset += MSG_STRING_SIZE(ts->ts_stamp);
  offset += MSG_STRING_SIZE(ts->ts_delay);

  return offset;
}

static
char *sip_timestamp_dup_one(sip_header_t *dst,
			    sip_header_t const *src,
			    char *b,
			    isize_t xtra)
{
  sip_timestamp_t *ts = dst->sh_timestamp;
  sip_timestamp_t const *o = src->sh_timestamp;
  char *end = b + xtra;

  MSG_STRING_DUP(b, ts->ts_stamp, o->ts_stamp);
  MSG_STRING_DUP(b, ts->ts_delay, o->ts_delay);

  assert(b <= end); (void)end;

  return b;
}

/* ====================================================================== */

/**@SIP_HEADER sip_user_agent User-Agent Header
 * 
 * The User-Agent header contains information about the client user agent
 * originating the request. Its syntax is defined in [H14.43, S10.45] as
 * follows:
 * 
 * @code
 *    User-Agent       =  "User-Agent" HCOLON server-val *(LWS server-val)
 *    server-val       =  product / comment
 *    product          =  token [SLASH product-version]
 *    product-version  =  token
 * @endcode
 * 
 * The parsed User-Agent header is stored in #sip_user_agent_t structure.
 */

/**@ingroup sip_user_agent
 * @typedef struct msg_generic_s sip_user_agent_t; 
 *
 * The structure #sip_user_agent_t contains representation of a SIP 
 * @UserAgent header.
 *
 * The #sip_user_agent_t is defined as follows:
 * @code
 * typedef struct msg_generic_s
 * {
 *   msg_common_t   g_common[1];    // Common fragment info
 *   msg_generic_t *g_next;	    // Link to next header
 *   char const    *g_string;	    // User-Agent components
 * } sip_user_agent_t;
 * @endcode
 */

msg_hclass_t sip_user_agent_class[] = 
SIP_HEADER_CLASS_G(user_agent, "User-Agent", "", single);

issize_t sip_user_agent_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  return sip_generic_d(home, h, s, slen);
}

issize_t sip_user_agent_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  assert(sip_user_agent_p(h));
  return sip_generic_e(b, bsiz, h, f);
}

/* ====================================================================== */

/**@SIP_HEADER sip_etag SIP-ETag Header
 * 
 * The @b SIP-ETag header field identifies the published event state. Its
 * syntax is defined in @RFC3903 as follows:
 * 
 * @code
 *      SIP-ETag           = "SIP-ETag" HCOLON entity-tag
 *      entity-tag         = token
 * @endcode
 *
 * The parsed SIP-ETag header is stored in #sip_etag_t structure.
 */

/**@ingroup sip_etag
 * @typedef struct msg_generic_s sip_etag_t; 
 *
 * The structure #sip_etag_t contains representation of a SIP 
 * @SIPETag header.
 *
 * The #sip_etag_t is defined as follows:
 * @code
 * typedef struct msg_generic_s
 * {
 *   msg_common_t   g_common[1];    // Common fragment info
 *   msg_generic_t *g_next;	    // Link to next header
 *   char const    *g_string;	    // entity-tag
 * } sip_etag_t;
 * @endcode
 */

msg_hclass_t sip_etag_class[] = 
SIP_HEADER_CLASS_G(etag, "SIP-ETag", "", single);

issize_t sip_etag_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  sip_etag_t *etag = (sip_etag_t *)h;

  return msg_token_d(&s, &etag->g_value);
}

issize_t sip_etag_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  return msg_generic_e(b, bsiz, h, f);
}

/* ====================================================================== */

/**@SIP_HEADER sip_if_match SIP-If-Match Header
 * 
 * The @b SIP-If-Match header field identifies the specific entity of event
 * state that the request is refreshing, modifying or removing. Its syntax
 * is defined in @RFC3903 as follows:
 * 
 * @code
 *      SIP-If-Match       = "SIP-If-Match" HCOLON entity-tag
 *      entity-tag         = token
 * @endcode
 *
 * The parsed SIP-If-Match header is stored in #sip_if_match_t structure.
 */

/**@ingroup sip_if_match
 * @typedef struct msg_generic_s sip_if_match_t; 
 *
 * The structure #sip_if_match_t contains representation of a SIP 
 * @SIPIfMatch header.
 *
 * The #sip_if_match_t is defined as follows:
 * @code
 * typedef struct msg_generic_s
 * {
 *   msg_common_t   g_common[1];    // Common fragment info
 *   msg_generic_t *g_next;	    // Link to next header
 *   char const    *g_string;	    // entity-tag
 * } sip_if_match_t;
 * @endcode
 */

msg_hclass_t sip_if_match_class[] = 
SIP_HEADER_CLASS_G(if_match, "SIP-If-Match", "", single);

issize_t sip_if_match_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  return sip_etag_d(home, h, s, slen);
}

issize_t sip_if_match_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  return sip_etag_e(b, bsiz, h, f);
}

/* ====================================================================== */

/** Parsing @CallInfo, @ErrorInfo. */
static
issize_t sip_info_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  sip_call_info_t *ci = h->sh_call_info;
  char *end = s + slen;

  assert(h);

  while (*s == ',')
    s += span_lws(s + 1) + 1;

  if (sip_name_addr_d(home, &s, NULL, ci->ci_url, &ci->ci_params, NULL) < 0)
    return -1;

  /* Recurse */
  return msg_parse_next_field(home, h, s, end - s);
}

isize_t sip_info_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_call_info_t const *ci = h->sh_call_info;

  return sip_name_addr_xtra(NULL,
			    ci->ci_url,
			    ci->ci_params,
			    offset);
}

char *sip_info_dup_one(sip_header_t *dst,
		       sip_header_t const *src,
		       char *b,
		       isize_t xtra)
{
  sip_call_info_t *ci = dst->sh_call_info;
  sip_call_info_t const *o = src->sh_call_info;

  return sip_name_addr_dup(NULL, NULL,
			   ci->ci_url, o->ci_url,
			   &ci->ci_params, o->ci_params,
			   b, xtra);
}

/* ====================================================================== */

#if SU_HAVE_EXPERIMENTAL

/**@SIP_HEADER sip_suppress_body_if_match Suppress-Body-If-Match Header
 *
 * The @b Suppress-Body-If-Match header field identifies a SIP event content
 * already known by the watcher. Its syntax is defined in
 * draft-niemi-sip-subnot-etags-01 as follows:
 *
 * @code
 *    Suppress-Body-If-Match = "Suppress-Body-If-Match" HCOLON entity-tag
 *    entity-tag             = token
 * @endcode
 *
 * The parsed Suppress-Body-If-Match header is stored in
 * #sip_suppress_body_if_match_t structure.
 *
 * @sa @RFC3265, draft-niemi-sip-subnot-etags-01.txt
 *
 * @EXP_1_12_5. Note that #sip_t does not contain @a
 * sip_suppress_body_if_match field, but sip_suppress_body_if_match()
 * function should be used for accessing the @b Suppress-Body-If-Match
 * header structure.
 */

/**@ingroup sip_suppress_body_if_match
 * @typedef struct sip_suppress_body_if_match_s sip_suppress_body_if_match_t;
 *
 * The structure #sip_suppress_body_if_match_t contains representation of a
 * SIP @SuppressBodyIfMatch header.
 *
 * The #sip_suppress_body_if_match_t is defined as follows:
 * @code
 * typedef struct sip_suppress_body_if_match_s
 * {
 *   sip_common_t   sbim_common[1]; // Common fragment info
 *   sip_error_t   *sbim_next;      // Dummy link to next header
 *   char const    *sbim_tag;       // entity-tag
 * } sip_suppress_body_if_match_t;
 * @endcode
 */

#define sip_suppress_body_if_match_dup_xtra  msg_generic_dup_xtra
#define sip_suppress_body_if_match_dup_one   msg_generic_dup_one
#define sip_suppress_body_if_match_update NULL

msg_hclass_t sip_suppress_body_if_match_class[] =
SIP_HEADER_CLASS(suppress_body_if_match,
		 "Suppress-Body-If-Match", "",
		 sbim_common, single, suppress_body_if_match);

issize_t sip_suppress_body_if_match_d(su_home_t *home,
				      sip_header_t *h,
				      char *s, isize_t slen)
{
  sip_suppress_body_if_match_t *sbim = (void *)h;
  return msg_token_d(&s, &sbim->sbim_tag);
}

issize_t sip_suppress_body_if_match_e(char b[], isize_t bsiz,
				      sip_header_t const *h,
				      int f)
{
  return sip_etag_e(b, bsiz, h, f);
}


/* ====================================================================== */

/**@SIP_HEADER sip_suppress_notify_if_match Suppress-Notify-If-Match Header
 *
 * The @b Suppress-Notify-If-Match header is used to suppress
 * superfluous NOTIFY transactions. Its syntax is defined in
 * draft-niemi-sip-subnot-etags-01 as follows:
 *
 * @code
 *    Suppress-Notify-If-Match = "Suppress-Notify-If-Match" HCOLON entity-tag
 *    entity-tag               = token
 * @endcode
 *
 * The parsed Suppress-Notify-If-Match header is stored in
 * #sip_suppress_notify_if_match_t structure.
 *
 * @sa @RFC3265, draft-niemi-sip-subnot-etag-01
 *
 * @EXP_1_12_5. Note that #sip_t does not contain @a
 * sip_suppress_notify_if_match field, but sip_suppress_notify_if_match()
 * function should be used for accessing the @b Suppress-Notify-If-Match
 * header structure.
 */

/**@ingroup sip_suppress_notify_if_match
 * @typedef struct sip_suppress_notify_if_match_s \
 * sip_suppress_notify_if_match_t;
 *
 * The structure #sip_suppress_notify_if_match_t contains representation of a
 * SIP @SuppressNotifyIfMatch header.
 *
 * The #sip_suppress_notify_if_match_t is defined as follows:
 * @code
 * typedef struct sip_suppress_notify_if_match_s
 * {
 *   sip_common_t   snim_common[1]; // Common fragment info
 *   sip_error_t   *snim_next;      // Dummy link to next header
 *   char const    *snim_tag;       // entity-tag
 * } sip_suppress_notify_if_match_t;
 * @endcode
 */

#define sip_suppress_notify_if_match_dup_xtra  msg_generic_dup_xtra
#define sip_suppress_notify_if_match_dup_one   msg_generic_dup_one
#define sip_suppress_notify_if_match_update NULL

msg_hclass_t sip_suppress_notify_if_match_class[] =
SIP_HEADER_CLASS(suppress_notify_if_match,
		 "Suppress-Notify-If-Match", "",
		 snim_common, single, suppress_notify_if_match);

issize_t sip_suppress_notify_if_match_d(su_home_t *home,
					sip_header_t *h,
					char *s, isize_t slen)
{
  sip_suppress_notify_if_match_t *snim = (void *)h;
  return msg_token_d(&s, &snim->snim_tag);
}

issize_t sip_suppress_notify_if_match_e(char b[], isize_t bsiz,
					sip_header_t const *h,
					int f)
{
  return msg_generic_e(b, bsiz, h, f);
}

#endif

#if SIP_HAVE_REMOTE_PARTY_ID

/**@SIP_HEADER sip_remote_party_id Remote-Party-ID Header
 *
 * The syntax of the Remote-Party-ID header is described as follows:
 * @code
 *   Remote-Party-ID  = "Remote-Party-ID" HCOLON rpid *(COMMA rpid)
 *
 *   rpid             =  [display-name] LAQUOT addr-spec RAQUOT
 *                                                 *(SEMI rpi-token)
 *
 *   rpi-token        = rpi-screen / rpi-pty-type /
 *                       rpi-id-type / rpi-privacy / other-rpi-token
 *
 *   rpi-screen       = "screen" EQUAL ("no" / "yes")
 *
 *   rpi-pty-type     = "party" EQUAL ("calling" / "called" / token)
 *
 *   rpi-id-type      = "id-type" EQUAL ("subscriber" / "user" /
 *                                                  "term"  / token)
 *
 *   rpi-privacy      = "privacy" EQUAL
 *                      ( rpi-priv-element
 *                        / (LDQUOT rpi-priv-element
 *                          *(COMMA rpi-priv-element) RDQUOT) )
 *
 *   rpi-priv-element = ("full" / "name" / "uri" / "off" / token)
 *                                      ["-" ( "network" / token )]
 *
 *   other-rpi-token  = ["-"] token [EQUAL (token / quoted-string)]
 *
 * @endcode
 *
 * @sa sip_update_default_mclass(), draft-ietf-sip-privacy-04.txt, @RFC3325
 *
 * @NEW_1_12_7. In order to use @b Remote-Party-ID header,
 * initialize the SIP parser with, e.g.,
 * sip_update_default_mclass(sip_extend_mclass(NULL)).
 */

/**@ingroup sip_remote_party_id
 * @typedef typedef struct sip_remote_party_id_s sip_remote_party_id_t;
 *
 * The structure #sip_remote_party_id_t contains representation of SIP
 * @RemotePartyID header.
 *
 * The #sip_remote_party_id_t is defined as follows:
 * @code
 * typedef struct sip_remote_party_id_s {
 *   sip_common_t           rpid_common[1]; // Common fragment info
 *   sip_remote_party_id_t *rpid_next;      // Link to next
 *   char const        *rpid_display;       // Display name
 *   url_t              rpid_url[1];        // URL
 *   sip_param_t const *rpid_params;        // Parameters
 *   // Shortcuts to screen, party, id-type and privacy parameters
 *   char const        *rpid_screen, *rpid_party, *rpid_id_type, *rpid_privacy;
 * } sip_remote_party_id_t;
 * @endcode
 */

extern msg_xtra_f sip_remote_party_id_dup_xtra;
extern msg_dup_f sip_remote_party_id_dup_one;

static msg_update_f sip_remote_party_id_update;

msg_hclass_t sip_remote_party_id_class[] =
SIP_HEADER_CLASS(remote_party_id, "Remote-Party-ID", "",
		 rpid_params, append, remote_party_id);

issize_t sip_remote_party_id_d(su_home_t *home, sip_header_t *h,
			       char *s, isize_t slen)
{
  sip_remote_party_id_t *rpid = (sip_remote_party_id_t *)h;

  while (*s == ',')   /* Ignore empty entries (comma-whitespace) */
    *s = '\0', s += span_lws(s + 1) + 1;

  if (sip_name_addr_d(home, &s,
		      &rpid->rpid_display,
		      rpid->rpid_url,
		      &rpid->rpid_params, NULL) == -1)
    return -1;

  return msg_parse_next_field(home, h, s, slen);
}

issize_t sip_remote_party_id_e(char b[], isize_t bsiz,
			       sip_header_t const *h, int f)
{
  sip_remote_party_id_t const *rpid = (sip_remote_party_id_t *)h;

  return sip_name_addr_e(b, bsiz, f,
			 rpid->rpid_display, 1,
			 rpid->rpid_url,
			 rpid->rpid_params,
			 NULL);
}

/** Calculate size of extra data required for duplicating one
 *  sip_remote_party_id_t header. 
 */
isize_t sip_remote_party_id_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_remote_party_id_t const *rpid = (sip_remote_party_id_t *)h;
  return sip_name_addr_xtra(rpid->rpid_display, 
			    rpid->rpid_url,
			    rpid->rpid_params,
			    offset);
}

/** Duplicate one sip_remote_party_id_t object */
char *sip_remote_party_id_dup_one(sip_header_t *dst,
				  sip_header_t const *src,
				  char *b, isize_t xtra)
{
  sip_remote_party_id_t *rpid = (sip_remote_party_id_t *)dst;
  sip_remote_party_id_t const *o = (sip_remote_party_id_t const *)src;

  return sip_name_addr_dup(&rpid->rpid_display, o->rpid_display,
			   rpid->rpid_url, o->rpid_url,
			   &rpid->rpid_params, o->rpid_params,
			   b, xtra);
}

static int sip_remote_party_id_update(msg_common_t *h, 
				      char const *name, isize_t namelen,
				      char const *value)
{
  sip_remote_party_id_t *rpid = (sip_remote_party_id_t *)h;

  if (name == NULL) {
    rpid->rpid_screen = NULL;
    rpid->rpid_party = NULL;
    rpid->rpid_id_type = NULL;
    rpid->rpid_privacy = NULL;
  }

#define MATCH(s) (namelen == strlen(#s) && !strncasecmp(name, #s, strlen(#s)))

  else if (MATCH(screen))
    rpid->rpid_screen = value;
  else if (MATCH(party))
    rpid->rpid_party = value;
  else if (MATCH(id-type))
    rpid->rpid_id_type = value;
  else if (MATCH(privacy))
    rpid->rpid_privacy = value;

#undef MATCH

  return 0;
}

#endif

#if SIP_HAVE_P_ASSERTED_IDENTITY

/**@SIP_HEADER sip_p_asserted_identity P-Asserted-Identity Header
 *
 * The P-Asserted-Identity header is used used among trusted SIP entities
 * (typically intermediaries) to carry the identity of the user sending a
 * SIP message as it was verified by authentication. It is "defined" in
 * @RFC3325 section 9.1 as follows:
 *
 * @code
 *    PAssertedID = "P-Asserted-Identity" HCOLON PAssertedID-value
 *                    *(COMMA PAssertedID-value)
 *    PAssertedID-value = name-addr / addr-spec
 * @endcode
 *
 * @sa @RFC3325, @PPreferredIdentity
 *
 * @NEW_1_12_7. In order to use @b P-Asserted-Identity header, 
 * initialize the SIP parser with, e.g.,
 * sip_update_default_mclass(sip_extend_mclass(NULL)).
 */

/**@ingroup sip_p_asserted_identity
 * @typedef typedef struct sip_p_asserted_identity_s sip_p_asserted_identity_t;
 *
 * The structure #sip_p_asserted_identity_t contains representation of SIP
 * @PAssertedIdentity header.
 *
 * The #sip_p_asserted_identity_t is defined as follows:
 * @code
 * typedef struct sip_p_asserted_identity_s {
 *   sip_common_t           paid_common[1];   // Common fragment info
 *   sip_p_asserted_identity_t *paid_next;    // Link to next
 *   char const                *paid_display; // Display name
 *   url_t                      paid_url[1];  // URL
 * } sip_p_asserted_identity_t;
 * @endcode
 */

static msg_xtra_f sip_p_asserted_identity_dup_xtra;
static msg_dup_f sip_p_asserted_identity_dup_one;

#define sip_p_asserted_identity_update NULL

msg_hclass_t sip_p_asserted_identity_class[] =
SIP_HEADER_CLASS(p_asserted_identity, "P-Asserted-Identity", "",
		 paid_common, append, p_asserted_identity);

issize_t sip_p_asserted_identity_d(su_home_t *home, sip_header_t *h,
				   char *s, isize_t slen)
{
  sip_p_asserted_identity_t *paid = (sip_p_asserted_identity_t *)h;

  while (*s == ',')   /* Ignore empty entries (comma-whitespace) */
    *s = '\0', s += span_lws(s + 1) + 1;

  if (sip_name_addr_d(home, &s,
		      &paid->paid_display,
		      paid->paid_url,
		      NULL, NULL) == -1)
    return -1;

  return msg_parse_next_field(home, h, s, slen);
}

issize_t sip_p_asserted_identity_e(char b[], isize_t bsiz,
				   sip_header_t const *h, int f)
{
  sip_p_asserted_identity_t const *paid = (sip_p_asserted_identity_t *)h;

  return sip_name_addr_e(b, bsiz, f,
			 paid->paid_display, MSG_IS_CANONIC(f),
			 paid->paid_url,
			 NULL,
			 NULL);
}

isize_t sip_p_asserted_identity_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_p_asserted_identity_t const *paid = (sip_p_asserted_identity_t *)h;

  return sip_name_addr_xtra(paid->paid_display, 
			    paid->paid_url,
			    NULL,
			    offset);
}

/** Duplicate one sip_p_asserted_identity_t object */
char *sip_p_asserted_identity_dup_one(sip_header_t *dst,
				      sip_header_t const *src,
				      char *b, isize_t xtra)
{
  sip_p_asserted_identity_t *paid = (sip_p_asserted_identity_t *)dst;
  sip_p_asserted_identity_t const *o = (sip_p_asserted_identity_t *)src;

  return sip_name_addr_dup(&paid->paid_display, o->paid_display,
			   paid->paid_url, o->paid_url,
			   NULL, NULL,
			   b, xtra);
}

#endif

#if SIP_HAVE_P_PREFERRED_IDENTITY

/**@SIP_HEADER sip_p_preferred_identity P-Preferred-Identity Header
 *
 * The P-Preferred-Identity header is used used among trusted SIP entities
 * (typically intermediaries) to carry the identity of the user sending a
 * SIP message as it was verified by authentication. It is "defined" in
 * @RFC3325 section 9.1 as follows:
 *
 * @code
 *    PPreferredID = "P-Preferred-Identity" HCOLON PPreferredID-value
 *                    *(COMMA PPreferredID-value)
 *    PPreferredID-value = name-addr / addr-spec
 * @endcode
 *
 * @sa @RFC3325, @PAssertedIdentity
 *
 * @NEW_1_12_7. In order to use @b P-Preferred-Identity header,
 * initialize the SIP parser with, e.g.,
 * sip_update_default_mclass(sip_extend_mclass(NULL)).
 */

/**@ingroup sip_p_preferred_identity
 * @typedef typedef struct sip_p_preferred_identity_s sip_p_preferred_identity_t;
 *
 * The structure #sip_p_preferred_identity_t contains representation of SIP
 * @PPreferredIdentity header.
 *
 * The #sip_p_preferred_identity_t is defined as follows:
 * @code
 * typedef struct sip_p_preferred_identity_s {
 *   sip_common_t           ppid_common[1]; // Common fragment info
 *   sip_p_preferred_identity_t *ppid_next; // Link to next
 *   char const        *ppid_display;       // Display name
 *   url_t              ppid_url[1];        // URL
 * } sip_p_preferred_identity_t;
 * @endcode
 */


msg_hclass_t sip_p_preferred_identity_class[] =
SIP_HEADER_CLASS(p_preferred_identity, "P-Preferred-Identity", "",
		 ppid_common, append, p_asserted_identity);

issize_t sip_p_preferred_identity_d(su_home_t *home, sip_header_t *h,
				    char *s, isize_t slen)
{
  return sip_p_asserted_identity_d(home, h, s, slen);
}

issize_t sip_p_preferred_identity_e(char b[], isize_t bsiz,
				    sip_header_t const *h, int f)
{
  return sip_p_asserted_identity_e(b, bsiz, h, f);
}

#endif 
