Logo Search packages:      
Sourcecode: sofia-sip version File versions  Download package

sip_basic.c

/*
 * 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_basic.c
 * @brief Basic SIP headers.
 *
 * Implementation of header classes for basic SIP headers, like request and
 * status lines, payload, @CallID, @CSeq, @Contact, @ContentLength, @Date,
 * @Expires, @From, @Route, @RecordRoute, @To, and @Via.
 *
 * @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/su_alloc.h>
#include <sofia-sip/su_string.h>

#include "sofia-sip/sip_parser.h"
#include <sofia-sip/sip_util.h>
#include <sofia-sip/sip_status.h>

#include <sofia-sip/msg_date.h>

#include <sofia-sip/su_uniqueid.h>

#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#include <limits.h>

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

/**@SIP_HEADER sip_request Request Line
 *
 * The request line is first line in a SIP request message.  Its syntax defined
 * in @RFC3261 as follows:
 *
 * @code
 *    Request-Line   =  Method SP Request-URI SP SIP-Version CRLF
 *    Request-URI    =  SIP-URI / SIPS-URI / absoluteURI
 *    absoluteURI    =  scheme ":" ( hier-part / opaque-part )
 *    hier-part      =  ( net-path / abs-path ) [ "?" query ]
 *    net-path       =  "//" authority [ abs-path ]
 *    abs-path       =  "/" path-segments
 *    opaque-part    =  uric-no-slash *uric
 *    uric           =  reserved / unreserved / escaped
 *    uric-no-slash  =  unreserved / escaped / ";" / "?" / ":" / "@"
 *                      / "&" / "=" / "+" / "$" / ","
 *    path-segments  =  segment *( "/" segment )
 *    segment        =  *pchar *( ";" param )
 *    param          =  *pchar
 *    pchar          =  unreserved / escaped /
 *                      ":" / "@" / "&" / "=" / "+" / "$" / ","
 *    scheme         =  ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
 *    authority      =  srvr / reg-name
 *    srvr           =  [ [ userinfo "@" ] hostport ]
 *    reg-name       =  1*( unreserved / escaped / "$" / ","
 *                      / ";" / ":" / "@" / "&" / "=" / "+" )
 *    query          =  *uric
 *    SIP-Version    =  "SIP" "/" 1*DIGIT "." 1*DIGIT
 * @endcode
 *
 * The parsed request-line is stored in #sip_request_t structure.
 */

/**@ingroup sip_request
 * @typedef typedef struct sip_request_s sip_request_t;
 *
 * The structure #sip_request_t contains representation of SIP request line.
 *
 * The #sip_request_t is defined as follows:
 * @code
 * typedef struct sip_request_s {
 *   sip_common_t     rq_common[1];     // Common fragment info
 *   sip_unknown_t   *rq_next;          // Link to next (dummy)
 *   sip_method_t     rq_method;        // Method enum
 *   char const      *rq_method_name;   // Method name
 *   url_t            rq_url[1];        // RequestURI
 *   char const      *rq_version;       // Protocol version
 * } sip_request_t;
 * @endcode
 */

#define sip_request_insert msg_request_insert

static msg_xtra_f sip_request_dup_xtra;
static msg_dup_f sip_request_dup_one;
#define sip_request_update NULL

msg_hclass_t sip_request_class[] =
SIP_HEADER_CLASS(request, NULL, "", rq_common, single_critical, request);

/**Parse @ref sip_request "request line" from a a SIP message. */
issize_t sip_request_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  sip_request_t *rq = (sip_request_t *)h;
  char *uri, *version;

  if (msg_firstline_d(s, &uri, &version) < 0 || !uri || !version ||
      (rq->rq_method = sip_method_d(&s, &rq->rq_method_name)) < 0 || *s ||
      url_d(rq->rq_url, uri) < 0 ||
      sip_version_d(&version, &rq->rq_version) < 0 || *version)
    return -1;

  return 0;
}

/**Encode @ref sip_request "request line" of a a SIP message. */
issize_t sip_request_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  sip_request_t const *rq = (sip_request_t *)h;

  return snprintf(b, bsiz, "%s " URL_FORMAT_STRING " %s" CRLF,
              rq->rq_method_name,
              URL_PRINT_ARGS(rq->rq_url),
              rq->rq_version);
}

isize_t sip_request_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_request_t const *rq = (sip_request_t *)h;

  offset += url_xtra(rq->rq_url);
  if (!rq->rq_method)
    offset += MSG_STRING_SIZE(rq->rq_method_name);
  offset += sip_version_xtra(rq->rq_version);

  return offset;
}

/** Duplicate one request header. */
char *sip_request_dup_one(sip_header_t *dst, sip_header_t const *src,
                    char *b, isize_t xtra)
{
  sip_request_t *rq = (sip_request_t *)dst;
  sip_request_t const *o = (sip_request_t *)src;
  char *end = b + xtra;

  URL_DUP(b, end, rq->rq_url, o->rq_url);

  if (!(rq->rq_method = o->rq_method))
    MSG_STRING_DUP(b, rq->rq_method_name, o->rq_method_name);
  else
    rq->rq_method_name = o->rq_method_name;
  sip_version_dup(&b, &rq->rq_version, o->rq_version);

  assert(b <= end);

  return b;
}

/**@ingroup sip_request
 *
 * Create a @ref sip_request "request line" object.
 *
 * Create a request line object with
 * method enum @a method, method name @a name, request URI @a uri, and
 * protocol version @a version.  The memory for the header object is
 * allocated from the memory home @a home.
 *
 * @param home     memory home used to allocate #sip_request_t object
 * @param method   method enum
 * @param name     method name (required if method is not well-known)
 * @param uri      request URI
 * @param version  version string (defaults to "SIP/2.0" if NULL)
 *
 * @par Example
 * The following code fragment creates an OPTIONS request object:
 * @code
 *   sip_request_t *rq;
 *   rq = sip_request_create(home, SIP_METHOD_OPTIONS, requestURI, NULL);
 * @endcode

 * @note
 * If you provide an non-NULL @a version string, it is not copied. The
 * version string @b MUST remain constant.
 */
00208 sip_request_t *sip_request_create(su_home_t *home,
                          sip_method_t method, char const *name,
                          url_string_t const *uri,
                          char const *version)
{
  size_t xtra;
  sip_request_t *rq;

  if (method)
    name = sip_method_name(method, name);

  if (!name)
    return NULL;

  if (!method)
    method = sip_method_code(name);

  xtra = url_xtra(uri->us_url) + (method ? 0 : strlen(name) + 1);

  rq = (sip_request_t *)sip_header_alloc(home, sip_request_class, xtra);

  if (rq) {
    char *b = (char *)(rq + 1), *end = b + xtra;

    rq->rq_method      = method;
    rq->rq_method_name = name;
    if (!method)
      MSG_STRING_DUP(b, rq->rq_method_name, name);

    URL_DUP(b, end, rq->rq_url, uri->us_url);

    rq->rq_version = version ? version : SIP_VERSION_CURRENT;
    assert(b == end);
  }

  return rq;
}

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

/**@SIP_HEADER sip_status Status Line
 *
 * The status line is first line in a response message.  It is defined in
 * @RFC3261 as follows:
 *
 * @code
 *    Status-Line     =  SIP-Version SP Status-Code SP Reason-Phrase CRLF
 *    Status-Code     =  Informational
 *                   /   Redirection
 *                   /   Success
 *                   /   Client-Error
 *                   /   Server-Error
 *                   /   Global-Failure
 *                   /   extension-code
 *    extension-code  =  3DIGIT
 *    Reason-Phrase   =  *(reserved / unreserved / escaped
 *                       / UTF8-NONASCII / UTF8-CONT / SP / HTAB)
 * @endcode
 *
 * The parsed status line is stored in #sip_status_t structure.
 */

/**@ingroup sip_status
 * @typedef typedef struct sip_status_s sip_status_t;
 *
 * The structure #sip_status_t contains representation of SIP
 * @ref sip_status "status line".
 *
 * The #sip_status_t is defined as follows:
 * @code
 * typedef struct sip_status_s {
 *   sip_common_t   st_common[1];       // Common fragment info
 *   sip_unknown_t *st_next;            // Link to next (dummy)
 *   char const    *st_version;         // Protocol version
 *   int            st_status;          // Status code
 *   char const    *st_phrase;          // Status phrase
 * } sip_status_t;
 * @endcode
 */


static msg_xtra_f sip_status_dup_xtra;
static msg_dup_f sip_status_dup_one;

#define sip_status_insert msg_status_insert
#define sip_status_update NULL

msg_hclass_t sip_status_class[] =
SIP_HEADER_CLASS(status, NULL, "", st_common, single_critical, status);

/** Parse status line */
issize_t sip_status_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  sip_status_t *st = (sip_status_t *)h;
  char *status, *phrase;
  unsigned long code;

  if (msg_firstline_d(s, &status, &phrase) < 0 ||
      sip_version_d(&s, &st->st_version) < 0 || *s ||
      (code = strtoul(status, &status, 10)) >= INT_MAX || *status)
    return -1;

  st->st_status = code;
  st->st_phrase = phrase;

  return 0;
}

issize_t sip_status_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  sip_status_t const *st = (sip_status_t *)h;
  int status;

  assert(sip_is_status(h));

  status = st->st_status;

  if (status > 999 || status < 100)
    status = 0;

  return snprintf(b, bsiz, "%s %03u %s" CRLF,
              st->st_version,
              status,
              st->st_phrase);
}

/** Extra size of a #sip_status_t object. */
isize_t sip_status_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_status_t const *st = (sip_status_t *)h;
  offset += sip_version_xtra(st->st_version);
  offset += MSG_STRING_SIZE(st->st_phrase);
  return offset;
}

/** Duplicate one status header. */
char *sip_status_dup_one(sip_header_t *dst, sip_header_t const *src,
                   char *b, isize_t xtra)
{
  sip_status_t *st = (sip_status_t *)dst;
  sip_status_t const *o = (sip_status_t *)src;
  char *end = b + xtra;

  sip_version_dup(&b, &st->st_version, o->st_version);
  st->st_status = o->st_status;
  MSG_STRING_DUP(b, st->st_phrase, o->st_phrase);

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

  return b;
}

