/* 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#include <pjsip/sip_uri.h>
#include <pjsip/sip_msg.h>
#include <pjsip/sip_parser.h>
#include <pjsip/print_util.h>
#include <pjsip/sip_errno.h>
#include <pjlib-util/string.h>
#include <pj/string.h>
#include <pj/pool.h>
#include <pj/assert.h>

/*
 * Generic parameter manipulation.
 */
PJ_DEF(pjsip_param*) pjsip_param_find(  const pjsip_param *param_list,
                                        const pj_str_t *name )
{
    pjsip_param *p = (pjsip_param*)param_list->next;
    while (p != param_list) {
        if (pj_stricmp(&p->name, name)==0)
            return p;
        p = p->next;
    }
    return NULL;
}

PJ_DEF(int) pjsip_param_cmp( const pjsip_param *param_list1,
                             const pjsip_param *param_list2,
                             pj_bool_t ig_nf)
{
    const pjsip_param *p1;

    if ((ig_nf & 1)==0 && pj_list_size(param_list1)!=pj_list_size(param_list2))
        return 1;

    p1 = param_list1->next;
    while (p1 != param_list1) {
        const pjsip_param *p2;
        p2 = pjsip_param_find(param_list2, &p1->name);
        if (p2 ) {
            int rc = pj_stricmp(&p1->value, &p2->value);
            if (rc != 0)
                return rc;
        } else if ((ig_nf & 1)==0)
            return 1;

        p1 = p1->next;
    }

    return 0;
}

PJ_DEF(void) pjsip_param_clone( pj_pool_t *pool, pjsip_param *dst_list,
                                const pjsip_param *src_list)
{
    const pjsip_param *p = src_list->next;

    pj_list_init(dst_list);
    while (p && p != src_list) {
        pjsip_param *new_param = PJ_POOL_ALLOC_T(pool, pjsip_param);
        pj_strdup(pool, &new_param->name, &p->name);
        pj_strdup(pool, &new_param->value, &p->value);
        pj_list_insert_before(dst_list, new_param);
        p = p->next;
    }
}


PJ_DEF(void) pjsip_param_shallow_clone( pj_pool_t *pool, 
                                        pjsip_param *dst_list,
                                        const pjsip_param *src_list)
{
    const pjsip_param *p = src_list->next;

    pj_list_init(dst_list);
    while (p != src_list) {
        pjsip_param *new_param = PJ_POOL_ALLOC_T(pool, pjsip_param);
        new_param->name = p->name;
        new_param->value = p->value;
        pj_list_insert_before(dst_list, new_param);
        p = p->next;
    }
}

PJ_DEF(pj_ssize_t) pjsip_param_print_on( const pjsip_param *param_list,
                                         char *buf, pj_size_t size,
                                         const pj_cis_t *pname_spec,
                                         const pj_cis_t *pvalue_spec,
                                         int sep)
{
    const pjsip_param *p;
    char *startbuf;
    char *endbuf;
    int printed;

    p = param_list->next;
    if (p == NULL || p == param_list)
        return 0;

    startbuf = buf;
    endbuf = buf + size;

    PJ_UNUSED_ARG(pname_spec);

    do {
        copy_advance_char_check(buf, (char)sep);
        copy_advance_escape(buf, p->name, (*pname_spec));
        if (p->value.slen) {
            copy_advance_char_check(buf, '=');
            if (*p->value.ptr == '"')
                copy_advance(buf, p->value);
            else
                copy_advance_escape(buf, p->value, (*pvalue_spec));
        }
        p = p->next;
        if (sep == '?') sep = '&';
    } while (p != param_list);

    return buf-startbuf;
}


/*
 * URI stuffs
 */
#define IS_SIPS(url)    ((url)->vptr==&sips_url_vptr)

static const pj_str_t *pjsip_url_get_scheme( const pjsip_sip_uri* );
static const pj_str_t *pjsips_url_get_scheme( const pjsip_sip_uri* );
static const pj_str_t *pjsip_name_addr_get_scheme( const pjsip_name_addr * );
static void *pjsip_get_uri( pjsip_uri *uri );
static void *pjsip_name_addr_get_uri( pjsip_name_addr *name );

static pj_str_t sip_str = { "sip", 3 };
static pj_str_t sips_str = { "sips", 4 };

