diff options
Diffstat (limited to 'pjsip/src/pjsip/sip_tel_uri.c')
-rw-r--r-- | pjsip/src/pjsip/sip_tel_uri.c | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/pjsip/src/pjsip/sip_tel_uri.c b/pjsip/src/pjsip/sip_tel_uri.c new file mode 100644 index 0000000..4120ae0 --- /dev/null +++ b/pjsip/src/pjsip/sip_tel_uri.c @@ -0,0 +1,448 @@ +/* $Id: sip_tel_uri.c 3553 2011-05-05 06:14:19Z nanang $ */ +/* + * 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_tel_uri.h> +#include <pjsip/sip_msg.h> +#include <pjsip/sip_parser.h> +#include <pjsip/print_util.h> +#include <pj/pool.h> +#include <pj/assert.h> +#include <pj/string.h> +#include <pj/ctype.h> +#include <pj/except.h> +#include <pjlib-util/string.h> +#include <pjlib-util/scanner.h> + +#define ALPHA +#define DIGITS "0123456789" +#define HEX "aAbBcCdDeEfF" +#define HEX_DIGITS DIGITS HEX +#define VISUAL_SEP "-.()" +#define PHONE_DIGITS DIGITS VISUAL_SEP +#define GLOBAL_DIGITS "+" PHONE_DIGITS +#define LOCAL_DIGITS HEX_DIGITS "*#" VISUAL_SEP +#define NUMBER_SPEC LOCAL_DIGITS GLOBAL_DIGITS +#define PHONE_CONTEXT ALPHA GLOBAL_DIGITS +//#define RESERVED ";/?:@&=+$," +#define RESERVED "/:@&$,+" +#define MARK "-_.!~*'()" +#define UNRESERVED ALPHA DIGITS MARK +#define ESCAPED "%" +#define URIC RESERVED UNRESERVED ESCAPED "[]+" +#define PARAM_UNRESERVED "[]/:&+$" +#define PARAM_CHAR PARAM_UNRESERVED UNRESERVED ESCAPED + +static pj_cis_buf_t cis_buf; +static pj_cis_t pjsip_TEL_NUMBER_SPEC; +static pj_cis_t pjsip_TEL_EXT_VALUE_SPEC; +static pj_cis_t pjsip_TEL_PHONE_CONTEXT_SPEC; +static pj_cis_t pjsip_TEL_URIC_SPEC; +static pj_cis_t pjsip_TEL_VISUAL_SEP_SPEC; +static pj_cis_t pjsip_TEL_PNAME_SPEC; +static pj_cis_t pjsip_TEL_PVALUE_SPEC; +static pj_cis_t pjsip_TEL_PVALUE_SPEC_ESC; +static pj_cis_t pjsip_TEL_PARSING_PVALUE_SPEC; +static pj_cis_t pjsip_TEL_PARSING_PVALUE_SPEC_ESC; + +static pj_str_t pjsip_ISUB_STR = { "isub", 4 }; +static pj_str_t pjsip_EXT_STR = { "ext", 3 }; +static pj_str_t pjsip_PH_CTX_STR = { "phone-context", 13 }; + + +static const pj_str_t *tel_uri_get_scheme( const pjsip_tel_uri* ); +static void *tel_uri_get_uri( pjsip_tel_uri* ); +static pj_ssize_t tel_uri_print( pjsip_uri_context_e context, + const pjsip_tel_uri *url, + char *buf, pj_size_t size); +static int tel_uri_cmp( pjsip_uri_context_e context, + const pjsip_tel_uri *url1, const pjsip_tel_uri *url2); +static pjsip_tel_uri* tel_uri_clone(pj_pool_t *pool, const pjsip_tel_uri *rhs); +static void* tel_uri_parse( pj_scanner *scanner, pj_pool_t *pool, + pj_bool_t parse_params); + +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 tel_uri_vptr = +{ + (P_GET_SCHEME) &tel_uri_get_scheme, + (P_GET_URI) &tel_uri_get_uri, + (P_PRINT_URI) &tel_uri_print, + (P_CMP_URI) &tel_uri_cmp, + (P_CLONE) &tel_uri_clone +}; + + +PJ_DEF(pjsip_tel_uri*) pjsip_tel_uri_create(pj_pool_t *pool) +{ + pjsip_tel_uri *uri = PJ_POOL_ZALLOC_T(pool, pjsip_tel_uri); + uri->vptr = &tel_uri_vptr; + pj_list_init(&uri->other_param); + return uri; +} + + +static const pj_str_t *tel_uri_get_scheme( const pjsip_tel_uri *uri ) +{ + PJ_UNUSED_ARG(uri); + return &pjsip_parser_const()->pjsip_TEL_STR; +} + +static void *tel_uri_get_uri( pjsip_tel_uri *uri ) +{ + return uri; +} + + +pj_status_t pjsip_tel_uri_subsys_init(void) +{ + pj_status_t status; + + pj_cis_buf_init(&cis_buf); + + status = pj_cis_init(&cis_buf, &pjsip_TEL_EXT_VALUE_SPEC); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + pj_cis_add_str(&pjsip_TEL_EXT_VALUE_SPEC, PHONE_DIGITS); + + status = pj_cis_init(&cis_buf, &pjsip_TEL_NUMBER_SPEC); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + pj_cis_add_str(&pjsip_TEL_NUMBER_SPEC, NUMBER_SPEC); + + status = pj_cis_init(&cis_buf, &pjsip_TEL_VISUAL_SEP_SPEC); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + pj_cis_add_str(&pjsip_TEL_VISUAL_SEP_SPEC, VISUAL_SEP); + + status = pj_cis_init(&cis_buf, &pjsip_TEL_PHONE_CONTEXT_SPEC); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + pj_cis_add_alpha(&pjsip_TEL_PHONE_CONTEXT_SPEC); + pj_cis_add_num(&pjsip_TEL_PHONE_CONTEXT_SPEC); + pj_cis_add_str(&pjsip_TEL_PHONE_CONTEXT_SPEC, PHONE_CONTEXT); + + status = pj_cis_init(&cis_buf, &pjsip_TEL_URIC_SPEC); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + pj_cis_add_alpha(&pjsip_TEL_URIC_SPEC); + pj_cis_add_num(&pjsip_TEL_URIC_SPEC); + pj_cis_add_str(&pjsip_TEL_URIC_SPEC, URIC); + + status = pj_cis_init(&cis_buf, &pjsip_TEL_PNAME_SPEC); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + pj_cis_add_alpha(&pjsip_TEL_PNAME_SPEC); + pj_cis_add_num(&pjsip_TEL_PNAME_SPEC); + pj_cis_add_str(&pjsip_TEL_PNAME_SPEC, "-"); + + status = pj_cis_init(&cis_buf, &pjsip_TEL_PVALUE_SPEC); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + pj_cis_add_alpha(&pjsip_TEL_PVALUE_SPEC); + pj_cis_add_num(&pjsip_TEL_PVALUE_SPEC); + pj_cis_add_str(&pjsip_TEL_PVALUE_SPEC, PARAM_CHAR); + + status = pj_cis_dup(&pjsip_TEL_PVALUE_SPEC_ESC, &pjsip_TEL_PVALUE_SPEC); + pj_cis_del_str(&pjsip_TEL_PVALUE_SPEC_ESC, "%"); + + status = pj_cis_dup(&pjsip_TEL_PARSING_PVALUE_SPEC, &pjsip_TEL_URIC_SPEC); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + pj_cis_add_cis(&pjsip_TEL_PARSING_PVALUE_SPEC, &pjsip_TEL_PVALUE_SPEC); + pj_cis_add_str(&pjsip_TEL_PARSING_PVALUE_SPEC, "="); + + status = pj_cis_dup(&pjsip_TEL_PARSING_PVALUE_SPEC_ESC, + &pjsip_TEL_PARSING_PVALUE_SPEC); + pj_cis_del_str(&pjsip_TEL_PARSING_PVALUE_SPEC_ESC, "%"); + + status = pjsip_register_uri_parser("tel", &tel_uri_parse); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + + return PJ_SUCCESS; +} + +/* Print tel: URI */ +static pj_ssize_t tel_uri_print( pjsip_uri_context_e context, + const pjsip_tel_uri *uri, + char *buf, pj_size_t size) +{ + int printed; + char *startbuf = buf; + char *endbuf = buf+size; + const pjsip_parser_const_t *pc = pjsip_parser_const(); + + PJ_UNUSED_ARG(context); + + /* Print scheme. */ + copy_advance(buf, pc->pjsip_TEL_STR); + *buf++ = ':'; + + /* Print number. */ + copy_advance_escape(buf, uri->number, pjsip_TEL_NUMBER_SPEC); + + /* ISDN sub-address or extension must appear first. */ + + /* Extension param. */ + copy_advance_pair_escape(buf, ";ext=", 5, uri->ext_param, + pjsip_TEL_EXT_VALUE_SPEC); + + /* ISDN sub-address. */ + copy_advance_pair_escape(buf, ";isub=", 6, uri->isub_param, + pjsip_TEL_URIC_SPEC); + + /* Followed by phone context, if present. */ + copy_advance_pair_escape(buf, ";phone-context=", 15, uri->context, + pjsip_TEL_PHONE_CONTEXT_SPEC); + + + /* Print other parameters. */ + printed = pjsip_param_print_on(&uri->other_param, buf, (endbuf-buf), + &pjsip_TEL_PNAME_SPEC, + &pjsip_TEL_PVALUE_SPEC, ';'); + if (printed < 0) + return -1; + buf += printed; + + return (buf-startbuf); +} + +/* Compare two numbers, according to RFC 3966: + * - both must be either local or global numbers. + * - The 'global-number-digits' and the 'local-number-digits' must be + * equal, after removing all visual separators. + */ +PJ_DEF(int) pjsip_tel_nb_cmp(const pj_str_t *number1, const pj_str_t *number2) +{ + const char *s1 = number1->ptr, + *e1 = number1->ptr + number1->slen, + *s2 = number2->ptr, + *e2 = number2->ptr + number2->slen; + + /* Compare each number, ignoreing visual separators. */ + while (s1!=e1 && s2!=e2) { + int diff; + + if (pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s1)) { + ++s1; + continue; + } + if (pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s2)) { + ++s2; + continue; + } + + diff = pj_tolower(*s1) - pj_tolower(*s2); + if (!diff) { + ++s1, ++s2; + continue; + } else + return diff; + } + + /* Exhaust remaining visual separators. */ + while (s1!=e1 && pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s1)) + ++s1; + while (s2!=e2 && pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s2)) + ++s2; + + if (s1==e1 && s2==e2) + return 0; + else if (s1==e1) + return -1; + else + return 1; +} + +/* Compare two tel: URI */ +static int tel_uri_cmp( pjsip_uri_context_e context, + const pjsip_tel_uri *url1, const pjsip_tel_uri *url2) +{ + int result; + + PJ_UNUSED_ARG(context); + + /* Scheme must match. */ + if (url1->vptr != url2->vptr) + return -1; + + /* Compare number. */ + result = pjsip_tel_nb_cmp(&url1->number, &url2->number); + if (result != 0) + return result; + + /* Compare phone-context as hostname or as as global nb. */ + if (url1->context.slen) { + if (*url1->context.ptr != '+') + result = pj_stricmp(&url1->context, &url2->context); + else + result = pjsip_tel_nb_cmp(&url1->context, &url2->context); + + if (result != 0) + return result; + + } else if (url2->context.slen) + return -1; + + /* Compare extension. */ + if (url1->ext_param.slen) { + result = pjsip_tel_nb_cmp(&url1->ext_param, &url2->ext_param); + if (result != 0) + return result; + } + + /* Compare isub bytes by bytes. */ + if (url1->isub_param.slen) { + result = pj_stricmp(&url1->isub_param, &url2->isub_param); + if (result != 0) + return result; + } + + /* Other parameters are compared regardless of the order. + * If one URI has parameter not found in the other URI, the URIs are + * not equal. + */ + if (url1->other_param.next != &url1->other_param) { + const pjsip_param *p1, *p2; + int cnt1 = 0, cnt2 = 0; + + p1 = url1->other_param.next; + while (p1 != &url1->other_param) { + p2 = pjsip_param_cfind(&url2->other_param, &p1->name); + if (!p2 ) + return 1; + + result = pj_stricmp(&p1->value, &p2->value); + if (result != 0) + return result; + + p1 = p1->next; + ++cnt1; + } + + p2 = url2->other_param.next; + while (p2 != &url2->other_param) + ++cnt2, p2 = p2->next; + + if (cnt1 < cnt2) + return -1; + else if (cnt1 > cnt2) + return 1; + + } else if (url2->other_param.next != &url2->other_param) + return -1; + + /* Equal. */ + return 0; +} + +/* Clone tel: URI */ +static pjsip_tel_uri* tel_uri_clone(pj_pool_t *pool, const pjsip_tel_uri *rhs) +{ + pjsip_tel_uri *uri = pjsip_tel_uri_create(pool); + + pj_strdup(pool, &uri->number, &rhs->number); + pj_strdup(pool, &uri->context, &rhs->context); + pj_strdup(pool, &uri->ext_param, &rhs->ext_param); + pj_strdup(pool, &uri->isub_param, &rhs->isub_param); + pjsip_param_clone(pool, &uri->other_param, &rhs->other_param); + + return uri; +} + +/* Parse tel: URI + * THis actually returns (pjsip_tel_uri *) type. + */ +static void* tel_uri_parse( pj_scanner *scanner, pj_pool_t *pool, + pj_bool_t parse_params) +{ + pjsip_tel_uri *uri; + pj_str_t token; + int skip_ws = scanner->skip_ws; + const pjsip_parser_const_t *pc = pjsip_parser_const(); + + scanner->skip_ws = 0; + + /* Parse scheme. */ + pj_scan_get(scanner, &pc->pjsip_TOKEN_SPEC, &token); + if (pj_scan_get_char(scanner) != ':') + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + if (pj_stricmp_alnum(&token, &pc->pjsip_TEL_STR) != 0) + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + + /* Create URI */ + uri = pjsip_tel_uri_create(pool); + + /* Get the phone number. */ +#if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0 + pj_scan_get_unescape(scanner, &pjsip_TEL_NUMBER_SPEC, &uri->number); +#else + pj_scan_get(scanner, &pjsip_TEL_NUMBER_SPEC, &uri->number); + uri->number = pj_str_unescape(pool, &uri->number); +#endif + + /* Get all parameters. */ + if (parse_params && *scanner->curptr==';') { + pj_str_t pname, pvalue; + const pjsip_parser_const_t *pc = pjsip_parser_const(); + + do { + /* Eat the ';' separator. */ + pj_scan_get_char(scanner); + + /* Get pname. */ + pj_scan_get(scanner, &pc->pjsip_PARAM_CHAR_SPEC, &pname); + + if (*scanner->curptr == '=') { + pj_scan_get_char(scanner); + +# if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0 + pj_scan_get_unescape(scanner, + &pjsip_TEL_PARSING_PVALUE_SPEC_ESC, + &pvalue); +# else + pj_scan_get(scanner, &pjsip_TEL_PARSING_PVALUE_SPEC, + &pvalue); + pvalue = pj_str_unescape(pool, &pvalue); +# endif + + } else { + pvalue.slen = 0; + pvalue.ptr = NULL; + } + + /* Save the parameters. */ + if (pj_stricmp_alnum(&pname, &pjsip_ISUB_STR)==0) { + uri->isub_param = pvalue; + } else if (pj_stricmp_alnum(&pname, &pjsip_EXT_STR)==0) { + uri->ext_param = pvalue; + } else if (pj_stricmp_alnum(&pname, &pjsip_PH_CTX_STR)==0) { + uri->context = pvalue; + } else { + pjsip_param *param = PJ_POOL_ALLOC_T(pool, pjsip_param); + param->name = pname; + param->value = pvalue; + pj_list_insert_before(&uri->other_param, param); + } + + } while (*scanner->curptr==';'); + } + + scanner->skip_ws = skip_ws; + pj_scan_skip_whitespace(scanner); + return uri; +} + |