/**@ingroup sip_status
 *
 * Create a @ref sip_status "status line" object.
 *
 * @param home    memory home used to allocate #sip_status_t object
 * @param status  status code (in range 100 - 699)
 * @param phrase  status phrase (may be NULL)
 * @param version version string (defaults to "SIP/2.0" if NULL)
 *
 * @note
 * If you provide an non-NULL @a version string, it is not copied. The
 * string @b MUST remain constant.
 *
 * @return
 * A pointer to newly created @ref sip_status "status line"
 * structure when successful, or NULL upon an error.
 */
00377 sip_status_t *sip_status_create(su_home_t *home,
                        unsigned status,
                        char const *phrase,
                        char const *version)
{
  sip_status_t *st;

  if (status < 100 || status > 699)
    return NULL;

  if (phrase == NULL && (phrase = sip_status_phrase(status)) == NULL)
    phrase = "";

  if ((st = (sip_status_t *)sip_header_alloc(home, sip_status_class, 0))) {
    st->st_status = status;
    st->st_phrase = phrase;
    st->st_version = version ? version : SIP_VERSION_CURRENT;
  }

  return st;
}

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

/**@SIP_HEADER sip_payload Message Body
 *
 * The payload structure contains the optional message body. The message
 * body stored in the #sip_payload_t structure has no internal structure,
 * but it is accessed as a byte array. Use @ref sdp_parser "SDP parser" to
 * parse SDP content, for instance.
 *
 * The message body is stored in a #sip_payload_t structure.
 */

/**@ingroup sip_payload
 * @typedef typedef struct sip_payload_s sip_payload_t;
 *
 * The structure #sip_payload_t contains representation of SIP message payload.
 *
 * The #sip_payload_t is defined as follows:
 * @code
 * typedef struct sip_payload_s {
 *   msg_common_t    pl_common[1];      // Common fragment info
 *   msg_header_t   *pl_next;           // Next payload (if multipart message)
 *   char           *pl_data;           // Data - may contain NUL
 *   unsigned        pl_len;            // Length of message payload
 * } sip_payload_t;
 * @endcode
 */

#define sip_payload_d        msg_payload_d
#define sip_payload_e        msg_payload_e
#define sip_payload_dup_xtra msg_payload_dup_xtra
#define sip_payload_dup_one  msg_payload_dup_one
#define sip_payload_update   NULL

msg_hclass_t sip_payload_class[] =
SIP_HEADER_CLASS(payload, NULL, "", pl_common, single, payload);

/**@ingroup sip_payload
 *
 * Create a @ref sip_payload "SIP payload" structure.
 *
 * Create a new SIP payload structure. it
 * copies the given data to the the payload data, and NUL terminates it (it
 * allocates one extra byte for NUL).  If a NULL pointer is given as @a data,
 * sip_payload_create() allocates and zeroes a data buffer of @a len bytes.
 *
 * @param home memory home
 * @param data payload data
 * @param len  payload length
 *
 * @return A pointer to newly created
 * payload structure, if successful, and NULL upon an error.
 */
00452 sip_payload_t *sip_payload_create(su_home_t *home, void const *data, isize_t len)
{
  msg_hclass_t *hc = sip_payload_class;
  sip_header_t *h = sip_header_alloc(home, hc, len + 1);
  sip_payload_t *pl = (sip_payload_t *)h;

  if (pl) {
    char *b = sip_header_data(h);
    if (data) {
      memcpy(b, data, len);
      b[len] = 0;
    }
    else {
      memset(b, 0, len + 1);
    }

    h->sh_data = pl->pl_data = b;
    h->sh_len = pl->pl_len = len;
  }

  return pl;
}

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

/**@SIP_HEADER sip_separator Separator Line
 *
 * An empty line separates message headers from the message body (payload).
 * In order to avoid modifying messages with integrity protection, the
 * separator line has its own header structure which is included in the
 * #sip_t structure.
 *
 * The parsed separator line is stored in #sip_separator_t structure.
 */

/**@ingroup sip_separator
 * @typedef typedef struct sip_separator_s sip_separator_t;
 *
 * The structure #sip_separator_t contains representation of separator line
 * between message headers and body.
 *
 * The #sip_separator_t is defined as follows:
 * @code
 * typedef struct sip_separator_s {
 *   msg_common_t    sep_common[1];     // Common fragment info
 *   msg_header_t   *sep_next;          // Pointer to next header
 *   char            sep_data[4];       // NUL-terminated separator
 * } sip_separator_t;
 * @endcode
 */

#define sip_separator_d msg_separator_d
#define sip_separator_e msg_separator_e
#define sip_separator_insert msg_separator_insert

msg_hclass_t sip_separator_class[] =
SIP_HEADER_CLASS(separator, NULL, "", sep_common, single, any);

/**@ingroup sip_separator
 *
 * Create a @ref sip_separator "SIP separator line" structure.
 */
00514 sip_separator_t *sip_separator_create(su_home_t *home)
{
  sip_separator_t *sep = (sip_separator_t *)
    sip_header_alloc(home, sip_separator_class, 0);

  if (sep)
    strcpy(sep->sep_data, CRLF);

  return sep;
}

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

/**@SIP_HEADER sip_unknown Unknown Headers
 *
 * The unknown headers are handled with #sip_unknown_t structure. The
 * unknown header name is stored in @a un_name field and the header field
 * following the colon is stored in @a un_value field.
 *
 * @note It is possible to speed up parsing process by creating a parser
 * which does understand only a minimum number of headers. If such a parser
 * is used, some well-known headers are regarded as unknown and put into
 * list of unknown headers.
 */

/**@ingroup sip_unknown
 * @typedef typedef struct sip_unknown_s sip_unknown_t;
 *
 * The structure #sip_unknown_t contains representation of unknown headers.
 *
 * The #sip_unknown_t is defined as follows:
 * @code
 * typedef struct msg_unknown_s {
 *   msg_common_t    un_common[1];  // Common fragment info
 *   msg_unknown_t  *un_next;       // Link to next unknown header
 *   char const     *un_name;       // Header name
 *   char const     *un_value;      // Header field value
 * } sip_unknown_t;
 * @endcode
 */

#define sip_unknown_dup_xtra msg_unknown_dup_xtra
#define sip_unknown_dup_one  msg_unknown_dup_one
#define sip_unknown_update NULL

msg_hclass_t sip_unknown_class[] =
SIP_HEADER_CLASS(unknown, "", "", un_common, append, unknown);

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

issize_t sip_unknown_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  return msg_unknown_e(b, bsiz, h, flags);
}

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

/**@SIP_HEADER sip_error Erroneous Headers
 *
 * The erroneous headers are stored in #sip_error_t structure.
 *
 * @note Other headers (like duplicate @ContentLength headers) may be put
 * into the list of erroneous headers (@c sip->sip_error). If the list of
 * erroneous headers is processed, the header type must be validated first
 * by calling sip_is_error() (or by other relevant tests).
 */

/**@ingroup sip_error
 * @typedef typedef msg_error_t sip_error_t;
 * The structure #sip_error_t contains representation of error headers.
 *
 * The #sip_error_t is defined as follows:
 * @code
 * typedef struct msg_error_s {
 *   msg_common_t    er_common[1];  // Common fragment info
 *   msg_error_t    *er_next;       // Link to next header
 *   char const     *er_name;       // Name of bad header (if any)
 * } sip_error_t;
 * @endcode
 */

msg_hclass_t sip_error_class[] =
SIP_HEADER_CLASS(error, NULL, "", er_common, append, any);

issize_t sip_error_d(su_home_t *home, msg_header_t *h, char *s, isize_t slen)
{
  return 0;
}

issize_t sip_error_e(char b[], isize_t bsiz, msg_header_t const *h, int flags)
{
  /* There is no way to encode an erroneous header */
  return 0;
}

/* ====================================================================== */
/*
 * addr           = ("To" | "t" | "From" | "f") ":"
 *                  ( name-addr | addr-spec ) *( ";" addr-params )
 * name-addr      = [ display-name ] "<" addr-spec ">"
 * addr-spec      = SIP-URL | URI
 * display-name   = *token | quoted-string
 */

/**Parse @e name-addr.
 *
 * Parses <i>( name-addr | addr-spec )</i> construct on @Contact, @From,
 * @To, and other compatible headers. It splits the argument string in
 * four parts:
 *
 * @par
 * @e [display-name] @e addr-spec @e [parameters] @e [comment] @e [ss]
 *
 * @param home           pointer to memory home
 * @param inout_s        pointer to pointer to string to be parsed
 * @param return_display value-result parameter for @e display-name
 * @param return_url     value-result parameter for @e addr-spec
 * @param return_params  value-result paramater for @e parameters
 * @param return_comment value-result parameter for @e comment
 *
 * @note After succesful call to the function @c sip_name_addr_d(), *ss
 * contains pointer to the first character not beloging to @e name-addr,
 * most probably a comma. If that character is a separator, the last parameter
 * may not be NUL (zero) terminated. So, after examining value of @a **ss,
 * the calling function @b MUST set it to NUL.
 *
 * @retval 0 if successful
 * @retval -1 upon an error
 *
 * @sa @From, @To, @Contact
 */