static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, 
                                               const pjsip_name_addr *rhs);
static pj_ssize_t pjsip_name_addr_print(pjsip_uri_context_e context,
                                        const pjsip_name_addr *name, 
                                        char *buf, pj_size_t size);
static int pjsip_name_addr_compare(  pjsip_uri_context_e context,
                                     const pjsip_name_addr *naddr1,
                                     const pjsip_name_addr *naddr2);
static pj_ssize_t pjsip_url_print(  pjsip_uri_context_e context,
                                    const pjsip_sip_uri *url, 
                                    char *buf, pj_size_t size);
static int pjsip_url_compare( pjsip_uri_context_e context,
                              const pjsip_sip_uri *url1, 
                              const pjsip_sip_uri *url2);
static pjsip_sip_uri* pjsip_url_clone(pj_pool_t *pool, 
                                      const pjsip_sip_uri *rhs);

typedef const pj_str_t* (*P_GET_SCHEME)(const void*);
typedef void*           (*P_GET_URI)(void*);
typedef pj_ssize_t      (*P_PRINT_URI)(pjsip_uri_context_e,const void *,
                                       char*,pj_size_t);
typedef int             (*P_CMP_URI)(pjsip_uri_context_e, const void*, 
                                     const void*);
typedef void*           (*P_CLONE)(pj_pool_t*, const void*);


static pjsip_uri_vptr sip_url_vptr = 
{
    (P_GET_SCHEME)      &pjsip_url_get_scheme,
    (P_GET_URI)         &pjsip_get_uri,
    (P_PRINT_URI)       &pjsip_url_print,
    (P_CMP_URI)         &pjsip_url_compare,
    (P_CLONE)           &pjsip_url_clone
};

static pjsip_uri_vptr sips_url_vptr = 
{
    (P_GET_SCHEME)      &pjsips_url_get_scheme,
    (P_GET_URI)         &pjsip_get_uri,
    (P_PRINT_URI)       &pjsip_url_print,
    (P_CMP_URI)         &pjsip_url_compare,
    (P_CLONE)           &pjsip_url_clone
};

static pjsip_uri_vptr name_addr_vptr = 
{
    (P_GET_SCHEME)      &pjsip_name_addr_get_scheme,
    (P_GET_URI)         &pjsip_name_addr_get_uri,
    (P_PRINT_URI)       &pjsip_name_addr_print,
    (P_CMP_URI)         &pjsip_name_addr_compare,
    (P_CLONE)           &pjsip_name_addr_clone
};

static const pj_str_t *pjsip_url_get_scheme(const pjsip_sip_uri *url)
{
    PJ_UNUSED_ARG(url);
    return &sip_str;
}

static const pj_str_t *pjsips_url_get_scheme(const pjsip_sip_uri *url)
{
    PJ_UNUSED_ARG(url);
    return &sips_str;
}

static void *pjsip_get_uri( pjsip_uri *uri )
{
    return uri;
}

static void *pjsip_name_addr_get_uri( pjsip_name_addr *name )
{
    return pjsip_uri_get_uri(name->uri);
}

PJ_DEF(void) pjsip_sip_uri_set_secure( pjsip_sip_uri *url, 
                                       pj_bool_t secure )
{
    url->vptr = secure ? &sips_url_vptr : &sip_url_vptr;
}

PJ_DEF(void) pjsip_sip_uri_init(pjsip_sip_uri *url, pj_bool_t secure)
{
    pj_bzero(url, sizeof(*url));
    url->ttl_param = -1;
    pjsip_sip_uri_set_secure(url, secure);
    pj_list_init(&url->other_param);
    pj_list_init(&url->header_param);
}

PJ_DEF(pjsip_sip_uri*) pjsip_sip_uri_create( pj_pool_t *pool, 
                                             pj_bool_t secure )
{
    pjsip_sip_uri *url = PJ_POOL_ALLOC_T(pool, pjsip_sip_uri);
    pjsip_sip_uri_init(url, secure);
    return url;
}

