blob: 466a6995a5cea78530f49064fa91b63fda4a5ff7 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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.
10 *
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.
15 *
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
19 */
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>
24#include <pj/pool.h>
25#include <pj/assert.h>
26#include <pj/string.h>
27#include <pj/ctype.h>
28#include <pj/except.h>
29#include <pjlib-util/string.h>
30#include <pjlib-util/scanner.h>
31
32#define ALPHA
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
46#define ESCAPED "%"
47#define URIC RESERVED UNRESERVED ESCAPED "[]+"
48#define PARAM_UNRESERVED "[]/:&+$"
49#define PARAM_CHAR PARAM_UNRESERVED UNRESERVED ESCAPED
50
51static pj_cis_buf_t cis_buf;
52static pj_cis_t pjsip_TEL_NUMBER_SPEC;
53static pj_cis_t pjsip_TEL_EXT_VALUE_SPEC;
54static pj_cis_t pjsip_TEL_PHONE_CONTEXT_SPEC;
55static pj_cis_t pjsip_TEL_URIC_SPEC;
56static pj_cis_t pjsip_TEL_VISUAL_SEP_SPEC;
57static pj_cis_t pjsip_TEL_PNAME_SPEC;
58static pj_cis_t pjsip_TEL_PVALUE_SPEC;
59static pj_cis_t pjsip_TEL_PVALUE_SPEC_ESC;
60static pj_cis_t pjsip_TEL_PARSING_PVALUE_SPEC;
61static pj_cis_t pjsip_TEL_PARSING_PVALUE_SPEC_ESC;
62
63static pj_str_t pjsip_ISUB_STR = { "isub", 4 };
64static pj_str_t pjsip_EXT_STR = { "ext", 3 };
65static pj_str_t pjsip_PH_CTX_STR = { "phone-context", 13 };
66
67
68static const pj_str_t *tel_uri_get_scheme( const pjsip_tel_uri* );
69static void *tel_uri_get_uri( pjsip_tel_uri* );
70static pj_ssize_t tel_uri_print( pjsip_uri_context_e context,
71 const pjsip_tel_uri *url,
72 char *buf, pj_size_t size);
73static int tel_uri_cmp( pjsip_uri_context_e context,
74 const pjsip_tel_uri *url1, const pjsip_tel_uri *url2);
75static pjsip_tel_uri* tel_uri_clone(pj_pool_t *pool, const pjsip_tel_uri *rhs);
76static void* tel_uri_parse( pj_scanner *scanner, pj_pool_t *pool,
77 pj_bool_t parse_params);
78
79typedef const pj_str_t* (*P_GET_SCHEME)(const void*);
80typedef void* (*P_GET_URI)(void*);
81typedef pj_ssize_t (*P_PRINT_URI)(pjsip_uri_context_e,const void *,
82 char*,pj_size_t);
83typedef int (*P_CMP_URI)(pjsip_uri_context_e, const void*,
84 const void*);
85typedef void* (*P_CLONE)(pj_pool_t*, const void*);
86
87static pjsip_uri_vptr tel_uri_vptr =
88{
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
94};
95
96
97PJ_DEF(pjsip_tel_uri*) pjsip_tel_uri_create(pj_pool_t *pool)
98{
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);
102 return uri;
103}
104
105
106static const pj_str_t *tel_uri_get_scheme( const pjsip_tel_uri *uri )
107{
108 PJ_UNUSED_ARG(uri);
109 return &pjsip_parser_const()->pjsip_TEL_STR;
110}
111
112static void *tel_uri_get_uri( pjsip_tel_uri *uri )
113{
114 return uri;
115}
116
117
118pj_status_t pjsip_tel_uri_subsys_init(void)
119{
120 pj_status_t status;
121
122 pj_cis_buf_init(&cis_buf);
123
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);
127
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);
131
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);
135
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);
141
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);
147
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, "-");
153
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);
159
160 status = pj_cis_dup(&pjsip_TEL_PVALUE_SPEC_ESC, &pjsip_TEL_PVALUE_SPEC);
161 pj_cis_del_str(&pjsip_TEL_PVALUE_SPEC_ESC, "%");
162
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, "=");
167
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, "%");
171
172 status = pjsip_register_uri_parser("tel", &tel_uri_parse);
173 PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
174
175 return PJ_SUCCESS;
176}
177
178/* Print tel: URI */
179static pj_ssize_t tel_uri_print( pjsip_uri_context_e context,
180 const pjsip_tel_uri *uri,
181 char *buf, pj_size_t size)
182{
183 int printed;
184 char *startbuf = buf;
185 char *endbuf = buf+size-1;
186 const pjsip_parser_const_t *pc = pjsip_parser_const();
187
188 PJ_UNUSED_ARG(context);
189
190 /* Print scheme. */
191 copy_advance(buf, pc->pjsip_TEL_STR);
192 *buf++ = ':';
193
194 /* Print number. */
195 copy_advance_escape(buf, uri->number, pjsip_TEL_NUMBER_SPEC);
196
197 /* ISDN sub-address or extension must appear first. */
198
199 /* Extension param. */
200 copy_advance_pair_escape(buf, ";ext=", 5, uri->ext_param,
201 pjsip_TEL_EXT_VALUE_SPEC);
202
203 /* ISDN sub-address. */
204 copy_advance_pair_escape(buf, ";isub=", 6, uri->isub_param,
205 pjsip_TEL_URIC_SPEC);
206
207 /* Followed by phone context, if present. */
208 copy_advance_pair_escape(buf, ";phone-context=", 15, uri->context,
209 pjsip_TEL_PHONE_CONTEXT_SPEC);
210
211
212 /* Print other parameters. */
213 printed = (int)pjsip_param_print_on(&uri->other_param, buf, (endbuf-buf),
214 &pjsip_TEL_PNAME_SPEC,
215 &pjsip_TEL_PVALUE_SPEC, ';');
216 if (printed < 0)
217 return -1;
218 buf += printed;
219
220 *buf = '\0';
221
222 return (buf-startbuf);
223}
224
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.
229 */
230PJ_DEF(int) pjsip_tel_nb_cmp(const pj_str_t *number1, const pj_str_t *number2)
231{
232 const char *s1 = number1->ptr,
233 *e1 = number1->ptr + number1->slen,
234 *s2 = number2->ptr,
235 *e2 = number2->ptr + number2->slen;
236
237 /* Compare each number, ignoreing visual separators. */
238 while (s1!=e1 && s2!=e2) {
239 int diff;
240
241 if (pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s1)) {
242 ++s1;
243 continue;
244 }
245 if (pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s2)) {
246 ++s2;
247 continue;
248 }
249
250 diff = pj_tolower(*s1) - pj_tolower(*s2);
251 if (!diff) {
252 ++s1, ++s2;
253 continue;
254 } else
255 return diff;
256 }
257
258 /* Exhaust remaining visual separators. */
259 while (s1!=e1 && pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s1))
260 ++s1;
261 while (s2!=e2 && pj_cis_match(&pjsip_TEL_VISUAL_SEP_SPEC, *s2))
262 ++s2;
263
264 if (s1==e1 && s2==e2)
265 return 0;
266 else if (s1==e1)
267 return -1;
268 else
269 return 1;
270}
271
272/* Compare two tel: URI */
273static int tel_uri_cmp( pjsip_uri_context_e context,
274 const pjsip_tel_uri *url1, const pjsip_tel_uri *url2)
275{
276 int result;
277
278 PJ_UNUSED_ARG(context);
279
280 /* Scheme must match. */
281 if (url1->vptr != url2->vptr)
282 return -1;
283
284 /* Compare number. */
285 result = pjsip_tel_nb_cmp(&url1->number, &url2->number);
286 if (result != 0)
287 return result;
288
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);
293 else
294 result = pjsip_tel_nb_cmp(&url1->context, &url2->context);
295
296 if (result != 0)
297 return result;
298
299 } else if (url2->context.slen)
300 return -1;
301
302 /* Compare extension. */
303 if (url1->ext_param.slen) {
304 result = pjsip_tel_nb_cmp(&url1->ext_param, &url2->ext_param);
305 if (result != 0)
306 return result;
307 }
308
309 /* Compare isub bytes by bytes. */
310 if (url1->isub_param.slen) {
311 result = pj_stricmp(&url1->isub_param, &url2->isub_param);
312 if (result != 0)
313 return result;
314 }
315
316 /* Other parameters are compared regardless of the order.
317 * If one URI has parameter not found in the other URI, the URIs are
318 * not equal.
319 */
320 if (url1->other_param.next != &url1->other_param) {
321 const pjsip_param *p1, *p2;
322 int cnt1 = 0, cnt2 = 0;
323
324 p1 = url1->other_param.next;
325 while (p1 != &url1->other_param) {
326 p2 = pjsip_param_cfind(&url2->other_param, &p1->name);
327 if (!p2 )
328 return 1;
329
330 result = pj_stricmp(&p1->value, &p2->value);
331 if (result != 0)
332 return result;
333
334 p1 = p1->next;
335 ++cnt1;
336 }
337
338 p2 = url2->other_param.next;
339 while (p2 != &url2->other_param)
340 ++cnt2, p2 = p2->next;
341
342 if (cnt1 < cnt2)
343 return -1;
344 else if (cnt1 > cnt2)
345 return 1;
346
347 } else if (url2->other_param.next != &url2->other_param)
348 return -1;
349
350 /* Equal. */
351 return 0;
352}
353
354/* Clone tel: URI */
355static pjsip_tel_uri* tel_uri_clone(pj_pool_t *pool, const pjsip_tel_uri *rhs)
356{
357 pjsip_tel_uri *uri = pjsip_tel_uri_create(pool);
358
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);
364
365 return uri;
366}
367
368/* Parse tel: URI
369 * THis actually returns (pjsip_tel_uri *) type.
370 */
371static void* tel_uri_parse( pj_scanner *scanner, pj_pool_t *pool,
372 pj_bool_t parse_params)
373{
374 pjsip_tel_uri *uri;
375 pj_str_t token;
376 int skip_ws = scanner->skip_ws;
377 const pjsip_parser_const_t *pc = pjsip_parser_const();
378
379 scanner->skip_ws = 0;
380
381 /* Parse scheme. */
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);
387
388 /* Create URI */
389 uri = pjsip_tel_uri_create(pool);
390
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);
394#else
395 pj_scan_get(scanner, &pjsip_TEL_NUMBER_SPEC, &uri->number);
396 uri->number = pj_str_unescape(pool, &uri->number);
397#endif
398
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();
403
404 do {
405 /* Eat the ';' separator. */
406 pj_scan_get_char(scanner);
407
408 /* Get pname. */
409 pj_scan_get(scanner, &pc->pjsip_PARAM_CHAR_SPEC, &pname);
410
411 if (*scanner->curptr == '=') {
412 pj_scan_get_char(scanner);
413
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,
417 &pvalue);
418# else
419 pj_scan_get(scanner, &pjsip_TEL_PARSING_PVALUE_SPEC,
420 &pvalue);
421 pvalue = pj_str_unescape(pool, &pvalue);
422# endif
423
424 } else {
425 pvalue.slen = 0;
426 pvalue.ptr = NULL;
427 }
428
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;
436 } else {
437 pjsip_param *param = PJ_POOL_ALLOC_T(pool, pjsip_param);
438 param->name = pname;
439 param->value = pvalue;
440 pj_list_insert_before(&uri->other_param, param);
441 }
442
443 } while (*scanner->curptr==';');
444 }
445
446 scanner->skip_ws = skip_ws;
447 pj_scan_skip_whitespace(scanner);
448 return uri;
449}
450