1
/* $Id: sip_tel_uri.c 4322 2013-01-17 10:09:09Z bennylp $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjsip/sip_tel_uri.h>
21
#include <pjsip/sip_msg.h>
22
#include <pjsip/sip_parser.h>
23
#include <pjsip/print_util.h>
25
#include <pj/assert.h>
26
#include <pj/string.h>
28
#include <pj/except.h>
29
#include <pjlib-util/string.h>
30
#include <pjlib-util/scanner.h>
33
#define DIGITS "0123456789"
34
#define HEX "aAbBcCdDeEfF"
35
#define HEX_DIGITS DIGITS HEX
36
#define VISUAL_SEP "-.()"
37
#define PHONE_DIGITS DIGITS VISUAL_SEP
38
#define GLOBAL_DIGITS "+" PHONE_DIGITS
39
#define LOCAL_DIGITS HEX_DIGITS "*#" VISUAL_SEP
40
#define NUMBER_SPEC LOCAL_DIGITS GLOBAL_DIGITS
41
#define PHONE_CONTEXT ALPHA GLOBAL_DIGITS
42
//#define RESERVED ";/?:@&=+$,"
43
#define RESERVED "/:@&$,+"
44
#define MARK "-_.!~*'()"
45
#define UNRESERVED ALPHA DIGITS MARK
47
#define URIC RESERVED UNRESERVED ESCAPED "[]+"
48
#define PARAM_UNRESERVED "[]/:&+$"
49
#define PARAM_CHAR PARAM_UNRESERVED UNRESERVED ESCAPED
51
static pj_cis_buf_t cis_buf;
52
static pj_cis_t pjsip_TEL_NUMBER_SPEC;
53
static pj_cis_t pjsip_TEL_EXT_VALUE_SPEC;
54
static pj_cis_t pjsip_TEL_PHONE_CONTEXT_SPEC;
55
static pj_cis_t pjsip_TEL_URIC_SPEC;
56
static pj_cis_t pjsip_TEL_VISUAL_SEP_SPEC;
57
static pj_cis_t pjsip_TEL_PNAME_SPEC;
58
static pj_cis_t pjsip_TEL_PVALUE_SPEC;
59
static pj_cis_t pjsip_TEL_PVALUE_SPEC_ESC;
60
static pj_cis_t pjsip_TEL_PARSING_PVALUE_SPEC;
61
static pj_cis_t pjsip_TEL_PARSING_PVALUE_SPEC_ESC;
63
static pj_str_t pjsip_ISUB_STR = { "isub", 4 };
64
static pj_str_t pjsip_EXT_STR = { "ext", 3 };
65
static pj_str_t pjsip_PH_CTX_STR = { "phone-context", 13 };
68
static const pj_str_t *tel_uri_get_scheme( const pjsip_tel_uri* );
69
static void *tel_uri_get_uri( pjsip_tel_uri* );
70
static pj_ssize_t tel_uri_print( pjsip_uri_context_e context,
71
const pjsip_tel_uri *url,
72
char *buf, pj_size_t size);
73
static int tel_uri_cmp( pjsip_uri_context_e context,
74
const pjsip_tel_uri *url1, const pjsip_tel_uri *url2);
75
static pjsip_tel_uri* tel_uri_clone(pj_pool_t *pool, const pjsip_tel_uri *rhs);
76
static void* tel_uri_parse( pj_scanner *scanner, pj_pool_t *pool,
77
pj_bool_t parse_params);
79
typedef const pj_str_t* (*P_GET_SCHEME)(const void*);
80
typedef void* (*P_GET_URI)(void*);
81
typedef pj_ssize_t (*P_PRINT_URI)(pjsip_uri_context_e,const void *,
83
typedef int (*P_CMP_URI)(pjsip_uri_context_e, const void*,
85
typedef void* (*P_CLONE)(pj_pool_t*, const void*);
87
static pjsip_uri_vptr tel_uri_vptr =
89
(P_GET_SCHEME) &tel_uri_get_scheme,
90
(P_GET_URI) &tel_uri_get_uri,
91
(P_PRINT_URI) &tel_uri_print,
92
(P_CMP_URI) &tel_uri_cmp,
93
(P_CLONE) &tel_uri_clone
97
PJ_DEF(pjsip_tel_uri*) pjsip_tel_uri_create(pj_pool_t *pool)
99
pjsip_tel_uri *uri = PJ_POOL_ZALLOC_T(pool, pjsip_tel_uri);
100
uri->vptr = &tel_uri_vptr;
101
pj_list_init(&uri->other_param);
106
static const pj_str_t *tel_uri_get_scheme( const pjsip_tel_uri *uri )
109
return &pjsip_parser_const()->pjsip_TEL_STR;
112
static void *tel_uri_get_uri( pjsip_tel_uri *uri )
118
pj_status_t pjsip_tel_uri_subsys_init(void)
122
pj_cis_buf_init(&cis_buf);
124
status = pj_cis_init(&cis_buf, &pjsip_TEL_EXT_VALUE_SPEC);
125
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
126
pj_cis_add_str(&pjsip_TEL_EXT_VALUE_SPEC, PHONE_DIGITS);
128
status = pj_cis_init(&cis_buf, &pjsip_TEL_NUMBER_SPEC);
129
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
130
pj_cis_add_str(&pjsip_TEL_NUMBER_SPEC, NUMBER_SPEC);
132
status = pj_cis_init(&cis_buf, &pjsip_TEL_VISUAL_SEP_SPEC);
133
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
134
pj_cis_add_str(&pjsip_TEL_VISUAL_SEP_SPEC, VISUAL_SEP);
136
status = pj_cis_init(&cis_buf, &pjsip_TEL_PHONE_CONTEXT_SPEC);
137
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
138
pj_cis_add_alpha(&pjsip_TEL_PHONE_CONTEXT_SPEC);
139
pj_cis_add_num(&pjsip_TEL_PHONE_CONTEXT_SPEC);
140
pj_cis_add_str(&pjsip_TEL_PHONE_CONTEXT_SPEC, PHONE_CONTEXT);
142
status = pj_cis_init(&cis_buf, &pjsip_TEL_URIC_SPEC);
143
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
144
pj_cis_add_alpha(&pjsip_TEL_URIC_SPEC);
145
pj_cis_add_num(&pjsip_TEL_URIC_SPEC);
146
pj_cis_add_str(&pjsip_TEL_URIC_SPEC, URIC);
148
status = pj_cis_init(&cis_buf, &pjsip_TEL_PNAME_SPEC);
149
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
150
pj_cis_add_alpha(&pjsip_TEL_PNAME_SPEC);
151
pj_cis_add_num(&pjsip_TEL_PNAME_SPEC);
152
pj_cis_add_str(&pjsip_TEL_PNAME_SPEC, "-");
154
status = pj_cis_init(&cis_buf, &pjsip_TEL_PVALUE_SPEC);
155
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
156
pj_cis_add_alpha(&pjsip_TEL_PVALUE_SPEC);
157
pj_cis_add_num(&pjsip_TEL_PVALUE_SPEC);
158
pj_cis_add_str(&pjsip_TEL_PVALUE_SPEC, PARAM_CHAR);
160
status = pj_cis_dup(&pjsip_TEL_PVALUE_SPEC_ESC, &pjsip_TEL_PVALUE_SPEC);
161
pj_cis_del_str(&pjsip_TEL_PVALUE_SPEC_ESC, "%");
163
status = pj_cis_dup(&pjsip_TEL_PARSING_PVALUE_SPEC, &pjsip_TEL_URIC_SPEC);
164
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
165
pj_cis_add_cis(&pjsip_TEL_PARSING_PVALUE_SPEC, &pjsip_TEL_PVALUE_SPEC);
166
pj_cis_add_str(&pjsip_TEL_PARSING_PVALUE_SPEC, "=");
168
status = pj_cis_dup(&pjsip_TEL_PARSING_PVALUE_SPEC_ESC,
169
&pjsip_TEL_PARSING_PVALUE_SPEC);
170
pj_cis_del_str(&pjsip_TEL_PARSING_PVALUE_SPEC_ESC, "%");
172
status = pjsip_register_uri_parser("tel", &tel_uri_parse);
173
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
179
static pj_ssize_t tel_uri_print( pjsip_uri_context_e context,
180
const pjsip_tel_uri *uri,
181
char *buf, pj_size_t size)
184
char *startbuf = buf;
185
char *endbuf = buf+size-1;
186
const pjsip_parser_const_t *pc = pjsip_parser_const();
188
PJ_UNUSED_ARG(context);
191
copy_advance(buf, pc->pjsip_TEL_STR);
195
copy_advance_escape(buf, uri->number, pjsip_TEL_NUMBER_SPEC);
197
/* ISDN sub-address or extension must appear first. */
199
/* Extension param. */
200
copy_advance_pair_escape(buf, ";ext=", 5, uri->ext_param,
201
pjsip_TEL_EXT_VALUE_SPEC);
203
/* ISDN sub-address. */
204
copy_advance_pair_escape(buf, ";isub=", 6, uri->isub_param,
205
pjsip_TEL_URIC_SPEC);
207
/* Followed by phone context, if present. */
208
copy_advance_pair_escape(buf, ";phone-context=", 15, uri->context,
209
pjsip_TEL_PHONE_CONTEXT_SPEC);
212
/* Print other parameters. */
213
printed = pjsip_param_print_on(&uri->other_param, buf, (endbuf-buf),
214
&pjsip_TEL_PNAME_SPEC,
215
&pjsip_TEL_PVALUE_SPEC, ';');
222
return (buf-startbuf);
225
/* Compare two numbers, according to RFC 3966:
226
* - both must be either local or global numbers.
227
* - The 'global-number-digits' and the 'local-number-digits' must be
228
* equal, after removing all visual separators.
230
PJ_DEF(int) pjsip_tel_nb_cmp(const pj_str_t *number1, const pj_str_t *number2)
232
const char *s1 = number1->ptr,
233
*e1 = number1->ptr + number1->slen,
235
*e2 = number2->ptr + number2->slen;
237
/* Compare each number, ignoreing visual separators. */
238
while (s1!=e1 && s2!=e2) {
241
if (pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s1)) {
245
if (pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s2)) {
250
diff = pj_tolower(*s1) - pj_tolower(*s2);
258
/* Exhaust remaining visual separators. */
259
while (s1!=e1 && pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s1))
261
while (s2!=e2 && pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s2))
264
if (s1==e1 && s2==e2)
272
/* Compare two tel: URI */
273
static int tel_uri_cmp( pjsip_uri_context_e context,
274
const pjsip_tel_uri *url1, const pjsip_tel_uri *url2)
278
PJ_UNUSED_ARG(context);
280
/* Scheme must match. */
281
if (url1->vptr != url2->vptr)
284
/* Compare number. */
285
result = pjsip_tel_nb_cmp(&url1->number, &url2->number);
289
/* Compare phone-context as hostname or as as global nb. */
290
if (url1->context.slen) {
291
if (*url1->context.ptr != '+')
292
result = pj_stricmp(&url1->context, &url2->context);
294
result = pjsip_tel_nb_cmp(&url1->context, &url2->context);
299
} else if (url2->context.slen)
302
/* Compare extension. */
303
if (url1->ext_param.slen) {
304
result = pjsip_tel_nb_cmp(&url1->ext_param, &url2->ext_param);
309
/* Compare isub bytes by bytes. */
310
if (url1->isub_param.slen) {
311
result = pj_stricmp(&url1->isub_param, &url2->isub_param);
316
/* Other parameters are compared regardless of the order.
317
* If one URI has parameter not found in the other URI, the URIs are
320
if (url1->other_param.next != &url1->other_param) {
321
const pjsip_param *p1, *p2;
322
int cnt1 = 0, cnt2 = 0;
324
p1 = url1->other_param.next;
325
while (p1 != &url1->other_param) {
326
p2 = pjsip_param_cfind(&url2->other_param, &p1->name);
330
result = pj_stricmp(&p1->value, &p2->value);
338
p2 = url2->other_param.next;
339
while (p2 != &url2->other_param)
340
++cnt2, p2 = p2->next;
344
else if (cnt1 > cnt2)
347
} else if (url2->other_param.next != &url2->other_param)
355
static pjsip_tel_uri* tel_uri_clone(pj_pool_t *pool, const pjsip_tel_uri *rhs)
357
pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
359
pj_strdup(pool, &uri->number, &rhs->number);
360
pj_strdup(pool, &uri->context, &rhs->context);
361
pj_strdup(pool, &uri->ext_param, &rhs->ext_param);
362
pj_strdup(pool, &uri->isub_param, &rhs->isub_param);
363
pjsip_param_clone(pool, &uri->other_param, &rhs->other_param);
369
* THis actually returns (pjsip_tel_uri *) type.
371
static void* tel_uri_parse( pj_scanner *scanner, pj_pool_t *pool,
372
pj_bool_t parse_params)
376
int skip_ws = scanner->skip_ws;
377
const pjsip_parser_const_t *pc = pjsip_parser_const();
379
scanner->skip_ws = 0;
382
pj_scan_get(scanner, &pc->pjsip_TOKEN_SPEC, &token);
383
if (pj_scan_get_char(scanner) != ':')
384
PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
385
if (pj_stricmp_alnum(&token, &pc->pjsip_TEL_STR) != 0)
386
PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
389
uri = pjsip_tel_uri_create(pool);
391
/* Get the phone number. */
392
#if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0
393
pj_scan_get_unescape(scanner, &pjsip_TEL_NUMBER_SPEC, &uri->number);
395
pj_scan_get(scanner, &pjsip_TEL_NUMBER_SPEC, &uri->number);
396
uri->number = pj_str_unescape(pool, &uri->number);
399
/* Get all parameters. */
400
if (parse_params && *scanner->curptr==';') {
401
pj_str_t pname, pvalue;
402
const pjsip_parser_const_t *pc = pjsip_parser_const();
405
/* Eat the ';' separator. */
406
pj_scan_get_char(scanner);
409
pj_scan_get(scanner, &pc->pjsip_PARAM_CHAR_SPEC, &pname);
411
if (*scanner->curptr == '=') {
412
pj_scan_get_char(scanner);
414
# if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0
415
pj_scan_get_unescape(scanner,
416
&pjsip_TEL_PARSING_PVALUE_SPEC_ESC,
419
pj_scan_get(scanner, &pjsip_TEL_PARSING_PVALUE_SPEC,
421
pvalue = pj_str_unescape(pool, &pvalue);
429
/* Save the parameters. */
430
if (pj_stricmp_alnum(&pname, &pjsip_ISUB_STR)==0) {
431
uri->isub_param = pvalue;
432
} else if (pj_stricmp_alnum(&pname, &pjsip_EXT_STR)==0) {
433
uri->ext_param = pvalue;
434
} else if (pj_stricmp_alnum(&pname, &pjsip_PH_CTX_STR)==0) {
435
uri->context = pvalue;
437
pjsip_param *param = PJ_POOL_ALLOC_T(pool, pjsip_param);
439
param->value = pvalue;
440
pj_list_insert_before(&uri->other_param, param);
443
} while (*scanner->curptr==';');
446
scanner->skip_ws = skip_ws;
447
pj_scan_skip_whitespace(scanner);