static pj_ssize_t pjsip_url_print(  pjsip_uri_context_e context,
                                    const pjsip_sip_uri *url, 
                                    char *buf, pj_size_t size)
{
    int printed;
    char *startbuf = buf;
    char *endbuf = buf+size-1; // Need to minus one for NULL terminator
    const pj_str_t *scheme;
    const pjsip_parser_const_t *pc = pjsip_parser_const();

    if (endbuf < startbuf) return -1;
    *buf = '\0';

    /* Print scheme ("sip:" or "sips:") */
    scheme = pjsip_uri_get_scheme(url);
    copy_advance_check(buf, *scheme);
    copy_advance_char_check(buf, ':');

    /* Print "user:password@", if any. */
    if (url->user.slen) {
        const pj_cis_t *spec = pjsip_cfg()->endpt.allow_tx_hash_in_uri ?
                                &pc->pjsip_USER_SPEC_LENIENT :
                                &pc->pjsip_USER_SPEC;
        copy_advance_escape(buf, url->user, *spec);
        if (url->passwd.slen) {
            copy_advance_char_check(buf, ':');
            copy_advance_escape(buf, url->passwd, pc->pjsip_PASSWD_SPEC);
        }

        copy_advance_char_check(buf, '@');
    }

    /* Print host. */
    pj_assert(url->host.slen != 0);
    /* Detect IPv6 IP address */
    if (pj_memchr(url->host.ptr, ':', url->host.slen)) {
        copy_advance_pair_quote_cond(buf, "", 0, url->host, '[', ']');
    } else {
        copy_advance_check(buf, url->host);
    }

    /* Only print port if it is explicitly specified. 
     * Port is not allowed in To and From header, see Table 1 in
     * RFC 3261 Section 19.1.1
     */
    /* Note: ticket #1141 adds run-time setting to allow port number to
     * appear in From/To header. Default is still false.
     */
    if (url->port &&
        (context != PJSIP_URI_IN_FROMTO_HDR ||
         pjsip_cfg()->endpt.allow_port_in_fromto_hdr))
    {
        if (endbuf - buf < 10)
            return -1;

        copy_advance_char_check(buf, ':');
        printed = pj_utoa(url->port, buf);
        buf += printed;
    }

    /* User param is allowed in all contexes */
    copy_advance_pair_check(buf, ";user=", 6, url->user_param);

    /* Method param is only allowed in external/other context. */
    if (context == PJSIP_URI_IN_OTHER) {
        copy_advance_pair_escape(buf, ";method=", 8, url->method_param, 
                                 pc->pjsip_PARAM_CHAR_SPEC);
    }

    /* Transport is not allowed in From/To header. */
    if (context != PJSIP_URI_IN_FROMTO_HDR) {
        copy_advance_pair_escape(buf, ";transport=", 11, url->transport_param,
                                 pc->pjsip_PARAM_CHAR_SPEC);
    }

    /* TTL param is not allowed in From, To, Route, and Record-Route header. */
    if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR &&
        context != PJSIP_URI_IN_ROUTING_HDR) 
    {
        if (endbuf - buf < 15)
            return -1;
        pj_memcpy(buf, ";ttl=", 5);
        printed = pj_utoa(url->ttl_param, buf+5);
        buf += printed + 5;
    }

    /* maddr param is not allowed in From and To header. */
    if (context != PJSIP_URI_IN_FROMTO_HDR && url->maddr_param.slen) {
        /* Detect IPv6 IP address */
        if (pj_memchr(url->maddr_param.ptr, ':', url->maddr_param.slen)) {
            copy_advance_pair_quote_cond(buf, ";maddr=", 7, url->maddr_param,
                                         '[', ']');
        } else {
            copy_advance_pair_escape(buf, ";maddr=", 7, url->maddr_param,
                                     pc->pjsip_PARAM_CHAR_SPEC);
        }
    }

    /* lr param is not allowed in From, To, and Contact header. */
    if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR &&
        context != PJSIP_URI_IN_CONTACT_HDR) 
    {
        pj_str_t lr = { ";lr", 3 };
        if (endbuf - buf < 3)
            return -1;
        copy_advance_check(buf, lr);
    }

    /* Other param. */
    printed = (int)pjsip_param_print_on(&url->other_param, buf, endbuf-buf, 
                                        &pc->pjsip_PARAM_CHAR_SPEC, 
                                        &pc->pjsip_PARAM_CHAR_SPEC, ';');
    if (printed < 0)
        return -1;
    buf += printed;

    /* Header param. 
     * Header param is only allowed in these contexts:
     *  - PJSIP_URI_IN_CONTACT_HDR
     *  - PJSIP_URI_IN_OTHER
     */
    if (context == PJSIP_URI_IN_CONTACT_HDR || context == PJSIP_URI_IN_OTHER) {
        printed = (int)pjsip_param_print_on(&url->header_param, buf, endbuf-buf,
                                            &pc->pjsip_HDR_CHAR_SPEC, 
                                            &pc->pjsip_HDR_CHAR_SPEC, '?');
        if (printed < 0)
            return -1;
        buf += printed;
    }

    *buf = '\0';
    return buf-startbuf;
}