00648 issize_t sip_name_addr_d(su_home_t *home,
                   char **inout_s,
                   char const **return_display,
                   url_t *return_url,
                   msg_param_t const **return_params,
                   char const **return_comment)
{
  char c, *s = *inout_s;
  char *display = NULL, *addr_spec = NULL;
  size_t n;

  if (*s == '\0')       /* Empty string */
    return -1;

  if (return_display && *s == '"') {
    /* Quoted string */
    if (msg_quoted_d(&s, &display) == -1)
      return -1;

    /* Now, we should have a '<' in s[0] */
    if (s[0] != '<')
      return -1;
    s++[0] = '\0';            /* NUL terminate quoted string... */
    n = strcspn(s, ">");
    addr_spec = s; s += n;
    if (*s) *s++ = '\0'; else return -1;
  }
  else {
    if (return_display)
      n = span_token_lws(s);
    else
      n = 0;

    if (s[n] == '<') {
      /* OK, we got a display name */
      display = s; s += n + 1;
      /* NUL terminate display name */
      while (n > 0 && IS_LWS(display[n - 1]))
      n--;
      if (n > 0)
      display[n] = '\0';
      else
      display = "";

      n = strcspn(s, ">");
      addr_spec = s; s += n; if (*s) *s++ = '\0'; else return -1;
    }
    else {
      /* addr-spec only */
      addr_spec = s;
      /**@sa
       * Discussion about comma, semicolon and question mark in
       * @RFC3261 section 20.10.
       */
      if (return_params)
      n = strcspn(s, " \t,;?");     /* DO NOT accept ,;? in URL */
      else
      /* P-Asserted-Identity and friends */
      n = strcspn(s, " ,"); /* DO NOT accept , in URL */
      s += n;
      if (IS_LWS(*s))
      *s++ = '\0';
    }
  }

  skip_lws(&s);

  if (return_display)
    *return_display = display;

  /* Now, url may still not be NUL terminated, e.g., if
   * it is like "Contact: url:foo,sip:bar,sip:zunk"
   */
  c = *s; *s = '\0';          /* terminate temporarily */
  /* Do not accept an empty URL */
  if (addr_spec[0] == '\0')
    return -1;
  if (url_d(return_url, addr_spec) == -1)
    return -1;
  *s = c;               /* return terminator */

  *inout_s = s;

  if (c == ';' && return_params)
    if (msg_params_d(home, inout_s, return_params) == -1)
      return -1;

  if (**inout_s == '(' && return_comment)
    if (msg_comment_d(inout_s, return_comment) == -1)
      return -1;

  return 0;
}

/**Encode @e name-addr and parameter list.
 *
 * Encodes @e name-addr headers, like @From, @To, @CallInfo, @ErrorInfo,
 * @Route, and @RecordRoute.
 *
 * @param b        buffer to store the encoding result
 * @param bsiz     size of the buffer @a b
 * @param flags    encoding flags
 * @param display  display name encoded before the @a url (may be NULL)
 * @param brackets if true, use always brackets around @a url
 * @param url      pointer to URL structure
 * @param params   pointer to parameter list (may be NULL)
 * @param comment  comment string encoded after others (may be NULL)
 *
 * @return
 * Returns number of characters in encoding, excluding the
 * final NUL.
 *
 * @note
 * The encoding result may be incomplete if the buffer size is not large
 * enough to store the whole encoding result.
 */
00764 issize_t sip_name_addr_e(char b[], isize_t bsiz,
                   int flags,
                   char const *display,
                   int brackets, url_t const url[],
                   msg_param_t const params[],
                   char const *comment)
{
  int const compact = MSG_IS_COMPACT(flags);
  char const *u;
  char *b0 = b, *end = b + bsiz;

  brackets = brackets || display ||
    (url && (url->url_params ||
           url->url_headers ||
           ((u = url->url_user) && u[strcspn(u, ";,?")]) ||
           ((u = url->url_password) && u[strcspn(u, ",")])));

  if (display && display[0]) {
    MSG_STRING_E(b, end, display);
    if (!compact) MSG_CHAR_E(b, end, ' ');
  }
  if (url) {
    if (brackets) MSG_CHAR_E(b, end, '<');
    URL_E(b, end, url);
    if (brackets) MSG_CHAR_E(b, end, '>');
  }

  MSG_PARAMS_E(b, end, params, flags);

  if (comment) {
    if (!compact) MSG_CHAR_E(b, end, ' ');
    MSG_CHAR_E(b, end, '(');
    MSG_STRING_E(b, end, comment);
    MSG_CHAR_E(b, end, ')');
  }

  MSG_TERM_E(b, end);

  return b - b0;
}

/** Calculate the extra size needed to duplicate a name-addr-params construct.
 *
 * @param display  display name (may be NULL)
 * @param addr     pointer to URL structure
 * @param params   pointer to parameter list (may be NULL)
 * @param offset   base offset
 *
 * @retval Size of duplicated name-addr-params construct, including base offset.
 *
 * @NEW_1_12_7.
 */
00816 isize_t sip_name_addr_xtra(char const *display, url_t const *addr,
                     msg_param_t const params[],
                     isize_t offset)
{
  SIP_PARAMS_SIZE(offset, params);
  offset += SIP_STRING_SIZE(display);
  offset += url_xtra(addr);
  return offset;
}

/**Duplicate a name-addr-params construct.
 *
 * @param d_display value-result parameter for copied @e name (may be NULL)
 * @param display   display name (may be NULL)
 * @param d_addr    value-result parameter for copied @e address
 * @param addr      pointer to URL address structure
 * @param d_params value-result parameter for copied parameters (may be NULL)
 * @param params   pointer to parameter list (may be NULL)
 * @param b        pointer to memory pool
 * @param xtra     size of the memory pool
 *
 * @retval End of the memory area used.
 *
 * @NEW_1_12_7.
 */
00841 char *sip_name_addr_dup(char const **d_display, char const *display,
                  url_t *d_addr, url_t const *addr,
                  msg_param_t const **d_params, msg_param_t const params[],
                  char *b, isize_t xtra)
{
  char *end = b + xtra;

  if (d_params)
    b = msg_params_dup(d_params, params, b, xtra);

  URL_DUP(b, end, d_addr, addr);

  if (d_display)
    MSG_STRING_DUP(b, *d_display, display);

  assert(b <= end);

  return b;
}


/** Parse @To or @From headers */
static issize_t sip_addr_d(su_home_t *home,
                     sip_header_t *h,
                     char *s,
                     isize_t slen)
{
  sip_addr_t *a = (sip_addr_t *)h;
  char const *comment = NULL;
  if (sip_name_addr_d(home,
                  &s,
                  &a->a_display,
                  a->a_url,
                  &a->a_params,
                  &comment) == -1
      || *s /* XXX - something extra? */)
    return -1;

  a->a_tag = msg_params_find(a->a_params, "tag=");

  return 0;
}

static int sip_addr_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  sip_addr_t const *a = (sip_addr_t const *)h;

  return sip_name_addr_e(b, bsiz,
                   flags,
                   a->a_display,
                   MSG_IS_CANONIC(flags), a->a_url,
                   a->a_params,
                   NULL);
}

/**
 * Extra dup size of a sip_addr_t object.
 *
 * This function calculates extra size required when duplicating a
 * sip_addr_t object.
 *
 * @param a pointer to a sip_addr_t object
 *
 * @return
 *   Size of strings related to sip_addr_t object.
 */
static
isize_t sip_addr_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_addr_t const *a = (sip_addr_t const *)h;

  return sip_name_addr_xtra(a->a_display,
                      a->a_url,
                      a->a_params,
                      offset);
}

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

  return sip_name_addr_dup(&a->a_display, o->a_display,
                     a->a_url, o->a_url,
                     &a->a_params, o->a_params,
                     b, xtra);
}

/** Update parameters in sip_addr_t object */
static int sip_addr_update(msg_common_t *h,
                     char const *name, isize_t namelen,
                     char const *value)
{
  sip_addr_t *a = (sip_addr_t *)h;

  if (name == NULL) {
    a->a_tag = NULL;
  }
  else if (namelen == strlen("tag") && su_casenmatch(name, "tag", namelen)) {
    a->a_tag = value;
  }

  return 0;
}

/** Create an address header object from URL */
static sip_addr_t *
sip_addr_make_url(su_home_t *home, msg_hclass_t *hc, url_string_t const *us)
{
  size_t n;
  sip_header_t *h;

  n = url_xtra(us->us_url);
  h = sip_header_alloc(home, hc, n);

  if (h) {
    sip_addr_t *a = (sip_to_t *)h;
    char *s2 = sip_header_data(h);

    if ((size_t)url_dup(s2, n, a->a_url, us->us_url) == n)
      return a;

    su_free(home, h);
  }

  return NULL;
}

/** Add a tag to address structure. */
static
int sip_addr_tag(su_home_t *home, sip_addr_t *a, char const *tag)
{
  if (a && tag) {
    msg_param_t value = strchr(tag, '=');

    if (value)
      value = strchr(value, '=') + 1;
    else
      value = tag;

    if (a->a_tag) {
      if (su_casematch(a->a_tag, value))
      return 0;
      else
      return -1;
    }

    if (tag == value)
      tag = su_sprintf(home, "tag=%s", tag);
    else
      tag = su_strdup(home, tag);

    if (tag)
      if (msg_header_replace_param(home, a->a_common, tag) >= 0)
      return 0;
  }

  return -1;
}

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

/**@SIP_HEADER sip_call_id Call-ID Header
 *
 * The @b Call-ID header uniquely identifies a particular invitation or all
 * registrations of a particular client. It is defined in @RFC3261 as
 * follows:
 *
 * @code
 *    Call-ID  =  ( "Call-ID" / "i" ) HCOLON callid
 *    callid   =  word [ "@" word ]
 *    word        =  1*(alphanum / "-" / "." / "!" / "%" / "*" /
 *                   "_" / "+" / "`" / "'" / "~" / "(" / ")" / "<" / ">" /
 *                   ":" / "\" / DQUOTE / "/" / "[" / "]" / "?" / "{" / "}" )
 * @endcode
 *
 * The parsed Call-ID Header is stored in #sip_call_id_t structure.
 */

/**@ingroup sip_call_id
 * @typedef typedef struct sip_call_id_s sip_call_id_t;
 *
 * The structure #sip_call_id_t contains representation of SIP @CallID
 * header.
 *
 * The #sip_call_id_t is defined as follows:
 * @code
 * typedef struct sip_call_id_s {
 *   sip_common_t   i_common[1];        // Common fragment info
 *   sip_call_id_t *i_next;             // Link to next (dummy)
 *   char const    *i_id;               // ID value
 *   uint32_t       i_hash;             // Hash value (always nonzero)
 * } sip_call_id_t;
 * @endcode
 */

static msg_xtra_f sip_call_id_dup_xtra;
static msg_dup_f sip_call_id_dup_one;
#define sip_call_id_update NULL

msg_hclass_t sip_call_id_class[] =
SIP_HEADER_CLASS(call_id, "Call-ID", "i", i_common, single, call_id);

issize_t sip_call_id_d(su_home_t *home,
                   sip_header_t *h,
                   char *s,
                   isize_t slen)
{
  sip_call_id_t *i = (sip_call_id_t *)h;

  i->i_id = s; /* XXX - why not sip_word_at_word_d(&s); */
  i->i_hash = msg_hash_string(s);

  return 0;
}


