/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * 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 #include #include #include #include #include #include #include #include #include #include #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-1; 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 = (int)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; *buf = '\0'; 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; 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; }