static pj_status_t pjsip_url_compare( pjsip_uri_context_e context,
                                      const pjsip_sip_uri *url1, 
                                      const pjsip_sip_uri *url2)
{
    const pjsip_param *p1;

    /*
     * Compare two SIP URL's according to Section 19.1.4 of RFC 3261.
     */

    /* SIP and SIPS URI are never equivalent. 
     * Note: just compare the vptr to avoid string comparison. 
     *       Pretty neat huh!!
     */
    if (url1->vptr != url2->vptr)
        return PJSIP_ECMPSCHEME;

    /* Comparison of the userinfo of SIP and SIPS URIs is case-sensitive. 
     * This includes userinfo containing passwords or formatted as 
     * telephone-subscribers.
     */
    if (pj_strcmp(&url1->user, &url2->user) != 0)
        return PJSIP_ECMPUSER;
    if (pj_strcmp(&url1->passwd, &url2->passwd) != 0)
        return PJSIP_ECMPPASSWD;
    
    /* Comparison of all other components of the URI is
     * case-insensitive unless explicitly defined otherwise.
     */

    /* The ordering of parameters and header fields is not significant 
     * in comparing SIP and SIPS URIs.
     */

    /* Characters other than those in the reserved set (see RFC 2396 [5])
     * are equivalent to their encoding.
     */

    /* An IP address that is the result of a DNS lookup of a host name 
     * does not match that host name.
     */
    if (pj_stricmp(&url1->host, &url2->host) != 0)
        return PJSIP_ECMPHOST;

    /* A URI omitting any component with a default value will not match a URI
     * explicitly containing that component with its default value. 
     * For instance, a URI omitting the optional port component will not match
     * a URI explicitly declaring port 5060. 
     * The same is true for the transport-parameter, ttl-parameter, 
     * user-parameter, and method components.
     */

    /* Port is not allowed in To and From header.
     */
    if (context != PJSIP_URI_IN_FROMTO_HDR) {
        if (url1->port != url2->port)
            return PJSIP_ECMPPORT;
    }
    /* Transport is not allowed in From/To header. */
    if (context != PJSIP_URI_IN_FROMTO_HDR) {
        if (pj_stricmp(&url1->transport_param, &url2->transport_param) != 0)
            return PJSIP_ECMPTRANSPORTPRM;
    }
    /* TTL param is not allowed in From, To, Route, and Record-Route header. */
    if (context != PJSIP_URI_IN_FROMTO_HDR &&
        context != PJSIP_URI_IN_ROUTING_HDR)
    {
        if (url1->ttl_param != url2->ttl_param)
            return PJSIP_ECMPTTLPARAM;
    }
    /* User param is allowed in all contexes */
    if (pj_stricmp(&url1->user_param, &url2->user_param) != 0)
        return PJSIP_ECMPUSERPARAM;
    /* Method param is only allowed in external/other context. */
    if (context == PJSIP_URI_IN_OTHER) {
        if (pj_stricmp(&url1->method_param, &url2->method_param) != 0)
            return PJSIP_ECMPMETHODPARAM;
    }
    /* maddr param is not allowed in From and To header. */
    if (context != PJSIP_URI_IN_FROMTO_HDR) {
        if (pj_stricmp(&url1->maddr_param, &url2->maddr_param) != 0)
            return PJSIP_ECMPMADDRPARAM;
    }

    /* lr parameter is ignored (?) */
    /* lr param is not allowed in From, To, and Contact header. */


    /* All other uri-parameters appearing in only one URI are ignored when 
     * comparing the URIs.
     */
    if (pjsip_param_cmp(&url1->other_param, &url2->other_param, 1)!=0)
        return PJSIP_ECMPOTHERPARAM;

    /* URI header components are never ignored. Any present header component
     * MUST be present in both URIs and match for the URIs to match. 
     * The matching rules are defined for each header field in Section 20.
     */
    p1 = url1->header_param.next;
    while (p1 != &url1->header_param) {
        const pjsip_param *p2;
        p2 = pjsip_param_find(&url2->header_param, &p1->name);
        if (p2) {
            /* It seems too much to compare two header params according to
             * the rule of each header. We'll just compare them string to
             * string..
             */
            if (pj_stricmp(&p1->value, &p2->value) != 0)
                return PJSIP_ECMPHEADERPARAM;
        } else {
            return PJSIP_ECMPHEADERPARAM;
        }
        p1 = p1->next;
    }

    /* Equal!! Pheuww.. */
    return PJ_SUCCESS;
}