issize_t sip_call_id_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  sip_call_id_t const *i = (sip_call_id_t *)h;
  size_t n = strlen(i->i_id);

  if (bsiz > n)
    strcpy(b, i->i_id);

  return (issize_t)n;
}

/** Extra size of a #sip_call_id_t object. */
isize_t sip_call_id_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_call_id_t const *i = (sip_call_id_t *)h;
  return offset + MSG_STRING_SIZE(i->i_id);
}

/**Duplicate a sip_call_id object.
 *
 * Duplicate (copy deeply) a single #sip_call_id_t header object.
 *
 * @param dst   pointer to newly allocated header object
 * @param src   pointer to a header object to be duplicated
 * @param b     memory buffer used to copy external references
 * @param xtra  number bytes in buffer @a b
 *
 * @return Pointer to the new copy of #sip_call_id_t object, or @c NULL
 * upon an error.
 */
char *sip_call_id_dup_one(sip_header_t *dst, sip_header_t const *src,
                    char *b, isize_t xtra)
{
  sip_call_id_t *i = (sip_call_id_t *)dst;
  sip_call_id_t const *o = (sip_call_id_t *)src;
  char *end = b + xtra;

  MSG_STRING_DUP(b, i->i_id, o->i_id);
  if (!(i->i_hash = o->i_hash))
    i->i_hash = msg_hash_string(i->i_id);
  assert(b <= end); (void)end;

  return b;
}

/**@ingroup sip_call_id
 *
 * Create a @CallID header object.
 *
 * Create a Call-ID header object with a new unique value. It uses
 * su_guid_generate() function to generate the value. If the local host name
 * @a domain is specified, it is prepended to the generated value instead of
 * local MAC address.

 * @param home        memory home
 * @param domain      local domain name
 *
 * @return A pointer to newly created @CallID header object when
 * successful or NULL upon an error.
 *
 * @sa su_guid_generate(), su_guid_sprintf()
 */
01124 sip_call_id_t *sip_call_id_create(su_home_t *home, char const *domain)
{
  sip_call_id_t *i;
  size_t xtra = su_guid_strlen + 1 + (domain ? strlen(domain) + 1 : 0);

  i = (sip_call_id_t *)sip_header_alloc(home, sip_call_id_class, xtra);

  if (i) {
    char *b;
    su_guid_t guid[1];

    i->i_id = b = (char *)(i + 1);

    su_guid_generate(guid);
    /*
     * Guid looks like "NNNNNNNN-NNNN-NNNN-NNNN-XXXXXXXXXXXX"
     * where NNNNNNNN-NNNN-NNNN-NNNN is timestamp and XX is MAC address
     * (but we use usually random ID for MAC because we do not have
     *  guid generator available for all processes within node)
     */
    su_guid_sprintf(b, su_guid_strlen + 1, guid);

    /* If we have a domain name don't include MAC address at the end of guid */
    if (domain) {
      b[8 + 5 + 5 + 5] = '@';
      strcpy(b + 8 + 5 + 5 + 5 + 1, domain);
    }

    i->i_hash = msg_hash_string(i->i_id);
  }

  return i;

}

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

/**@SIP_HEADER sip_cseq CSeq Header
 *
 * The CSeq header (command sequence) uniquely identifies transactions
 * within a dialog.  It is defined in @RFC3261 as follows:
 *
 * @code
 *    CSeq              =  "CSeq" HCOLON 1*DIGIT LWS Method
 *    Method            =  INVITEm / ACKm / OPTIONSm / BYEm
 *                         / CANCELm / REGISTERm
 *                         / extension-method
 *    extension-method  =  token
 * @endcode
 *
 * The parsed CSeq header is stored in #sip_cseq_t structure.
 */

/**@ingroup sip_cseq
 * @typedef typedef struct sip_cseq_s sip_cseq_t;
 *
 * The structure #sip_cseq_t contains representation of SIP @CSeq header.
 *
 * The #sip_cseq_t is defined as follows:
 * @code
 * typedef struct sip_cseq_s {
 *   sip_common_t   cs_common[1];       // Common fragment info
 *   sip_error_t   *cs_next;            // Link to next (dummy)
 *   uint32_t       cs_seq;             // Sequence number
 *   sip_method_t   cs_method;          // Method enum
 *   char const    *cs_method_name;     // Method name
 * } sip_cseq_t;
 * @endcode
 */

static msg_xtra_f sip_cseq_dup_xtra;
static msg_dup_f sip_cseq_dup_one;
#define sip_cseq_update NULL

msg_hclass_t sip_cseq_class[] =
SIP_HEADER_CLASS(cseq, "CSeq", "", cs_common, single, cseq);

issize_t sip_cseq_d(su_home_t *home,
                sip_header_t *h,
                char *s,
                isize_t slen)
{
  sip_cseq_t *cs = (sip_cseq_t *)h;

  if (msg_uint32_d(&s, &cs->cs_seq) < 0)
    return -1;

  if (*s) {
    if ((cs->cs_method = sip_method_d(&s, &cs->cs_method_name)) >= 0)
      return 0;
  }

  return -1;
}


issize_t sip_cseq_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  sip_cseq_t const *cs = (sip_cseq_t *)h;

  assert(sip_is_cseq(h));

  return snprintf(b, bsiz, "%u %s", cs->cs_seq, cs->cs_method_name);
}

isize_t sip_cseq_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_cseq_t const *cs = (sip_cseq_t *)h;
  if (!cs->cs_method)
    return offset + MSG_STRING_SIZE(cs->cs_method_name);
  else
    return offset;
}

char *sip_cseq_dup_one(sip_header_t *dst, sip_header_t const *src,
                   char *b, isize_t xtra)
{
  sip_cseq_t *cs = (sip_cseq_t *)dst;
  sip_cseq_t const *o = (sip_cseq_t *)src;
  char *end = b + xtra;

  if (!(cs->cs_method = o->cs_method))
    MSG_STRING_DUP(b, cs->cs_method_name, o->cs_method_name);
  else
    cs->cs_method_name = o->cs_method_name;
  cs->cs_seq = o->cs_seq;

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

  return b;
}

/**@ingroup sip_cseq
 *
 * Create a @CSeq header object.
 *
 * Create a @CSeq header object with the
 * sequence number @a seq, method enum @a method and method name @a
 * method_name.  The memory for the header object is allocated from the
 * memory home @a home.
 *
 * @param home        memory home
 * @param seq         sequence number
 * @param method      method enum
 * @param method_name method name (required if method is not well-known)
 *
 * @par Example
 * The following code fragment creates a cseq object for OPTIONS request:
 * @code
 *   sip_cseq_t *cseq;
 *   cseq = sip_cseq_create(home, agent->seq++, SIP_METHOD_OPTIONS);
 * @endcode
 *
 * @return
 * A pointer to newly created @CSeq
 * header object when successful or NULL upon an error.
 */
01281 sip_cseq_t *sip_cseq_create(su_home_t *home,
                      uint32_t seq,
                      unsigned method,
                      char const *method_name)
{
  size_t xtra;
  sip_cseq_t *cs;

  if (method)
    method_name = sip_method_name((sip_method_t)method, method_name);

  if (method_name == NULL)
    return NULL;

  xtra = (method ? 0 : (strlen(method_name) + 1));

  cs = (sip_cseq_t *)sip_header_alloc(home, sip_cseq_class, xtra);

  if (cs) {
    cs->cs_seq = seq;
    cs->cs_method = (sip_method_t)method;
    if (!method)
      method_name = strcpy((char *)(cs + 1), method_name);
    cs->cs_method_name = method_name;
  }
  return cs;
}


/* ====================================================================== */
/**@SIP_HEADER sip_contact Contact Header
 *
 * The Contact header contain a list of URLs used to redirect future
 * requests.  Its syntax is defined in @RFC3261 as follows:
 *
 * @code
 *    Contact            =  ("Contact" / "m" ) HCOLON
 *                          ( STAR / (contact-param *(COMMA contact-param)))
 *    contact-param      =  (name-addr / addr-spec) *(SEMI contact-params)
 *    name-addr          =  [ display-name ] LAQUOT addr-spec RAQUOT
 *    addr-spec          =  SIP-URI / SIPS-URI / absoluteURI
 *    display-name       =  *(token LWS)/ quoted-string
 *    contact-params     =  c-p-q / c-p-expires
 *                          / contact-extension
 *    c-p-q              =  "q" EQUAL qvalue
 *    c-p-expires        =  "expires" EQUAL delta-seconds
 *    contact-extension  =  generic-param
 *    delta-seconds      =  1*DIGIT
 * @endcode
 *
 * @note
 * The @RFC2543 syntax allowed <comment>. We accept it, but don't encode it.
 *
 * Each parsed Contact header field is stored in #sip_contact_t structure.
 */

/**@ingroup sip_contact
 * @typedef typedef struct sip_contact_s sip_contact_t;
 *
 * The structure #sip_contact_t contains representation of SIP @Contact
 * header.
 *
 * The #sip_contact_t is defined as follows:
 * @code
 * typedef struct sip_contact_s {
 *   sip_common_t        m_common[1];   // Common fragment info
 *   sip_contact_t      *m_next;        // Link to next
 *   char const         *m_display;     // Display name
 *   url_t               m_url[1];      // SIP URL
 *   msg_param_t const  *m_params;      // List of contact-params
 *   char const         *m_comment;     // Comment
 *
 *   char const         *m_q;           // Priority
 *   char const         *m_expires;     // Expiration time
 * } sip_contact_t;
 * @endcode
 *
 * @note The <comment> field @ref sip_contact_s::m_comment "m_comment" is
 * deprecated: it is parsed but not included in encoding.
 */

static msg_xtra_f sip_contact_dup_xtra;
static msg_dup_f sip_contact_dup_one;
static msg_update_f sip_contact_update;

