diff options
author | Benny Prijono <bennylp@teluu.com> | 2005-11-23 20:56:30 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2005-11-23 20:56:30 +0000 |
commit | 9894af102a5d2e61e7629b99b7746210a8fd3475 (patch) | |
tree | 261a8df5003bab4bd60a3218ce06f845386dc7b9 /pjsip/src/pjsip/sip_tel_uri.c | |
parent | 3366b25100e19ce91de500de827aed0606390f70 (diff) |
Added tel: uri and user-defined uri parser
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@82 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip/src/pjsip/sip_tel_uri.c')
-rw-r--r-- | pjsip/src/pjsip/sip_tel_uri.c | 416 |
1 files changed, 416 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 00000000..4563f6dc --- /dev/null +++ b/pjsip/src/pjsip/sip_tel_uri.c @@ -0,0 +1,416 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 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_PARSING_PVALUE_SPEC; + +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 int 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 pjsip_tel_uri *tel_uri_parse( pj_scanner *scanner, pj_pool_t *pool, + pj_bool_t parse_params); + +#ifdef __GNUC__ +# define HAPPY_FLAG (void*) +#else +# define HAPPY_FLAG +#endif + +static pjsip_uri_vptr tel_uri_vptr = +{ + HAPPY_FLAG &tel_uri_get_scheme, + HAPPY_FLAG &tel_uri_get_uri, + HAPPY_FLAG &tel_uri_print, + HAPPY_FLAG &tel_uri_cmp, + HAPPY_FLAG &tel_uri_clone +}; + + +PJ_DEF(pjsip_tel_uri*) pjsip_tel_uri_create(pj_pool_t *pool) +{ + pjsip_tel_uri *uri = pj_pool_zalloc(pool, sizeof(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_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_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 = pjsip_register_uri_parser("tel", &tel_uri_parse); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + + return PJ_SUCCESS; +} + +/* Print tel: URI */ +static int 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; + + PJ_UNUSED_ARG(context); + + /* Print scheme. */ + copy_advance(buf, 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 */ +static pjsip_tel_uri *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; + + scanner->skip_ws = 0; + + /* Parse scheme. */ + pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &token); + if (pj_scan_get_char(scanner) != ':') + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + if (pj_stricmp_alnum(&token, &pjsip_TEL_STR) != 0) + PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); + + /* Create URI */ + uri = pjsip_tel_uri_create(pool); + + /* Get the phone number. */ + pj_scan_get(scanner, &pjsip_TEL_NUMBER_SPEC, &uri->number); + uri->number = pj_str_unescape(pool, &uri->number); + + /* Get all parameters. */ + if (parse_params && *scanner->curptr==';') { + pj_str_t pname, pvalue; + + do { + /* Eat the ';' separator. */ + pj_scan_get_char(scanner); + + /* Get pname. */ + pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, &pname); + + if (*scanner->curptr == '=') { + pj_scan_get_char(scanner); + pj_scan_get(scanner, &pjsip_TEL_PARSING_PVALUE_SPEC, &pvalue); + } 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(pool, sizeof(pjsip_param)); + param->name = pname; + param->value = pvalue; + pj_list_insert_before(&uri->other_param, param); + } + + } while (*scanner->curptr==';'); + } + + scanner->skip_ws = skip_ws; + return uri; +} + |