PJ_DEF(void) pjsip_sip_uri_assign(pj_pool_t *pool, pjsip_sip_uri *url, 
                                  const pjsip_sip_uri *rhs)
{
    pj_strdup( pool, &url->user, &rhs->user);
    pj_strdup( pool, &url->passwd, &rhs->passwd);
    pj_strdup( pool, &url->host, &rhs->host);
    url->port = rhs->port;
    pj_strdup( pool, &url->user_param, &rhs->user_param);
    pj_strdup( pool, &url->method_param, &rhs->method_param);
    pj_strdup( pool, &url->transport_param, &rhs->transport_param);
    url->ttl_param = rhs->ttl_param;
    pj_strdup( pool, &url->maddr_param, &rhs->maddr_param);
    pjsip_param_clone(pool, &url->other_param, &rhs->other_param);
    pjsip_param_clone(pool, &url->header_param, &rhs->header_param);
    url->lr_param = rhs->lr_param;
}

static pjsip_sip_uri* pjsip_url_clone(pj_pool_t *pool, const pjsip_sip_uri *rhs)
{
    pjsip_sip_uri *url = PJ_POOL_ALLOC_T(pool, pjsip_sip_uri);
    if (!url)
        return NULL;

    pjsip_sip_uri_init(url, IS_SIPS(rhs));
    pjsip_sip_uri_assign(pool, url, rhs);
    return url;
}

static const pj_str_t *pjsip_name_addr_get_scheme(const pjsip_name_addr *name)
{
    pj_assert(name->uri != NULL);
    return pjsip_uri_get_scheme(name->uri);
}

PJ_DEF(void) pjsip_name_addr_init(pjsip_name_addr *name)
{
    name->vptr = &name_addr_vptr;
    name->uri = NULL;
    name->display.slen = 0;
    name->display.ptr = NULL;
}

PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool)
{
    pjsip_name_addr *name_addr = PJ_POOL_ALLOC_T(pool, pjsip_name_addr);
    pjsip_name_addr_init(name_addr);
    return name_addr;
}

static pj_ssize_t pjsip_name_addr_print(pjsip_uri_context_e context,
                                        const pjsip_name_addr *name, 
                                        char *buf, pj_size_t size)
{
    int printed;
    char *startbuf = buf;
    char *endbuf = buf + size - 1; // Need to minus one for NULL terminator
    pjsip_uri *uri;

    uri = (pjsip_uri*) pjsip_uri_get_uri(name->uri);
    pj_assert(uri != NULL);

    if (context != PJSIP_URI_IN_REQ_URI) {
        if (name->display.slen) {
            if (endbuf-buf < name->display.slen + 3) return -1;
            copy_advance_char_check(buf, '"');
            copy_advance(buf, name->display);
            copy_advance_char_check(buf, '"');
            copy_advance_char_check(buf, ' ');
        }
        copy_advance_char_check(buf, '<');;
    }

    printed = pjsip_uri_print(context,uri, buf, endbuf - buf);
    if (printed < 1)
        return -1;
    buf += printed;

    if (context != PJSIP_URI_IN_REQ_URI) {
        copy_advance_char_check(buf, '>');
    }

    *buf = '\0';
    return buf-startbuf;
}

PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst,
                                    const pjsip_name_addr *src)
{
    pj_strdup( pool, &dst->display, &src->display);
    dst->uri = (pjsip_uri*) pjsip_uri_clone(pool, src->uri);
}