/** @showinitializer */
msg_hclass_t sip_contact_class[] =
  /*
   * Cut through the fog of macros
   * SIP_HEADER_CLASS(contact, "Contact", "m", m_params, append, contact);
   * and show here how the msg_hclass_t is initialized
   */
  {{
    /* hc_hash: */     sip_contact_hash,
    /* hc_parse: */    sip_contact_d,
    /* hc_print: */    sip_contact_e,
    /* hc_dxtra: */    sip_contact_dup_xtra,
    /* hc_dup_one: */  sip_contact_dup_one,
    /* hc_update: */   sip_contact_update,
    /* hc_name: */     "Contact",
    /* hc_len: */      sizeof("Contact") - 1,
    /* hc_short: */    "m",
    /* hc_size: */     MSG_ALIGN(sizeof(sip_contact_t), sizeof(void*)),
    /* hc_params: */   offsetof(sip_contact_t, m_params),
    /* hc_kind:   */     msg_kind_append,
    /* hc_critical: */ 0
   }};

issize_t sip_contact_d(su_home_t *home,
                   sip_header_t *h,
                   char *s,
                   isize_t slen)
{
  sip_contact_t *m = (sip_contact_t *)h;

  assert(h);

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

  if (sip_name_addr_d(home, &s, &m->m_display, m->m_url,
                  &m->m_params, &m->m_comment) == -1)
    return -1;

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


issize_t sip_contact_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  sip_contact_t const *m = (sip_contact_t *)h;
  int always_lt_gt = MSG_IS_CANONIC(flags) && m->m_url->url_type != url_any;

  assert(sip_is_contact(h));

  return sip_name_addr_e(b, bsiz, flags,
                   m->m_display, always_lt_gt, m->m_url,
                   m->m_params,
                   NULL /* m->m_comment */);
}


isize_t sip_contact_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_contact_t const *m = (sip_contact_t *)h;

  return sip_name_addr_xtra(m->m_display,
                      m->m_url,
                      m->m_params,
                      offset)
    + MSG_STRING_SIZE(m->m_comment);
}

char *sip_contact_dup_one(sip_header_t *dst, sip_header_t const *src,
                    char *b, isize_t xtra)
{
  sip_contact_t *m = (sip_contact_t *)dst;
  sip_contact_t const *o = (sip_contact_t *)src;

  b = sip_name_addr_dup(&m->m_display, o->m_display,
                  m->m_url, o->m_url,
                  &m->m_params, o->m_params,
                  b, xtra);
  MSG_STRING_DUP(b, m->m_comment, o->m_comment);
  return b;
}

/** Update parameter in #sip_contact_t */
static int sip_contact_update(msg_common_t *h,
                        char const *name, isize_t namelen,
                        char const *value)
{
  sip_contact_t *m = (sip_contact_t *)h;

  if (name == NULL) {
    m->m_q = NULL;
    m->m_expires = NULL;
  }
  else if (namelen == 1 && su_casenmatch(name, "q", 1)) {
    /* XXX - check for invalid value? */
    m->m_q = value;
  }
  else if (namelen == strlen("expires") &&
         su_casenmatch(name, "expires", namelen)) {
    m->m_expires = value;
  }

  return 0;
}

/**@ingroup sip_contact
 *
 * Add a parameter to a @Contact header object
 *
 * Add a parameter to a @Contact
 * object. It does not copy the contents of the string @c param.
 *
 * @note This function @b does @b not duplicate @p param.
 *
 * @param home   memory home
 * @param m      #sip_contact_t object
 * @param param  parameter string
 *
 * @return 0 when successful, and -1 upon an error.
 *
 * @deprecated Use msg_header_replace_param() directly.
 */
01488 int sip_contact_add_param(su_home_t *home,
                    sip_contact_t *m,
                    char const *param)
{
  return msg_header_replace_param(home, m->m_common, param);
}

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

/**@SIP_HEADER sip_content_length Content-Length Header
 *
 * The Content-Length header indicates the size of the message-body in
 * decimal number of octets.  Its syntax is defined in @RFC3261 as
 * follows:
 *
 * @code
 *    Content-Length  =  ( "Content-Length" / "l" ) HCOLON 1*DIGIT
 * @endcode
 *
 * The parsed Content-Length header is stored in #sip_content_length_t
 * structure.
 */

/**@ingroup sip_content_length
 * @typedef typedef struct sip_content_length_s sip_content_length_t;
 *
 * The structure #sip_content_length_t contains representation of SIP
 * @ContentLength header.
 *
 * The #sip_content_length_t is defined as follows:
 * @code
 * typedef struct sip_content_length_s {
 *   sip_common_t   l_common[1];        // Common fragment info
 *   sip_error_t   *l_next;             // Dummy link to next
 *   uint32_t       l_length;           // Message body length in bytes
 * } sip_content_length_t;
 * @endcode
 */

msg_hclass_t sip_content_length_class[] =
SIP_HEADER_CLASS(content_length, "Content-Length", "l", l_common,
             single_critical, any);

issize_t sip_content_length_d(su_home_t *home,
                        sip_header_t *h,
                        char *s,
                        isize_t slen)
{
  sip_content_length_t *l = (sip_content_length_t *)h;
  issize_t retval = msg_uint32_d(&s, &l->l_length);
  if (*s)
    retval = -1;
  return retval;
}

issize_t sip_content_length_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  sip_content_length_t const *l = (sip_content_length_t const *)h;
  assert(sip_is_content_length(h));
  return snprintf(b, bsiz, "%lu", (unsigned long)l->l_length);
}

/**@ingroup sip_content_length
 *
 * Create a @ContentLength header object.
 *
 * Create a @ContentLength
 * header object with the value @a n.  The memory for the header is
 * allocated from the memory home @a home.
 *
 * @param home  memory home
 * @param n     payload size in bytes
 *
 * @return
 * A pointer to newly created @ContentLength header object when successful
 * or NULL upon an error.
 */
01565 sip_content_length_t *sip_content_length_create(su_home_t *home, uint32_t n)
{
  sip_content_length_t *l = (sip_content_length_t *)
    sip_header_alloc(home, sip_content_length_class, 0);

  if (l)
    l->l_length = n;

  return l;
}

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

/**@SIP_HEADER sip_date Date Header
 *
 * The Date header field reflects the time when the request or response was
 * first sent.  Its syntax is defined in @RFC3261 and @RFC2616 section 14.18 as
 * follows:
 *
 * @code
 *    Date          =  "Date" HCOLON SIP-date
 *    SIP-date      =  rfc1123-date
 *    rfc1123-date  =  wkday "," SP date1 SP time SP "GMT"
 *    date1         =  2DIGIT SP month SP 4DIGIT
 *                     ; day month year (e.g., 02 Jun 1982)
 *    time          =  2DIGIT ":" 2DIGIT ":" 2DIGIT
 *                     ; 00:00:00 - 23:59:59
 *    wkday         =  "Mon" / "Tue" / "Wed"
 *                     / "Thu" / "Fri" / "Sat" / "Sun"
 *    month         =  "Jan" / "Feb" / "Mar" / "Apr"
 *                     / "May" / "Jun" / "Jul" / "Aug"
 *                     / "Sep" / "Oct" / "Nov" / "Dec"
 * @endcode
 *
 * The parsed Date header is stored in #sip_date_t structure.
 */

/**@ingroup sip_date
 * @typedef typedef struct sip_date_s sip_date_t;
 *
 * The structure #sip_date_t contains representation of SIP @Date header.
 *
 * The #sip_date_t is defined as follows:
 * @code
 * typedef struct sip_date_s {
 *   sip_common_t   d_common[1];        // Common fragment info
 *   sip_date_t    *d_next;             // Link to next (dummy)
 *   sip_time_t     d_time;             // Seconds since Jan 1, 1900
 * } sip_date_t;
 * @endcode
 */

msg_hclass_t sip_date_class[] =
SIP_HEADER_CLASS(date, "Date", "", d_common, single, any);

issize_t sip_date_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  sip_date_t *date = (sip_date_t *)h;

  if (msg_date_d((char const **)&s, &date->d_time) < 0 || *s)
    return -1;
  else
    return 0;
}

issize_t sip_date_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  sip_date_t const *date = (sip_date_t *)h;

  return msg_date_e(b, bsiz, date->d_time);
}

/**@ingroup sip_date
 * @brief Create an @Date header object.
 *
 * Create a @Date header object with
 * the date @a date.  If @date is 0, current time (as returned by sip_now())
 * is used.
 *
 * @param home   memory home
 * @param date   date expressed as seconds since Mon, 01 Jan 1900 00:00:00
 *
 * @return
 * A pointer to newly created @Date header object when successful, or NULL
 * upon an error.
 */
01651 sip_date_t *sip_date_create(su_home_t *home, sip_time_t date)
{
  sip_date_t *d = (sip_date_t *)sip_header_alloc(home, sip_date_class, 0);

  if (d) {
    if (date == 0)
      date = sip_now();
    d->d_time = date;
  }

  return d;
}

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

/**@SIP_HEADER sip_expires Expires Header
 *
 * The Expires header field gives the date and time after which the message
 * content expires.  Its syntax is defined in @RFC3261 as follows:
 *
 * @code
 *    Expires     =  "Expires" HCOLON delta-seconds
 * @endcode
 *
 * Note that the first SIP revision (@RFC2543) also allowed absolute time in
 * Expires.
 *
 * The parsed Expires header is stored in #sip_expires_t structure.
 */

/**@ingroup sip_expires
 * @typedef typedef struct sip_expires_s sip_expires_t;
 *
 * The structure #sip_expires_t contains representation of SIP @Expires
 * header.
 *
 * The #sip_expires_t is defined as follows:
 * @code
 * typedef struct sip_expires_s {
 *   sip_common_t        ex_common[1];  // Common fragment info
 *   sip_error_t        *ex_next;       // Link to next (dummy)
 *   sip_time_t          ex_date;       // Seconds since Jan 1, 1900
 *   sip_time_t          ex_delta;      // ...or delta seconds
 * } sip_expires_t;
 * @endcode
 */

msg_hclass_t sip_expires_class[] =
SIP_HEADER_CLASS(expires, "Expires", "", ex_common, single, any);

issize_t sip_expires_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  sip_expires_t *expires = (sip_expires_t *)h;

  if (msg_date_delta_d((char const **)&s,
                   &expires->ex_date,
                   &expires->ex_delta) < 0 || *s)
    return -1;
  else
    return 0;
}

issize_t sip_expires_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  sip_expires_t const *expires = (sip_expires_t *)h;

  if (expires->ex_date)
    return msg_date_e(b, bsiz, expires->ex_date + expires->ex_delta);
  else
    return msg_delta_e(b, bsiz, expires->ex_delta);
}

/**@ingroup sip_expires
 * @brief Create an @Expires header object.
 *
 * Create an @Expires header object with the expiration time @a delta.
 *
 * @param home   memory home used to allocate #sip_expires_t structure
 * @param delta  relative expiration time in seconds
 *
 * @return
 * A pointer to newly created @Expires header object when successful or NULL
 * upon an error.
 */
01735 sip_expires_t *sip_expires_create(su_home_t *home, sip_time_t delta)
{
  sip_expires_t *ex = (sip_expires_t *)
    sip_header_alloc(home, sip_expires_class, 0);

  if (ex)
    ex->ex_delta = delta;

  return ex;
}

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

/**@SIP_HEADER sip_from From Header
 *
 * The From header indicates the initiator of the request.  It is defined in
 * @RFC3261 as follows:
 *
 * @code
 *    From        =  ( "From" / "f" ) HCOLON from-spec
 *    from-spec   =  ( name-addr / addr-spec )
 *                   *( SEMI from-param )
 *    from-param  =  tag-param / generic-param
 *    tag-param   =  "tag" EQUAL token
 * @endcode
 *
 *
 * The parsed From header is stored in #sip_from_t structure.
 */

/**@ingroup sip_from
 * @typedef typedef struct sip_addr_s sip_from_t;
 *
 * The structure #sip_from_t contains representation of @From header.
 *
 * The #sip_from_t is defined as follows:
 * @code
 * typedef struct sip_addr_s {
 *   sip_common_t       a_common[1];    // Common fragment info
 *   sip_error_t       *a_next;         // Link to next
 *   char const        *a_display;      // Display name
 *   url_t              a_url[1];       // URL
 *   msg_param_t const *a_params;       // List of from-param
 *   char const        *a_comment;      // Comment
 *   char const        *a_tag;          // Tag parameter
 * } sip_from_t;
 * @endcode
 *
 */

msg_hclass_t sip_from_class[] =
SIP_HEADER_CLASS(from, "From", "f", a_params, single, addr);

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

issize_t sip_from_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  assert(sip_is_from(h));
  return sip_addr_e(b, bsiz, h, flags);
}

/**@ingroup sip_from
 *
 * Create a @From header object with URL.
 *
 * @param home      memory home used to allocate #sip_from_t structure
 * @param s         pointer to the URL or a string
 *
 * @return
 * A pointer to newly created @From header object when successful or NULL
 * upon an error.
 */
sip_from_t *
01814 sip_from_create(su_home_t *home, url_string_t const *s)
{
  return sip_addr_make_url(home, sip_from_class, s);
}

/**@ingroup sip_from
 *
 * Add a parameter to an #sip_from_t object.
 *
 * @param home   memory home
 * @param from   a pointer to #sip_from_t object
 * @param param  parameter string
 *
 * @retval 0 when successful
 * @retval -1 upon an error
 *
 * @deprecated Use msg_header_replace_param() directly.
 */
01832 int sip_from_add_param(su_home_t *home,
                   sip_from_t *from,
                   char const *param)
{
  return msg_header_replace_param(home, from->a_common, param);
}

/**@ingroup sip_from
 *
 * Add a tag to a @From header. If @a tag is
 * identical with the existing one, nothing will be done. An error is
 * returned, if the header already contains a different tag. The @a tag can
 * be provided either as a single token ("deadbeer") or as in parameter form
 * ("tag=deadbeer"). In both cases the tag is duplicated using the memory
 * home @a home.
 *
 * @param home memory home used to allocate new tag
 * @param from @From header to modify
 * @param tag  tag token or parameter to be added
 *
 * @retval 0 when successful
 * @retval -1 upon an error.
 */
01855 int sip_from_tag(su_home_t *home, sip_from_t *from, char const *tag)
{
  return sip_addr_tag(home, from, tag);
}


int sip_to_tag(su_home_t *home, sip_to_t *to, char const *tag)
{
  return sip_addr_tag(home, to, tag);
}

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

/**@SIP_HEADER sip_max_forwards Max-Forwards Header
 *
 * The Max-Forwards header is used to limit the number of proxies or
 * gateways that can forward the request.  The Max-Forwards syntax is
 * defined in @RFC3261 as follows:
 *
 * @code
 *    Max-Forwards  =  "Max-Forwards" HCOLON 1*DIGIT
 * @endcode
 *
 *
 * The parsed Max-Forwards header is stored in #sip_max_forwards_t structure.
 */

/**@ingroup sip_max_forwards
 * @typedef typedef struct sip_max_forwards_s sip_max_forwards_t;
 *
 * The structure #sip_max_forwards_t contains representation of SIP
 * @MaxForwards header.
 *
 * The #sip_max_forwards_t is defined as follows:
 * @code
 * typedef struct sip_max_forwards_s {
 *   sip_common_t        mf_common[1];  // Common fragment info
 *   sip_error_t        *mf_next;       // Link to next (dummy)
 *   unsigned long       mf_count;      // Digits
 * } sip_max_forwards_t;
 * @endcode
 */

msg_hclass_t sip_max_forwards_class[] =
SIP_HEADER_CLASS(max_forwards, "Max-Forwards", "", mf_common,
             single, any);

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

issize_t sip_max_forwards_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  assert(sip_is_max_forwards(h));
  return sip_numeric_e(b, bsiz, h, f);
}

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

/**@SIP_HEADER sip_min_expires Min-Expires Header
 *
 * The Min-Expires header is used to limit the number of proxies or
 * gateways that can forward the request.  The Min-Expires syntax is
 * defined in @RFC3261 as follows:
 *
 * @code
 *    Min-Expires  =  "Min-Expires" HCOLON delta-seconds
 * @endcode
 *
 * The parsed Min-Expires header is stored in #sip_min_expires_t structure.
 */

/**@ingroup sip_min_expires
 * @typedef typedef struct sip_min_expires_s sip_min_expires_t;
 *
 * The structure #sip_min_expires_t contains representation of SIP
 * @MinExpires header.
 *
 * The #sip_min_expires_t is defined as follows:
 * @code
 * typedef struct sip_min_expires_s {
 *   sip_common_t        me_common[1];  // Common fragment info
 *   sip_error_t        *me_next;       // Link to next (dummy)
 *   unsigned long       me_delta;      // Seconds
 * } sip_min_expires_t;
 * @endcode
 */

msg_hclass_t sip_min_expires_class[] =
SIP_HEADER_CLASS(min_expires, "Min-Expires", "", me_common,
             single, any);

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

issize_t sip_min_expires_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  assert(sip_is_min_expires(h));
  return sip_numeric_e(b, bsiz, h, f);
}


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

/**@SIP_HEADER sip_retry_after Retry-After Header
 *
 * The Retry-After response-header field @RFC3261 section 20.33 can be used to
 * indicate how long the service is expected to be unavailable or when the
 * called party anticipates being available again. Its syntax is defined in
 * @RFC3261 as follows:
 *
 * @code
 *      Retry-After  =  "Retry-After" HCOLON delta-seconds
 *                      [ comment ] *( SEMI retry-param )
 *      retry-param  =  ("duration" EQUAL delta-seconds)
 *                      / generic-param
 * @endcode
 *
 * The parsed Retry-After header is stored in #sip_retry_after_t structure.
 */

/**@ingroup sip_retry_after
 * @typedef struct sip_retry_after_s sip_retry_after_t;
 *
 * The structure #sip_retry_after_t contains representation of an
 * @RetryAfter header.
 *
 * The #sip_retry_after_t is defined as follows:
 * @code
 * typedef struct sip_retry_after_s {
 *   sip_common_t        af_common[1]; // Common fragment info
 *   sip_error_t        *af_next;      // Link to next (dummy)
 *   sip_time_t          af_delta;     // Seconds to before retry
 *   char const         *af_comment;   // Comment string
 *   msg_param_t const  *af_params;    // List of parameters
 *   char const         *af_duration;  // Duration parameter
 * } sip_retry_after_t;
 * @endcode
 */

static msg_xtra_f sip_retry_after_dup_xtra;
static msg_dup_f sip_retry_after_dup_one;
static msg_update_f sip_retry_after_update;

msg_hclass_t sip_retry_after_class[] =
SIP_HEADER_CLASS(retry_after, "Retry-After", "", af_params, single,
             retry_after);

issize_t sip_retry_after_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  sip_retry_after_t *af = (sip_retry_after_t *)h;

  if ((msg_delta_d((char const **)&s, &af->af_delta) < 0) ||
      (*s == '(' && msg_comment_d(&s, &af->af_comment) == -1) ||
      (*s == ';' && msg_params_d(home, &s, &af->af_params) == -1) ||
      (*s != '\0')) {
    if (af->af_params)
      su_free(home, (void *)af->af_params), af->af_params = NULL;
    return -1;
  }

  if (af->af_params)
    msg_header_update_params(h->sh_common, 0);

  return 0;
}

issize_t sip_retry_after_e(char b[], isize_t bsiz, sip_header_t const *h, int f)
{
  sip_retry_after_t const *af = (sip_retry_after_t *)h;
  int const compact = MSG_IS_COMPACT(f);
  char *b0 = b, *end = b + bsiz;

  b += snprintf(b, bsiz, "%lu", af->af_delta);

  if (af->af_comment) {
    if (!compact)
      MSG_CHAR_E(b, end, ' ');
    MSG_CHAR_E(b, end, '(');
    MSG_STRING_E(b, end, af->af_comment);
    MSG_CHAR_E(b, end, ')');
    if (!compact && af->af_params && af->af_params[0])
      MSG_CHAR_E(b, end, ' ');
  }

  if (af->af_params)
    MSG_PARAMS_E(b, end, af->af_params, f);

  MSG_TERM_E(b, end);

  return b - b0;
}

isize_t sip_retry_after_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_retry_after_t const *af = (sip_retry_after_t *)h;

  MSG_PARAMS_SIZE(offset, af->af_params);
  offset += MSG_STRING_SIZE(af->af_comment);

  return offset;
}