static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, 
                                               const pjsip_name_addr *rhs)
{
    pjsip_name_addr *addr = PJ_POOL_ALLOC_T(pool, pjsip_name_addr);
    if (!addr)
        return NULL;

    pjsip_name_addr_init(addr);
    pjsip_name_addr_assign(pool, addr, rhs);
    return addr;
}

static int pjsip_name_addr_compare(  pjsip_uri_context_e context,
                                     const pjsip_name_addr *naddr1,
                                     const pjsip_name_addr *naddr2)
{
    int d;

    /* Check that naddr2 is also a name_addr */
    if (naddr1->vptr != naddr2->vptr)
        return -1;

    /* I'm not sure whether display name is included in the comparison. */
    if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) {
        return -1;
    }

    pj_assert( naddr1->uri != NULL );
    pj_assert( naddr2->uri != NULL );

    /* Compare name-addr as URL */
    d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri);
    if (d)
        return d;

    return 0;
}

///////////////////////////////////////////////////////////////////////////////

static const pj_str_t *other_uri_get_scheme( const pjsip_other_uri*);
static void *other_uri_get_uri( pjsip_other_uri*);
static pj_ssize_t other_uri_print( pjsip_uri_context_e context,
                                   const pjsip_other_uri *url, 
                                   char *buf, pj_size_t size);
static int other_uri_cmp( pjsip_uri_context_e context,
                          const pjsip_other_uri *url1, 
                          const pjsip_other_uri *url2);
static pjsip_other_uri* other_uri_clone( pj_pool_t *pool, 
                                         const pjsip_other_uri *rhs);

static pjsip_uri_vptr other_uri_vptr = 
{
    (P_GET_SCHEME)  &other_uri_get_scheme,
    (P_GET_URI)     &other_uri_get_uri,
    (P_PRINT_URI)   &other_uri_print,
    (P_CMP_URI)     &other_uri_cmp,
    (P_CLONE)       &other_uri_clone
};


PJ_DEF(pjsip_other_uri*) pjsip_other_uri_create(pj_pool_t *pool) 
{
    pjsip_other_uri *uri = PJ_POOL_ZALLOC_T(pool, pjsip_other_uri);
    uri->vptr = &other_uri_vptr;
    return uri;
}

static const pj_str_t *other_uri_get_scheme( const pjsip_other_uri *uri )
{
        return &uri->scheme;
}

static void *other_uri_get_uri( pjsip_other_uri *uri )
{
    return uri;
}

static pj_ssize_t other_uri_print(pjsip_uri_context_e context,
                                  const pjsip_other_uri *uri, 
                                  char *buf, pj_size_t size)
{
    char *startbuf = buf;
    char *endbuf = buf + size - 1; // Need to minus one for NULL terminator
    
    PJ_UNUSED_ARG(context);
    
    if (uri->scheme.slen + uri->content.slen + 1 > (int)size)
        return -1;

    /* Print scheme. */
    copy_advance(buf, uri->scheme);
    copy_advance_char_check(buf, ':');
    
    /* Print content. */
    copy_advance(buf, uri->content);
    
    *buf = '\0';
    return (buf - startbuf);
}

static int other_uri_cmp(pjsip_uri_context_e context,
                         const pjsip_other_uri *uri1,
                         const pjsip_other_uri *uri2)
{
    PJ_UNUSED_ARG(context);

    /* Check that uri2 is also an other_uri */
    if (uri1->vptr != uri2->vptr)
        return -1;
    
    /* Scheme must match. */
    if (pj_stricmp(&uri1->scheme, &uri2->scheme) != 0) {
        return PJSIP_ECMPSCHEME;
    }
    
    /* Content must match. */
    if(pj_stricmp(&uri1->content, &uri2->content) != 0) {
        return -1;
    }
    
    /* Equal. */
    return 0;
}

/* Clone *: URI */
static pjsip_other_uri* other_uri_clone(pj_pool_t *pool, 
                                        const pjsip_other_uri *rhs) 
{
    pjsip_other_uri *uri = pjsip_other_uri_create(pool);
    pj_strdup(pool, &uri->scheme, &rhs->scheme);
    pj_strdup(pool, &uri->content, &rhs->content);

    return uri;
}