char *sip_retry_after_dup_one(sip_header_t *dst,
                        sip_header_t const *src,
                        char *b,
                        isize_t xtra)
{
  sip_retry_after_t *af = (sip_retry_after_t *)dst;
  sip_retry_after_t const *o = (sip_retry_after_t *)src;
  char *end = b + xtra;

  b = msg_params_dup(&af->af_params, o->af_params, b, xtra);
  MSG_STRING_DUP(b, af->af_comment, o->af_comment);
  af->af_delta = o->af_delta;

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

  return b;
}

static int sip_retry_after_update(msg_common_t *h,
                          char const *name, isize_t namelen,
                          char const *value)
{
  sip_retry_after_t *af = (sip_retry_after_t *)h;

  if (name == NULL) {
    af->af_duration = NULL;
  }
  else if (namelen == strlen("duration") &&
         su_casenmatch(name, "duration", namelen)) {
    af->af_duration = value;
  }

  return 0;
}


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

/**Parse a @Route or a @RecordRoute header.
 *
 * @retval 0 when successful,
 * @retval -1 upon an error.
 */
02104 issize_t sip_any_route_d(su_home_t *home,
                   sip_header_t *h,
                   char *s,
                   isize_t slen)
{
  sip_route_t *r = (sip_route_t *)h;

  assert(h);

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

  if (sip_name_addr_d(home, &s, &r->r_display,
                  r->r_url, &r->r_params, NULL) < 0)
    return -1;

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

issize_t sip_any_route_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  sip_route_t const *r = (sip_route_t *)h;

  return sip_name_addr_e(b, bsiz, flags,
                   r->r_display, 1, r->r_url, r->r_params, NULL);
}

isize_t sip_any_route_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_route_t const *r = (sip_route_t *)h;
  return sip_name_addr_xtra(r->r_display,
                      r->r_url,
                      r->r_params,
                      offset);
}

char *sip_any_route_dup_one(sip_header_t *dst, sip_header_t const *src,
                      char *b,
                      isize_t xtra)
{
  sip_route_t *r = (sip_route_t *)dst;
  sip_route_t const *o = (sip_route_t *)src;
  return sip_name_addr_dup(&r->r_display, o->r_display,
                     r->r_url, o->r_url,
                     &r->r_params, o->r_params,
                     b, xtra);
}

#define sip_any_route_update NULL


/** Create a route.
 *
 * Create a route or record-route entry
 * from two URLs; first one provides the URL, second maddr parameter and
 * port.
 *
 * @param home   memory home
 * @param rq_url route URL
 * @param maddr  optional route address and port
 *  */
static
sip_route_t *sip_any_route_create(su_home_t *home,
                          msg_hclass_t *hc,
                          url_t const *rq_url,
                          url_t const *maddr)
{
  sip_header_t *h;
  sip_route_t *rr;
  url_t url[1];
  size_t xtra, n, n_url, n_params, n_addr;
  char *b, *param;

  *url = *rq_url;
  if (maddr) {
    url->url_port = maddr->url_port;
    url->url_params = NULL;
  }
  n_url = url_xtra(url);

  n_params = maddr && maddr->url_params ? strlen(maddr->url_params) : 0;

  if (maddr && (!maddr->url_params ||
            !url_param(maddr->url_params, "maddr", NULL, 0)))
    n_addr = (n_params != 0) + strlen("maddr=") + strlen(maddr->url_host);
  else
    n_addr = 0;

  xtra = n_url + n_params + n_addr + (n_params || n_addr);

  h = sip_header_alloc(home, hc, xtra);
  if ((rr = (sip_record_route_t *)h)) {
    b = sip_header_data(h);
    n = url_dup(b, n_url, rr->r_url, url);
    assert(n == n_url);

    if (n_params || n_addr) {
      param = b + n_url;
      if (n_params) {
      rr->r_url->url_params = strcpy(param, maddr->url_params);
      param += n_params;
      }
      if (n_addr) {
      if (n_params)
        *param++ = ';';
      strcpy(param, "maddr="), param += strlen("maddr=");
      strcpy(param, maddr->url_host), param += strlen(maddr->url_host);
      }
      assert(b + xtra == param + 1);
    }
  }

  return rr;
}

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

/**@SIP_HEADER sip_route Route Header
 *
 * The Route headers is used to store the route set of a transaction.
 * The Route header is defined in @RFC3261 as follows:
 *
 * @code
 *    Route        =  "Route" HCOLON route-param *(COMMA route-param)
 *    route-param  =  name-addr *( SEMI rr-param )
 * @endcode
 *
 * The parsed Route header is stored in #sip_route_t structure.
 */

/**@ingroup sip_route
 * @typedef typedef struct sip_route_s sip_route_t;
 *
 * The structure #sip_route_t contains representation of SIP @Route header.
 *
 * The #sip_route_t is defined as follows:
 * @code
 * typedef struct sip_route_s {
 *   sip_common_t        r_common[1];   // Common fragment info
 *   sip_route_t        *r_next;        // Link to next @Route
 *   char const         *r_display;     // Display name
 *   url_t               r_url[1];      // @Route URL
 *   msg_param_t const  *r_params;      // List of route parameters
 * } sip_route_t;
 * @endcode
 */

msg_hclass_t sip_route_class[] =
SIP_HEADER_CLASS(route, "Route", "", r_params, append, any_route);

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

issize_t sip_route_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  assert(sip_is_route(h));
  return sip_any_route_e(b, bsiz, h, flags);
}

/**@ingroup sip_route
 * @brief Create a @Route header object.
 *
 * Creates a route entry from two URLs; first one provides the URL, second
 * maddr parameter and port.
 *
 * @param home   memory home
 * @param url    route URL
 * @param maddr  optional route address and port
 *
 * @return
 * Returns a pointer to newly created @Route header object when successful,
 * or NULL upon an error.
 */
02282 sip_route_t *sip_route_create(su_home_t *home,
                        url_t const *url,
                        url_t const *maddr)
{
  return sip_any_route_create(home, sip_route_class, url, maddr);
}

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

/**@SIP_HEADER sip_record_route Record-Route Header
 *
 * The Record-Route headers are used to establish a route for transactions
 * belonging to a session.  The Record-Route header is defined in @RFC3261
 * as follows:
 *
 * @code
 *    Record-Route  =  "Record-Route" HCOLON rec-route *(COMMA rec-route)
 *    rec-route     =  name-addr *( SEMI rr-param )
 *    rr-param      =  generic-param
 * @endcode
 *
 * The parsed Record-Route header is stored in #sip_record_route_t structure.
 */

/**@ingroup sip_record_route
 * @typedef typedef struct sip_record_route_s sip_record_route_t;
 *
 * The structure #sip_record_route_t contains representation of SIP
 * @RecordRoute header.
 *
 * The #sip_record_route_t is defined as follows:
 * @code
 * typedef struct sip_route_s {
 *   sip_common_t        r_common[1];   // Common fragment info
 *   sip_record_route_t *r_next;        // Link to next <rec-route>
 *   char const         *r_display;     // Display name
 *   url_t               r_url[1];      // @RecordRoute URL
 *   msg_param_t const  *r_params;      // List of route parameters
 * } sip_record_route_t;
 * @endcode
 */

msg_hclass_t sip_record_route_class[] =
SIP_HEADER_CLASS(record_route, "Record-Route", "",
             r_params, prepend, any_route);

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

issize_t sip_record_route_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  assert(sip_is_record_route(h));
  return sip_any_route_e(b, bsiz, h, flags);
}

/** @ingroup sip_record_route
 *
 * Create a record-route.
 *
 * Create a record-route entry from two URLs; first one provides the URL,
 * second maddr parameter and port.
 *
 * @param home   memory home
 * @param rq_url route URL
 * @param maddr  optional route address and port
 *
 * @return
 * A pointer to newly created @RecordRoute header object when successful or
 * NULL upon an error.
 */
02357 sip_record_route_t *sip_record_route_create(su_home_t *home,
                                  url_t const *rq_url,
                                  url_t const *maddr)
{
  return sip_any_route_create(home, sip_record_route_class, rq_url, maddr);
}

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

/**@SIP_HEADER sip_to To Header
 *
 * The To header field specifies the "logical" recipient of the
 * request.  It is defined in @RFC3261 as follows:
 *
 * @code
 *    To        =  ( "To" / "t" ) HCOLON ( name-addr
 *                 / addr-spec ) *( SEMI to-param )
 *    to-param  =  tag-param / generic-param
 * @endcode
 *
 * The parsed To header is stored in #sip_to_t structure.
 */

/**@ingroup sip_to
 * @typedef typedef struct sip_addr_s sip_to_t;
 *
 * The structure #sip_to_t contains representation of @To header.
 *
 * The #sip_to_t is defined as follows:
 * @code
 * typedef struct {
 *   sip_common_t       a_common[1];    // Common fragment info
 *   sip_error_t       *a_next;         // Link to next (dummy)
 *   char const        *a_display;      // Display name
 *   url_t              a_url[1];       // URL
 *   msg_param_t const *a_params;       // List of to-params
 *   char const        *a_comment;      // Comment
 *   char const        *a_tag;          // Tag parameter
 * } sip_to_t;
 * @endcode
 *
 */

msg_hclass_t sip_to_class[] =
SIP_HEADER_CLASS(to, "To", "t", a_params, single, addr);

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

issize_t sip_to_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  assert(sip_is_to(h));

  return sip_addr_e(b, bsiz, h, flags);
}

/**@ingroup sip_to
 *
 * Create a @To header object with URL.
 *
 * @param home      memory home
 * @param url       URL (string or pointer to url_t)
 *
 * @return
 * A pointer to newly created @To header object when successful or NULL upon
 * an error.
 */
sip_to_t *
02427 sip_to_create(su_home_t *home, url_string_t const *url)
{
  return sip_addr_make_url(home, sip_to_class, url);
}

/**@ingroup sip_to
 *
 * Add a parameter to a #sip_to_t object.
 *
 * @note This function @b does @b not duplicate @p param.
 *
 * @param home   memory home
 * @param to     #sip_to_t structure
 * @param param  parameter string
 *
 * @retval 0 when successful
 * @retval -1 upon an error
 *
 * @deprecated Use msg_header_replace_param() directly.
 */
02447 int sip_to_add_param(su_home_t *home,
                 sip_to_t *to,
                 char const *param)
{
  return msg_header_replace_param(home, to->a_common, param);
}

/* ====================================================================== */
/**@SIP_HEADER sip_via Via Header
 *
 * The Via header indicates the path taken by the request so far.  Via
 * headers can be used to prevent request looping and ensure replies take
 * the same path as the requests.  The Via syntax is defined in @RFC3261
 * as follows:
 *
 * @code
 *    Via               =  ( "Via" / "v" ) HCOLON via-parm *(COMMA via-parm)
 *    via-parm          =  sent-protocol LWS sent-by *( SEMI via-params )
 *    via-params        =  via-ttl / via-maddr
 *                         / via-received / via-branch
 *                         / via-extension
 *    via-ttl           =  "ttl" EQUAL ttl
 *    via-maddr         =  "maddr" EQUAL host
 *    via-received      =  "received" EQUAL (IPv4address / IPv6address)
 *    via-branch        =  "branch" EQUAL token
 *    via-extension     =  generic-param
 *    sent-protocol     =  protocol-name SLASH protocol-version
 *                         SLASH transport
 *    protocol-name     =  "SIP" / token
 *    protocol-version  =  token
 *    transport         =  "UDP" / "TCP" / "TLS" / "SCTP"
 *                         / other-transport
 *    sent-by           =  host [ COLON port ]
 *    ttl               =  1*3DIGIT ; 0 to 255
 * @endcode
 *
 * @note
 * The @RFC2543 syntax allowed <comment>. We accept it, but don't encode it.
 *
 * In addition to the parameters defined in @RFC3261, @RFC3486 defines a
 * parameter "comp":
 * @code
 *     via-compression  =  "comp" EQUAL ("sigcomp" / other-compression)
 *    via-params      /=  via-compression
 * @endcode
 *
 * @RFC3581 defines a parameter "rport":
 * @code
 *    response-port  =  "rport" [EQUAL 1*DIGIT]
 *    via-params    /=  response-port
 * @endcode
 *
 * The parsed Via header is stored in #sip_via_t structure.
 */

/**@ingroup sip_via
 * @typedef typedef struct sip_via_s sip_via_t;
 *
 * The structure #sip_via_t contains representation of SIP @Via header.
 *
 * The #sip_via_t is defined as follows:
 * @code
 * typedef struct sip_via_s {
 *   sip_common_t        v_common[1];   // Common fragment info
 *   sip_via_t          *v_next;        // Link to next @Via header
 *   char const         *v_protocol;    // Application and transport protocol
 *   char const         *v_host;        // Hostname
 *   char const         *v_port;        // Port number
 *   msg_param_t const  *v_params;      // List of via-params
 *   char const         *v_comment;     // Comment
 *
 *   char const         *v_ttl;         // "ttl" parameter
 *   char const         *v_maddr;       // "maddr" parameter
 *   char const         *v_received;    // "received" parameter
 *   char const         *v_branch;      // "branch" parameter
 *   char const         *v_comp;        // "comp" parameter
 *   char const         *v_rport;       // "rport" parameter
 * } sip_via_t;
 * @endcode
 */

static msg_xtra_f sip_via_dup_xtra;
static msg_dup_f sip_via_dup_one;
static msg_update_f sip_via_update;

msg_hclass_t sip_via_class[] =
SIP_HEADER_CLASS(via, "Via", "v", v_params, prepend, via);

issize_t sip_via_d(su_home_t *home, sip_header_t *h, char *s, isize_t slen)
{
  sip_via_t *v = (sip_via_t *)h;

  assert(h);

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

  /* sent-protocol sent-by *( ";" via-params ) [ comment ] */

  /* Parse protocol */
  if (sip_transport_d(&s, &v->v_protocol) == -1)
    return -1;
  /* Host (and port) */
  if (msg_hostport_d(&s, &v->v_host, &v->v_port) == -1)
    return -1;
  /* Parameters */
  if (*s == ';' && msg_params_d(home, &s, &v->v_params) == -1)
    return -1;
  /* Comment */
  if (*s == '(' && msg_comment_d(&s, &v->v_comment) == -1)
    return -1;

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

issize_t sip_via_e(char b[], isize_t bsiz, sip_header_t const *h, int flags)
{
  char *b0 = b, *end = b + bsiz;
  sip_via_t const *v = (sip_via_t *)h;

  assert(sip_is_via(h));

  MSG_STRING_E(b, end, v->v_protocol);
  MSG_CHAR_E(b, end, ' ');
  MSG_STRING_E(b, end, v->v_host);
  if (v->v_port) {
    MSG_CHAR_E(b, end, ':');
    MSG_STRING_E(b, end, v->v_port);
  }
  MSG_PARAMS_E(b, end, v->v_params, flags);
#if 0
  /* Comment is deprecated in @RFC3265 - accept it, but do not send */
  if (v->v_comment) {
    if (!MSG_IS_COMPACT(flags))
      MSG_CHAR_E(b, end, ' ');
    MSG_CHAR_E(b, end, '(');
    MSG_STRING_E(b, end, v->v_comment);
    MSG_CHAR_E(b, end, ')');
  }
#endif
  MSG_TERM_E(b, end);

  return b - b0;
}

isize_t sip_via_dup_xtra(sip_header_t const *h, isize_t offset)
{
  sip_via_t const *v = (sip_via_t *)h;

  MSG_PARAMS_SIZE(offset, v->v_params);
  offset += sip_transport_xtra(v->v_protocol);
  offset += MSG_STRING_SIZE(v->v_host);
  offset += MSG_STRING_SIZE(v->v_port);
  offset += MSG_STRING_SIZE(v->v_comment);

  return offset;
}

/** Duplicate one #sip_via_t object */
char *sip_via_dup_one(sip_header_t *dst, sip_header_t const *src,
                  char *b, isize_t xtra)
{
  sip_via_t *v = (sip_via_t *)dst;
  sip_via_t const *o = (sip_via_t *)src;
  char *end = b + xtra;

  b = msg_params_dup(&v->v_params, o->v_params, b, xtra);
  sip_transport_dup(&b, &v->v_protocol, o->v_protocol);
  MSG_STRING_DUP(b, v->v_host, o->v_host);
  MSG_STRING_DUP(b, v->v_port, o->v_port);
  MSG_STRING_DUP(b, v->v_comment, o->v_comment);

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

  return b;
}

static int sip_via_update(msg_common_t *h,
                    char const *name, isize_t namelen,
                    char const *value)
{
  sip_via_t *v = (sip_via_t *)h;

  if (name == NULL) {
    v->v_ttl = NULL;
    v->v_maddr = NULL;
    v->v_received = NULL;
    v->v_branch = NULL;
    v->v_rport = NULL;
    v->v_comp = NULL;
  }
#define MATCH(s) (namelen == strlen(#s) && su_casenmatch(name, #s, strlen(#s)))

   else if (MATCH(ttl)) {
     v->v_ttl = value;
   }
   else if (MATCH(maddr)) {
     v->v_maddr = value;
   }
   else if (MATCH(received)) {
     v->v_received = value;
   }
   else if (MATCH(branch)) {
     v->v_branch = value;
   }
   else if (MATCH(rport)) {
     v->v_rport = value;
   }
   else if (MATCH(comp)) {
     v->v_comp = value;
   }

 #undef MATCH

   return 0;
 }

/**@ingroup sip_via
 *
 * Add a parameter to a @Via object.
 *
 * @note This function @b does @b not duplicate @p param.
 *
 * @param home   memory home
 * @param v      #sip_via_t object
 * @param param  parameter string
 *
 * @retval 0 when successful
 * @retval -1 upon an error.
 *
 * @deprecated Use msg_header_replace_param() directly.
 */
02679 int sip_via_add_param(su_home_t *home,
                  sip_via_t *v,
                  char const *param)
{
  return msg_header_replace_param(home, v->v_common, param);
}

/**@ingroup sip_via
 *
 * Create a @Via object.
 *
 * Create a new @Via header object with
 * given parameters.  If @a transport is NULL, the default transport
 * "SIP/2.0/UDP" is used.  A NULL-terminated list of parameters can be
 * specified after transport.
 *
 * @param home        memory home
 * @param host        host name
 * @param port        protocol port number
 * @param transport transport protocol (default is "SIP/2.0/UDP")
 * @param ...       NULL-terminated list of parameters
 *
 * @return
 * A pointer to newly created
 * @Via header object when successful or NULL upon an error.
 */
02705 sip_via_t *sip_via_create(su_home_t *home,
                          char const *host,
                          char const *port,
                          char const *transport,
                          /* char const *params */
                          ...)
{
  sip_via_t *v, via[1] = {{{{ NULL }}}};
  va_list params;

  via->v_common->h_class = sip_via_class;

  if (transport)
    via->v_protocol = transport;
  else
    via->v_protocol = sip_transport_udp;

  via->v_host = host;
  via->v_port = port;

  v = (sip_via_t *)msg_header_dup_as(home, sip_via_class, (sip_header_t *)via);

  if (v) {
    char const *param;
    va_start(params, transport);

    for (param = va_arg(params, char const *);
         param;
         param = va_arg(params, char const *)) {
      if ((param = su_strdup(home, param))) {
      if (msg_header_replace_param(home, v->v_common, param) < 0)
        break;
      }
    }

    va_end(params);
  }

  return v;
}

/**@ingroup sip_via
 *
 * Get port number corresponding to a @Via line.
 *
 * If @a using_rport is non-null, try rport.
 * If *using_rport is non-zero, try rport even if <protocol> is not UDP.
 * If <protocol> is UDP, set *using_rport to zero.
 */
02754 char const *sip_via_port(sip_via_t const *v, int *using_rport)
{
  if (v == NULL)
    return NULL;

  if (using_rport) {
    char const *port;

    if (v->v_rport && !v->v_maddr /* multicast */) {
      if (v->v_protocol == sip_transport_udp ||
        su_casematch(v->v_protocol, sip_transport_udp))
      port = v->v_rport, *using_rport = 0;
      else if (*using_rport)
      port = v->v_rport;
      else
      port = NULL;

      if (port && port[0])
      return port;
    }

    *using_rport = 0;         /* No, we don't... */
  }

  if (v->v_port)
    return v->v_port;

  if (sip_transport_has_tls(v->v_protocol))
    return SIPS_DEFAULT_SERV; /* 5061 */
  else
    return SIP_DEFAULT_SERV;  /* 5060 */
}

Generated by  Doxygen 1.6.0   Back to index