Merge "funcs/func_devstate: Remove new line in Device field of during module load"
[asterisk/asterisk.git] / channels / sip / reqresp_parser.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * See http://www.asterisk.org for more information about
7  * the Asterisk project. Please do not directly contact
8  * any of the maintainers of this project for assistance;
9  * the project provides a web site, mailing lists and IRC
10  * channels for your use.
11  *
12  * This program is free software, distributed under the terms of
13  * the GNU General Public License Version 2. See the LICENSE file
14  * at the top of the source tree.
15  */
16
17 /*!
18  * \file
19  * \brief sip request parsing functions and unit tests
20  */
21
22 /*** MODULEINFO
23         <support_level>extended</support_level>
24  ***/
25
26 #include "asterisk.h"
27
28 #include "include/sip.h"
29 #include "include/sip_utils.h"
30 #include "include/reqresp_parser.h"
31
32 #ifdef HAVE_XLOCALE_H
33 locale_t c_locale;
34 #endif
35
36 /*! \brief * parses a URI in its components.*/
37 int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
38                    char **hostport, struct uriparams *params, char **headers,
39                    char **residue)
40 {
41         char *userinfo = NULL;
42         char *parameters = NULL;
43         char *endparams = NULL;
44         char *c = NULL;
45         int error = 0;
46         int teluri_scheme = 0;
47
48         /*
49          * Initialize requested strings - some functions don't care if parse_uri fails
50          * and will attempt to use string pointers passed into parse_uri even after a
51          * parse_uri failure
52          */
53         if (user) {
54                 *user = "";
55         }
56         if (pass) {
57                 *pass = "";
58         }
59         if (hostport) {
60                 *hostport = "";
61         }
62         if (headers) {
63                 *headers = "";
64         }
65         if (residue) {
66                 *residue = "";
67         }
68
69         /* check for valid input */
70         if (ast_strlen_zero(uri)) {
71                 return -1;
72         }
73
74         if (scheme) {
75                 int l;
76                 char *scheme2 = ast_strdupa(scheme);
77                 char *cur = strsep(&scheme2, ",");
78                 for (; !ast_strlen_zero(cur); cur = strsep(&scheme2, ",")) {
79                         l = strlen(cur);
80                         if (!strncasecmp(uri, cur, l)) {
81                                 teluri_scheme = !strncasecmp(uri, "tel:", 4);   /* TEL URI */
82                                 uri += l;
83                                 break;
84                         }
85                 }
86                 if (ast_strlen_zero(cur)) {
87                         ast_debug(1, "No supported scheme found in '%s' using the scheme[s] %s\n", uri, scheme);
88                         error = -1;
89                 }
90         }
91
92         if (!hostport) {
93                 /* if we don't want to split around hostport, keep everything as a
94                  * userinfo - cos thats how old parse_uri operated*/
95                 userinfo = uri;
96         } else if (teluri_scheme) {
97                 /*
98                  * tel: TEL URI INVITE RFC 3966 patch
99                  * See http://www.ietf.org/rfc/rfc3966.txt
100                  *
101                  * Once the full RFC 3966 parsing is implemented,
102                  * the ext= or isub= parameters would be extracted from userinfo.
103                  * When this kind of subaddressing would be implemented, the userinfo must be further parsed.
104                  * Those parameters would be used for ISDN or PSTN local extensions.
105                  *
106                  * Current restrictions:
107                  * We currently consider the ";isub=" or the ";ext=" as part of the userinfo (unparsed).
108                  */
109
110                 if ((c = strstr(uri, ";phone-context="))) {
111                         /*
112                          * Local number with context or domain.
113                          * ext= or isub= TEL URI parameters should be upfront.
114                          * All other parameters should come after the ";phone-context=" parameter.
115                          * If other parameters would occur before ";phone-context=" they will be ignored.
116                          */
117
118                         *c = '\0';
119                         userinfo = uri;
120                         uri = c + 15;
121                         *hostport = uri;
122                 } else if ('+' == uri[0]) {
123                         /* Global number without context or domain; possibly followed by RFC 3966 and optional other parameters. */
124
125                         userinfo = uri;
126                         *hostport = uri;
127                 } else {
128                         ast_debug(1, "No RFC 3966 global number or context found in '%s'; returning local number anyway\n", uri);
129                         userinfo = uri;         /* Return local number anyway */
130                         error = -1;
131                 }
132         } else {
133                 char *dom = "";
134                 if ((c = strchr(uri, '@'))) {
135                         *c++ = '\0';
136                         dom = c;
137                         userinfo = uri;
138                         uri = c; /* userinfo can contain ? and ; chars so step forward before looking for params and headers */
139                 } else {
140                         /* domain-only URI, according to the SIP RFC. */
141                         dom = uri;
142                         userinfo = "";
143                 }
144
145                 *hostport = dom;
146         }
147
148         if (pass && (c = strchr(userinfo, ':'))) {        /* user:password */
149                 *c++ = '\0';
150                 *pass = c;
151         } else if (pass) {
152                 *pass = "";
153         }
154
155         if (user) {
156                 *user = userinfo;
157         }
158
159         parameters = uri;
160         /* strip [?headers] from end of uri  - even if no header pointer exists*/
161         if ((c = strrchr(uri, '?'))) {
162                 *c++ = '\0';
163                 uri = c;
164                 if (headers) {
165                         *headers = c;
166                 }
167                 if ((c = strrchr(uri, ';'))) {
168                         *c++ = '\0';
169                 } else {
170                         c = strrchr(uri, '\0');
171                 }
172                 uri = c; /* residue */
173
174
175         } else if (headers) {
176                 *headers = "";
177         }
178
179         /* parse parameters */
180         endparams = strchr(parameters,'\0');
181         if ((c = strchr(parameters, ';'))) {
182                 *c++ = '\0';
183                 parameters = c;
184         } else {
185                 parameters = endparams;
186         }
187
188         if (params) {
189                 char *rem = parameters; /* unparsed or unrecognised remainder */
190                 char *label;
191                 char *value;
192                 int lr = 0;
193
194                 params->transport = "";
195                 params->user = "";
196                 params->method = "";
197                 params->ttl = "";
198                 params->maddr = "";
199                 params->lr = 0;
200
201                 rem = parameters;
202
203                 while ((value = strchr(parameters, '=')) || (lr = !strncmp(parameters, "lr", 2))) {
204                         /* The while condition will not continue evaluation to set lr if it matches "lr=" */
205                         if (lr) {
206                                 value = parameters;
207                         } else {
208                                 *value++ = '\0';
209                         }
210                         label = parameters;
211                         if ((c = strchr(value, ';'))) {
212                                 *c++ = '\0';
213                                 parameters = c;
214                         } else {
215                                 parameters = endparams;
216                         }
217
218                         if (!strcmp(label, "transport")) {
219                                 params->transport = value;
220                                 rem = parameters;
221                         } else if (!strcmp(label, "user")) {
222                                 params->user = value;
223                                 rem = parameters;
224                         } else if (!strcmp(label, "method")) {
225                                 params->method = value;
226                                 rem = parameters;
227                         } else if (!strcmp(label, "ttl")) {
228                                 params->ttl = value;
229                                 rem = parameters;
230                         } else if (!strcmp(label, "maddr")) {
231                                 params->maddr = value;
232                                 rem = parameters;
233                         /* Treat "lr", "lr=yes", "lr=on", "lr=1", "lr=almostanything" as lr enabled and "", "lr=no", "lr=off", "lr=0", "lr=" and "lranything" as lr disabled */
234                         } else if ((!strcmp(label, "lr") && strcmp(value, "no") && strcmp(value, "off") && strcmp(value, "0") && strcmp(value, "")) || ((lr) && strcmp(value, "lr"))) {
235                                 params->lr = 1;
236                                 rem = parameters;
237                         } else {
238                                 value--;
239                                 *value = '=';
240                                 if (c) {
241                                         c--;
242                                         *c = ';';
243                                 }
244                         }
245                 }
246                 if (rem > uri) { /* no headers */
247                         uri = rem;
248                 }
249
250         }
251
252         if (residue) {
253                 *residue = uri;
254         }
255
256         return error;
257 }
258
259 #ifdef TEST_FRAMEWORK
260 AST_TEST_DEFINE(sip_parse_uri_full_test)
261 {
262         int res = AST_TEST_PASS;
263         char uri[1024];
264         char *user, *pass, *hostport, *headers, *residue;
265         struct uriparams params;
266
267         struct testdata {
268                 char *desc;
269                 char *uri;
270                 char *user;
271                 char *pass;
272                 char *hostport;
273                 char *headers;
274                 char *residue;
275                 struct uriparams params;
276                 AST_LIST_ENTRY(testdata) list;
277         };
278
279
280         struct testdata *testdataptr;
281
282         static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
283
284         struct testdata td1 = {
285                 .desc = "no headers",
286                 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=residue",
287                 .user = "user",
288                 .pass = "secret",
289                 .hostport = "host:5060",
290                 .headers = "",
291                 .residue = "param2=residue",
292                 .params.transport = "tcp",
293                 .params.lr = 0,
294                 .params.user = ""
295         };
296
297         struct testdata td2 = {
298                 .desc = "with headers",
299                 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=discard2?header=blah&header2=blah2;param3=residue",
300                 .user = "user",
301                 .pass = "secret",
302                 .hostport = "host:5060",
303                 .headers = "header=blah&header2=blah2",
304                 .residue = "param3=residue",
305                 .params.transport = "tcp",
306                 .params.lr = 0,
307                 .params.user = ""
308         };
309
310         struct testdata td3 = {
311                 .desc = "difficult user",
312                 .uri = "sip:-_.!~*'()&=+$,;?/:secret@host:5060;transport=tcp",
313                 .user = "-_.!~*'()&=+$,;?/",
314                 .pass = "secret",
315                 .hostport = "host:5060",
316                 .headers = "",
317                 .residue = "",
318                 .params.transport = "tcp",
319                 .params.lr = 0,
320                 .params.user = ""
321         };
322
323         struct testdata td4 = {
324                 .desc = "difficult pass",
325                 .uri = "sip:user:-_.!~*'()&=+$,@host:5060;transport=tcp",
326                 .user = "user",
327                 .pass = "-_.!~*'()&=+$,",
328                 .hostport = "host:5060",
329                 .headers = "",
330                 .residue = "",
331                 .params.transport = "tcp",
332                 .params.lr = 0,
333                 .params.user = ""
334         };
335
336         struct testdata td5 = {
337                 .desc = "difficult host",
338                 .uri = "sip:user:secret@1-1.a-1.:5060;transport=tcp",
339                 .user = "user",
340                 .pass = "secret",
341                 .hostport = "1-1.a-1.:5060",
342                 .headers = "",
343                 .residue = "",
344                 .params.transport = "tcp",
345                 .params.lr = 0,
346                 .params.user = ""
347         };
348
349         struct testdata td6 = {
350                 .desc = "difficult params near transport",
351                 .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$;transport=tcp",
352                 .user = "user",
353                 .pass = "secret",
354                 .hostport = "host:5060",
355                 .headers = "",
356                 .residue = "",
357                 .params.transport = "tcp",
358                 .params.lr = 0,
359                 .params.user = ""
360         };
361
362         struct testdata td7 = {
363                 .desc = "difficult params near headers",
364                 .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$?header=blah&header2=blah2;-_.!~*'()[]/:&+$=residue",
365                 .user = "user",
366                 .pass = "secret",
367                 .hostport = "host:5060",
368                 .headers = "header=blah&header2=blah2",
369                 .residue = "-_.!~*'()[]/:&+$=residue",
370                 .params.transport = "",
371                 .params.lr = 0,
372                 .params.user = ""
373         };
374
375         struct testdata td8 = {
376                 .desc = "lr parameter",
377                 .uri = "sip:user:secret@host:5060;param=discard;lr?header=blah",
378                 .user = "user",
379                 .pass = "secret",
380                 .hostport = "host:5060",
381                 .headers = "header=blah",
382                 .residue = "",
383                 .params.transport = "",
384                 .params.lr = 1,
385                 .params.user = ""
386         };
387
388         struct testdata td9 = {
389                 .desc = "alternative lr parameter",
390                 .uri = "sip:user:secret@host:5060;param=discard;lr=yes?header=blah",
391                 .user = "user",
392                 .pass = "secret",
393                 .hostport = "host:5060",
394                 .headers = "header=blah",
395                 .residue = "",
396                 .params.transport = "",
397                 .params.lr = 1,
398                 .params.user = ""
399         };
400
401         struct testdata td10 = {
402                 .desc = "no lr parameter",
403                 .uri = "sip:user:secret@host:5060;paramlr=lr;lr=no;lr=off;lr=0;lr=;=lr;lrextra;lrparam2=lr?header=blah",
404                 .user = "user",
405                 .pass = "secret",
406                 .hostport = "host:5060",
407                 .headers = "header=blah",
408                 .residue = "",
409                 .params.transport = "",
410                 .params.lr = 0,
411                 .params.user = ""
412         };
413
414         /* RFC 3966 TEL URI INVITE */
415         struct testdata td11 = {
416                 .desc = "tel local number",
417                 .uri = "tel:0987654321;phone-context=+32987654321",
418                 .user = "0987654321",
419                 .pass = "",
420                 .hostport = "+32987654321",
421                 .headers = "",
422                 .residue = "",
423                 .params.transport = "",
424                 .params.lr = 0,
425                 .params.user = ""
426         };
427
428         struct testdata td12 = {
429                 .desc = "tel global number",
430                 .uri = "tel:+32987654321",
431                 .user = "+32987654321",
432                 .pass = "",
433                 .hostport = "+32987654321",
434                 .headers = "",
435                 .residue = "",
436                 .params.transport = "",
437                 .params.lr = 0,
438                 .params.user = ""
439         };
440
441         /*
442          * Once the full RFC 3966 parsing is implemented,
443          * only the ext= or isub= parameters would be extracted from .user
444          * Then the ;param=discard would be ignored,
445          * and the .user would only contain "0987654321"
446          */
447         struct testdata td13 = {
448                 .desc = "tel local number",
449                 .uri = "tel:0987654321;ext=1234;param=discard;phone-context=+32987654321;transport=udp;param2=discard2?header=blah&header2=blah2;param3=residue",
450                 .user = "0987654321;ext=1234;param=discard",
451                 .pass = "",
452                 .hostport = "+32987654321",
453                 .headers = "header=blah&header2=blah2",
454                 .residue = "param3=residue",
455                 .params.transport = "udp",
456                 .params.lr = 0,
457                 .params.user = ""
458         };
459
460         AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
461         AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
462         AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
463         AST_LIST_INSERT_TAIL(&testdatalist, &td4, list);
464         AST_LIST_INSERT_TAIL(&testdatalist, &td5, list);
465         AST_LIST_INSERT_TAIL(&testdatalist, &td6, list);
466         AST_LIST_INSERT_TAIL(&testdatalist, &td7, list);
467         AST_LIST_INSERT_TAIL(&testdatalist, &td8, list);
468         AST_LIST_INSERT_TAIL(&testdatalist, &td9, list);
469         AST_LIST_INSERT_TAIL(&testdatalist, &td10, list);
470         AST_LIST_INSERT_TAIL(&testdatalist, &td11, list);
471         AST_LIST_INSERT_TAIL(&testdatalist, &td12, list);
472         AST_LIST_INSERT_TAIL(&testdatalist, &td13, list);
473
474         switch (cmd) {
475         case TEST_INIT:
476                 info->name = "sip_uri_full_parse_test";
477                 info->category = "/channels/chan_sip/";
478                 info->summary = "tests sip full uri parsing";
479                 info->description =
480                         "Tests full parsing of various URIs "
481                         "Verifies output matches expected behavior.";
482                 return AST_TEST_NOT_RUN;
483         case TEST_EXECUTE:
484                 break;
485         }
486
487         AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
488                 user = pass = hostport = headers = residue = NULL;
489                 params.transport = params.user = params.method = params.ttl = params.maddr = NULL;
490                 params.lr = 0;
491
492                 ast_copy_string(uri,testdataptr->uri,sizeof(uri));
493                 if (parse_uri_full(uri, "sip:,sips:,tel:", &user,
494                                    &pass, &hostport,
495                                    &params,
496                                    &headers,
497                                    &residue) ||
498                         (user && strcmp(testdataptr->user, user)) ||
499                         (pass && strcmp(testdataptr->pass, pass)) ||
500                         (hostport && strcmp(testdataptr->hostport, hostport)) ||
501                         (headers && strcmp(testdataptr->headers, headers)) ||
502                         (residue && strcmp(testdataptr->residue, residue)) ||
503                         (strcmp(testdataptr->params.transport,params.transport)) ||
504                         (testdataptr->params.lr != params.lr) ||
505                         (strcmp(testdataptr->params.user,params.user))
506                 ) {
507                                 ast_test_status_update(test, "Sub-Test: %s, failed.\n", testdataptr->desc);
508                                 res = AST_TEST_FAIL;
509                 }
510         }
511
512
513         return res;
514 }
515 #endif
516
517 int parse_uri(char *uri, const char *scheme, char **user, char **pass,
518               char **hostport, char **transport) {
519         int ret;
520         char *headers;
521         struct uriparams params;
522
523         headers = NULL;
524         ret = parse_uri_full(uri, scheme, user, pass, hostport, &params, &headers, NULL);
525         if (transport) {
526                 *transport=params.transport;
527         }
528         return ret;
529 }
530
531 #ifdef TEST_FRAMEWORK
532 AST_TEST_DEFINE(sip_parse_uri_test)
533 {
534         int res = AST_TEST_PASS;
535         char *name, *pass, *hostport, *transport;
536         char uri1[] = "sip:name@host";
537         char uri2[] = "sip:name@host;transport=tcp";
538         char uri3[] = "sip:name:secret@host;transport=tcp";
539         char uri4[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
540         /* test 5 is for NULL input */
541         char uri6[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
542         char uri7[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
543         char uri8[] = "sip:host";
544         char uri9[] = "sip:host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
545         char uri10[] = "host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
546         char uri11[] = "host";
547         char uri12[] = "tel:911";       /* TEL URI Local number without context or global number */
548
549         switch (cmd) {
550         case TEST_INIT:
551                 info->name = "sip_uri_parse_test";
552                 info->category = "/channels/chan_sip/";
553                 info->summary = "tests sip uri parsing";
554                 info->description =
555                                                         "Tests parsing of various URIs "
556                                                         "Verifies output matches expected behavior.";
557                 return AST_TEST_NOT_RUN;
558         case TEST_EXECUTE:
559                 break;
560         }
561
562         /* Test 1, simple URI */
563         name = pass = hostport = transport = NULL;
564         if (parse_uri(uri1, "sip:,sips:", &name, &pass, &hostport, &transport) ||
565                         strcmp(name, "name")        ||
566                         !ast_strlen_zero(pass)      ||
567                         strcmp(hostport, "host")      ||
568                         !ast_strlen_zero(transport)) {
569                 ast_test_status_update(test, "Test 1: simple uri failed. \n");
570                 res = AST_TEST_FAIL;
571         }
572
573         /* Test 2, add tcp transport */
574         name = pass = hostport = transport = NULL;
575         if (parse_uri(uri2, "sip:,sips:", &name, &pass, &hostport, &transport) ||
576                         strcmp(name, "name")        ||
577                         !ast_strlen_zero(pass)      ||
578                         strcmp(hostport, "host")    ||
579                         strcmp(transport, "tcp")) {
580                 ast_test_status_update(test, "Test 2: uri with addtion of tcp transport failed. \n");
581                 res = AST_TEST_FAIL;
582         }
583
584         /* Test 3, add secret */
585         name = pass = hostport = transport = NULL;
586         if (parse_uri(uri3, "sip:,sips:", &name, &pass, &hostport, &transport) ||
587                         strcmp(name, "name")        ||
588                         strcmp(pass, "secret")      ||
589                         strcmp(hostport, "host")    ||
590                         strcmp(transport, "tcp")) {
591                 ast_test_status_update(test, "Test 3: uri with addition of secret failed.\n");
592                 res = AST_TEST_FAIL;
593         }
594
595         /* Test 4, add port and unparsed header field*/
596         name = pass = hostport = transport = NULL;
597         if (parse_uri(uri4, "sip:,sips:", &name, &pass, &hostport, &transport) ||
598                         strcmp(name, "name")        ||
599                         strcmp(pass, "secret")      ||
600                         strcmp(hostport, "host:port") ||
601                         strcmp(transport, "tcp")) {
602                 ast_test_status_update(test, "Test 4: add port and unparsed header field failed.\n");
603                 res = AST_TEST_FAIL;
604         }
605
606         /* Test 5, verify parse_uri does not crash when given a NULL uri */
607         name = pass = hostport = transport = NULL;
608         if (!parse_uri(NULL, "sip:,sips:", &name, &pass, &hostport, &transport)) {
609                 ast_test_status_update(test, "Test 5: passing a NULL uri failed.\n");
610                 res = AST_TEST_FAIL;
611         }
612
613         /* Test 6, verify parse_uri does not crash when given a NULL output parameters */
614         name = pass = hostport = transport = NULL;
615         if (parse_uri(uri6, "sip:,sips:", NULL, NULL, NULL, NULL)) {
616                 ast_test_status_update(test, "Test 6: passing NULL output parameters failed.\n");
617                 res = AST_TEST_FAIL;
618         }
619
620         /* Test 7, verify parse_uri returns user:secret and hostport when no port or secret output parameters are supplied. */
621         name = pass = hostport = transport = NULL;
622         if (parse_uri(uri7, "sip:,sips:", &name, NULL, &hostport, NULL) ||
623                         strcmp(name, "name:secret")        ||
624                         strcmp(hostport, "host:port")) {
625
626                 ast_test_status_update(test, "Test 7: providing no port and secret output parameters failed.\n");
627                 res = AST_TEST_FAIL;
628         }
629
630         /* Test 8, verify parse_uri can handle a hostport only uri */
631         name = pass = hostport = transport = NULL;
632         if (parse_uri(uri8, "sip:,sips:", &name, &pass, &hostport, &transport) ||
633                         strcmp(hostport, "host") ||
634                         !ast_strlen_zero(name)) {
635                 ast_test_status_update(test, "Test 8: add port and unparsed header field failed.\n");
636                 res = AST_TEST_FAIL;
637         }
638
639         /* Test 9, add port and unparsed header field with hostport only uri*/
640         name = pass = hostport = transport = NULL;
641         if (parse_uri(uri9, "sip:,sips:", &name, &pass, &hostport, &transport) ||
642                         !ast_strlen_zero(name)        ||
643                         !ast_strlen_zero(pass)      ||
644                         strcmp(hostport, "host:port")    ||
645                         strcmp(transport, "tcp")) {
646                 ast_test_status_update(test, "Test 9: hostport only uri failed \n");
647                 res = AST_TEST_FAIL;
648         }
649
650         /* Test 10, handle invalid/missing "sip:,sips:" scheme
651          * we expect parse_uri to return an error, but still parse
652          * the results correctly here */
653         name = pass = hostport = transport = NULL;
654         if (!parse_uri(uri10, "sip:,sips:", &name, &pass, &hostport, &transport) ||
655                         !ast_strlen_zero(name)        ||
656                         !ast_strlen_zero(pass)      ||
657                         strcmp(hostport, "host:port")    ||
658                         strcmp(transport, "tcp")) {
659                 ast_test_status_update(test, "Test 10: missing \"sip:sips:\" scheme failed\n");
660                 res = AST_TEST_FAIL;
661         }
662
663         /* Test 11, simple hostport only URI with missing scheme
664          * we expect parse_uri to return an error, but still parse
665          * the results correctly here */
666         name = pass = hostport = transport = NULL;
667         if (!parse_uri(uri11, "sip:,sips:", &name, &pass, &hostport, &transport) ||
668                         !ast_strlen_zero(name)      ||
669                         !ast_strlen_zero(pass)      ||
670                         strcmp(hostport, "host")      ||
671                         !ast_strlen_zero(transport)) {
672                 ast_test_status_update(test, "Test 11: simple uri with missing scheme failed. \n");
673                 res = AST_TEST_FAIL;
674         }
675
676         /* Test 12, simple URI */
677         name = pass = hostport = transport = NULL;
678         if (!parse_uri(uri12, "sip:,sips:,tel:", &name, &pass, &hostport, &transport) ||
679                         strcmp(name, "911")      ||     /* We return local number anyway */
680                         !ast_strlen_zero(pass)      ||
681                         !ast_strlen_zero(hostport)      ||      /* No global number nor context */
682                         !ast_strlen_zero(transport)) {
683                 ast_test_status_update(test, "Test 12: TEL URI INVITE failed.\n");
684                 res = AST_TEST_FAIL;
685         }
686
687         return res;
688 }
689 #endif
690
691 /*! \brief  Get caller id name from SIP headers, copy into output buffer
692  *
693  *  \retval input string pointer placed after display-name field if possible
694  */
695 const char *get_calleridname(const char *input, char *output, size_t outputsize)
696 {
697         /* From RFC3261:
698          *
699          * From           =  ( "From" / "f" ) HCOLON from-spec
700          * from-spec      =  ( name-addr / addr-spec ) *( SEMI from-param )
701          * name-addr      =  [ display-name ] LAQUOT addr-spec RAQUOT
702          * display-name   =  *(token LWS)/ quoted-string
703          * token          =  1*(alphanum / "-" / "." / "!" / "%" / "*"
704          *                     / "_" / "+" / "`" / "'" / "~" )
705          * quoted-string  =  SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE
706          * qdtext         =  LWS / %x21 / %x23-5B / %x5D-7E
707          *                     / UTF8-NONASCII
708          * quoted-pair    =  "\" (%x00-09 / %x0B-0C / %x0E-7F)
709          *
710          * HCOLON         = *WSP ":" SWS
711          * SWS            = [LWS]
712          * LWS            = *[*WSP CRLF] 1*WSP
713          * WSP            = (SP / HTAB)
714          *
715          * Deviations from it:
716          * - following CRLF's in LWS is not done (here at least)
717          * - ascii NUL is never legal as it terminates the C-string
718          * - utf8-nonascii is not checked for validity
719          */
720         char *orig_output = output;
721         const char *orig_input = input;
722
723         if (!output || !outputsize) {
724                 /* Bad output parameters.  Should never happen. */
725                 return input;
726         }
727
728         /* clear any empty characters in the beginning */
729         input = ast_skip_blanks(input);
730
731         /* make sure the output buffer is initilized */
732         *orig_output = '\0';
733
734         /* make room for '\0' at the end of the output buffer */
735         --outputsize;
736
737         /* no data at all or no display name? */
738         if (!input || *input == '<') {
739                 return input;
740         }
741
742         /* quoted-string rules */
743         if (input[0] == '"') {
744                 input++; /* skip the first " */
745
746                 for (; *input; ++input) {
747                         if (*input == '"') {  /* end of quoted-string */
748                                 break;
749                         } else if (*input == 0x5c) { /* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) */
750                                 ++input;
751                                 if (!*input) {
752                                         break;
753                                 }
754                                 if ((unsigned char) *input > 0x7f || *input == 0xa || *input == 0xd) {
755                                         continue;  /* not a valid quoted-pair, so skip it */
756                                 }
757                         } else if ((*input != 0x9 && (unsigned char) *input < 0x20)
758                                 || *input == 0x7f) {
759                                 continue; /* skip this invalid character. */
760                         }
761
762                         if (0 < outputsize) {
763                                 /* We still have room for the output display-name. */
764                                 *output++ = *input;
765                                 --outputsize;
766                         }
767                 }
768
769                 /* if this is successful, input should be at the ending quote */
770                 if (*input != '"') {
771                         ast_log(LOG_WARNING, "No ending quote for display-name was found\n");
772                         *orig_output = '\0';
773                         return orig_input;
774                 }
775
776                 /* make sure input is past the last quote */
777                 ++input;
778
779                 /* terminate output */
780                 *output = '\0';
781         } else {  /* either an addr-spec or tokenLWS-combo */
782                 for (; *input; ++input) {
783                         /* token or WSP (without LWS) */
784                         if ((*input >= '0' && *input <= '9') || (*input >= 'A' && *input <= 'Z')
785                                 || (*input >= 'a' && *input <= 'z') || *input == '-' || *input == '.'
786                                 || *input == '!' || *input == '%' || *input == '*' || *input == '_'
787                                 || *input == '+' || *input == '`' || *input == '\'' || *input == '~'
788                                 || *input == 0x9 || *input == ' ') {
789                                 if (0 < outputsize) {
790                                         /* We still have room for the output display-name. */
791                                         *output++ = *input;
792                                         --outputsize;
793                                 }
794                         } else if (*input == '<') {   /* end of tokenLWS-combo */
795                                 /* we could assert that the previous char is LWS, but we don't care */
796                                 break;
797                         } else if (*input == ':') {
798                                 /* This invalid character which indicates this is addr-spec rather than display-name. */
799                                 *orig_output = '\0';
800                                 return orig_input;
801                         } else {         /* else, invalid character we can skip. */
802                                 continue;    /* skip this character */
803                         }
804                 }
805
806                 if (*input != '<') {   /* if we never found the start of addr-spec then this is invalid */
807                         *orig_output = '\0';
808                         return orig_input;
809                 }
810
811                 /* terminate output while trimming any trailing whitespace */
812                 do {
813                         *output-- = '\0';
814                 } while (orig_output <= output && (*output == 0x9 || *output == ' '));
815         }
816
817         return input;
818 }
819
820 #ifdef TEST_FRAMEWORK
821 AST_TEST_DEFINE(get_calleridname_test)
822 {
823         int res = AST_TEST_PASS;
824         const char *in1 = " \" quoted-text internal \\\" quote \"<stuff>";
825         const char *in2 = " token text with no quotes <stuff>";
826         const char *overflow1 = " \"quoted-text overflow 1234567890123456789012345678901234567890\" <stuff>";
827         const char *overflow2 = " non-quoted text overflow 1234567890123456789012345678901234567890 <stuff>";
828         const char *noendquote = " \"quoted-text no end <stuff>";
829         const char *addrspec = " sip:blah@blah";
830         const char *no_quotes_no_brackets = "blah@blah";
831         const char *after_dname;
832         char dname[40];
833
834         switch (cmd) {
835         case TEST_INIT:
836                 info->name = "sip_get_calleridname_test";
837                 info->category = "/channels/chan_sip/";
838                 info->summary = "decodes callerid name from sip header";
839                 info->description = "Decodes display-name field of sip header.  Checks for valid output and expected failure cases.";
840                 return AST_TEST_NOT_RUN;
841         case TEST_EXECUTE:
842                 break;
843         }
844
845         /* quoted-text with backslash escaped quote */
846         after_dname = get_calleridname(in1, dname, sizeof(dname));
847         ast_test_status_update(test, "display-name1: %s\nafter: %s\n", dname, after_dname);
848         if (strcmp(dname, " quoted-text internal \" quote ")) {
849                 ast_test_status_update(test, "display-name1 test failed\n");
850                 res = AST_TEST_FAIL;
851         }
852
853         /* token text */
854         after_dname = get_calleridname(in2, dname, sizeof(dname));
855         ast_test_status_update(test, "display-name2: %s\nafter: %s\n", dname, after_dname);
856         if (strcmp(dname, "token text with no quotes")) {
857                 ast_test_status_update(test, "display-name2 test failed\n");
858                 res = AST_TEST_FAIL;
859         }
860
861         /* quoted-text buffer overflow */
862         after_dname = get_calleridname(overflow1, dname, sizeof(dname));
863         ast_test_status_update(test, "overflow display-name1: %s\nafter: %s\n", dname, after_dname);
864         if (strcmp(dname, "quoted-text overflow 123456789012345678")) {
865                 ast_test_status_update(test, "overflow display-name1 test failed\n");
866                 res = AST_TEST_FAIL;
867         }
868
869         /* non-quoted-text buffer overflow */
870         after_dname = get_calleridname(overflow2, dname, sizeof(dname));
871         ast_test_status_update(test, "overflow display-name2: %s\nafter: %s\n", dname, after_dname);
872         if (strcmp(dname, "non-quoted text overflow 12345678901234")) {
873                 ast_test_status_update(test, "overflow display-name2 test failed\n");
874                 res = AST_TEST_FAIL;
875         }
876
877         /* quoted-text buffer with no terminating end quote */
878         after_dname = get_calleridname(noendquote, dname, sizeof(dname));
879         ast_test_status_update(test, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname);
880         if (*dname != '\0' && after_dname != noendquote) {
881                 ast_test_status_update(test, "no end quote for quoted-text display-name failed\n");
882                 res = AST_TEST_FAIL;
883         }
884
885         /* addr-spec rather than display-name. */
886         after_dname = get_calleridname(addrspec, dname, sizeof(dname));
887         ast_test_status_update(test, "addr-spec display-name1: %s\nafter: %s\n", dname, after_dname);
888         if (*dname != '\0' && after_dname != addrspec) {
889                 ast_test_status_update(test, "detection of addr-spec failed\n");
890                 res = AST_TEST_FAIL;
891         }
892
893         /* no quotes, no brackets */
894         after_dname = get_calleridname(no_quotes_no_brackets, dname, sizeof(dname));
895         ast_test_status_update(test, "no_quotes_no_brackets display-name1: %s\nafter: %s\n", dname, after_dname);
896         if (*dname != '\0' && after_dname != no_quotes_no_brackets) {
897                 ast_test_status_update(test, "detection of addr-spec failed\n");
898                 res = AST_TEST_FAIL;
899         }
900
901         return res;
902 }
903 #endif
904
905 int get_name_and_number(const char *hdr, char **name, char **number)
906 {
907         char header[256];
908         char tmp_name[256];
909         char *tmp_number = NULL;
910         char *hostport = NULL;
911         char *dummy = NULL;
912
913         if (!name || !number || ast_strlen_zero(hdr)) {
914                 return -1;
915         }
916
917         *number = NULL;
918         *name = NULL;
919         ast_copy_string(header, hdr, sizeof(header));
920
921         /* strip the display-name portion off the beginning of the header. */
922         get_calleridname(header, tmp_name, sizeof(tmp_name));
923
924         /* get uri within < > brackets */
925         tmp_number = get_in_brackets(header);
926
927         /* parse out the number here */
928         if (parse_uri(tmp_number, "sip:,sips:", &tmp_number, &dummy, &hostport, NULL) || ast_strlen_zero(tmp_number)) {
929                 ast_log(LOG_ERROR, "can not parse name and number from sip header.\n");
930                 return -1;
931         }
932
933         /* number is not option, and must be present at this point */
934         *number = ast_strdup(tmp_number);
935         ast_uri_decode(*number, ast_uri_sip_user);
936
937         /* name is optional and may not be present at this point */
938         if (!ast_strlen_zero(tmp_name)) {
939                 *name = ast_strdup(tmp_name);
940         }
941
942         return 0;
943 }
944
945 #ifdef TEST_FRAMEWORK
946 AST_TEST_DEFINE(get_name_and_number_test)
947 {
948         int res = AST_TEST_PASS;
949         char *name = NULL;
950         char *number = NULL;
951         const char *in1 = "NAME <sip:NUMBER@place>";
952         const char *in2 = "\"NA><ME\" <sip:NUMBER@place>";
953         const char *in3 = "NAME";
954         const char *in4 = "<sip:NUMBER@place>";
955         const char *in5 = "This is a screwed up string <sip:LOLCLOWNS<sip:>@place>";
956
957         switch (cmd) {
958         case TEST_INIT:
959                 info->name = "sip_get_name_and_number_test";
960                 info->category = "/channels/chan_sip/";
961                 info->summary = "Tests getting name and number from sip header";
962                 info->description =
963                                 "Runs through various test situations in which a name and "
964                                 "and number can be retrieved from a sip header.";
965                 return AST_TEST_NOT_RUN;
966         case TEST_EXECUTE:
967                 break;
968         }
969
970         /* Test 1. get name and number */
971         number = name = NULL;
972         if ((get_name_and_number(in1, &name, &number)) ||
973                 strcmp(name, "NAME") ||
974                 strcmp(number, "NUMBER")) {
975
976                 ast_test_status_update(test, "Test 1, simple get name and number failed.\n");
977                 res = AST_TEST_FAIL;
978         }
979         ast_free(name);
980         ast_free(number);
981
982         /* Test 2. get quoted name and number */
983         number = name = NULL;
984         if ((get_name_and_number(in2, &name, &number)) ||
985                 strcmp(name, "NA><ME") ||
986                 strcmp(number, "NUMBER")) {
987
988                 ast_test_status_update(test, "Test 2, get quoted name and number failed.\n");
989                 res = AST_TEST_FAIL;
990         }
991         ast_free(name);
992         ast_free(number);
993
994         /* Test 3. name only */
995         number = name = NULL;
996         if (!(get_name_and_number(in3, &name, &number))) {
997
998                 ast_test_status_update(test, "Test 3, get name only was expected to fail but did not.\n");
999                 res = AST_TEST_FAIL;
1000         }
1001         ast_free(name);
1002         ast_free(number);
1003
1004         /* Test 4. number only */
1005         number = name = NULL;
1006         if ((get_name_and_number(in4, &name, &number)) ||
1007                 !ast_strlen_zero(name) ||
1008                 strcmp(number, "NUMBER")) {
1009
1010                 ast_test_status_update(test, "Test 4, get number with no name present failed.\n");
1011                 res = AST_TEST_FAIL;
1012         }
1013         ast_free(name);
1014         ast_free(number);
1015
1016         /* Test 5. malformed string, since number can not be parsed, this should return an error.  */
1017         number = name = NULL;
1018         if (!(get_name_and_number(in5, &name, &number)) ||
1019                 !ast_strlen_zero(name) ||
1020                 !ast_strlen_zero(number)) {
1021
1022                 ast_test_status_update(test, "Test 5, processing malformed string failed.\n");
1023                 res = AST_TEST_FAIL;
1024         }
1025         ast_free(name);
1026         ast_free(number);
1027
1028         /* Test 6. NULL output parameters */
1029         number = name = NULL;
1030         if (!(get_name_and_number(in5, NULL, NULL))) {
1031
1032                 ast_test_status_update(test, "Test 6, NULL output parameters failed.\n");
1033                 res = AST_TEST_FAIL;
1034         }
1035
1036         /* Test 7. NULL input parameter */
1037         number = name = NULL;
1038         if (!(get_name_and_number(NULL, &name, &number)) ||
1039                 !ast_strlen_zero(name) ||
1040                 !ast_strlen_zero(number)) {
1041
1042                 ast_test_status_update(test, "Test 7, NULL input parameter failed.\n");
1043                 res = AST_TEST_FAIL;
1044         }
1045         ast_free(name);
1046         ast_free(number);
1047
1048         return res;
1049 }
1050 #endif
1051
1052 int get_in_brackets_const(const char *src,const char **start,int *length)
1053 {
1054         const char *parse = src;
1055         const char *first_bracket;
1056         const char *second_bracket;
1057
1058         if (start == NULL) {
1059                 return -1;
1060         }
1061         if (length == NULL) {
1062                 return -1;
1063         }
1064         *start = NULL;
1065         *length = -1;
1066         if (ast_strlen_zero(src)) {
1067                 return 1;
1068         }
1069
1070         /*
1071          * Skip any quoted text until we find the part in brackets.
1072          * On any error give up and return -1
1073          */
1074         while ( (first_bracket = strchr(parse, '<')) ) {
1075                 const char *first_quote = strchr(parse, '"');
1076                 first_bracket++;
1077                 if (!first_quote || first_quote >= first_bracket) {
1078                         break; /* no need to look at quoted part */
1079                 }
1080                 /* the bracket is within quotes, so ignore it */
1081                 parse = find_closing_quote(first_quote + 1, NULL);
1082                 if (!*parse) {
1083                         ast_log(LOG_WARNING, "No closing quote found in '%s'\n", src);
1084                         return  -1;
1085                 }
1086                 parse++;
1087         }
1088
1089         /* Require a first bracket.  Unlike get_in_brackets_full, this procedure is passed a const,
1090          * so it can expect a pointer to an original value */
1091         if (!first_bracket) {
1092                 ast_log(LOG_WARNING, "No opening bracket found in '%s'\n", src);
1093                 return 1;
1094         }
1095
1096         if ((second_bracket = strchr(first_bracket, '>'))) {
1097                 *start = first_bracket;
1098                 *length = second_bracket - first_bracket;
1099                 return 0;
1100         }
1101         ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", src);
1102         return -1;
1103 }
1104
1105 int get_in_brackets_full(char *tmp,char **out,char **residue)
1106 {
1107         const char *parse = tmp;
1108         char *first_bracket;
1109         char *second_bracket;
1110
1111         if (out) {
1112                 *out = "";
1113         }
1114         if (residue) {
1115                 *residue = "";
1116         }
1117
1118         if (ast_strlen_zero(tmp)) {
1119                 return 1;
1120         }
1121
1122         /*
1123          * Skip any quoted text until we find the part in brackets.
1124         * On any error give up and return -1
1125         */
1126         while ( (first_bracket = strchr(parse, '<')) ) {
1127                 char *first_quote = strchr(parse, '"');
1128                 first_bracket++;
1129                 if (!first_quote || first_quote >= first_bracket) {
1130                         break; /* no need to look at quoted part */
1131                 }
1132                 /* the bracket is within quotes, so ignore it */
1133                 parse = find_closing_quote(first_quote + 1, NULL);
1134                 if (!*parse) {
1135                         ast_log(LOG_WARNING, "No closing quote found in '%s'\n", tmp);
1136                         return  -1;
1137                 }
1138                 parse++;
1139         }
1140
1141         /* If no first bracket then still look for a second bracket as some other parsing functions
1142         may overwrite first bracket with NULL when terminating a token based display-name. As this
1143         only affects token based display-names there is no danger of brackets being in quotes */
1144         if (first_bracket) {
1145                 parse = first_bracket;
1146         } else {
1147                 parse = tmp;
1148         }
1149
1150         if ((second_bracket = strchr(parse, '>'))) {
1151                 *second_bracket++ = '\0';
1152                 if (out) {
1153                         *out = (char *) parse;
1154                 }
1155                 if (residue) {
1156                         *residue = second_bracket;
1157                 }
1158                 return 0;
1159         }
1160
1161         if ((first_bracket)) {
1162                 ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
1163                 return -1;
1164         }
1165
1166         if (out) {
1167                 *out = tmp;
1168         }
1169
1170         return 1;
1171 }
1172
1173 char *get_in_brackets(char *tmp)
1174 {
1175         char *out;
1176
1177         if ((get_in_brackets_full(tmp, &out, NULL))) {
1178                 return tmp;
1179         }
1180         return out;
1181 }
1182
1183 #ifdef TEST_FRAMEWORK
1184 AST_TEST_DEFINE(get_in_brackets_test)
1185 {
1186         int res = AST_TEST_PASS;
1187         char in_brackets[] = "sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah";
1188         char no_name[] = "<sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1189         char quoted_string[] = "\"I'm a quote stri><ng\" <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1190         char missing_end_quote[] = "\"I'm a quote string <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1191         char name_no_quotes[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1192         char no_end_bracket[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah";
1193         char no_name_no_brackets[] = "sip:name@host";
1194         char missing_start_bracket[] = "sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1195         char *uri = NULL;
1196
1197         switch (cmd) {
1198         case TEST_INIT:
1199                 info->name = "sip_get_in_brackets_test";
1200                 info->category = "/channels/chan_sip/";
1201                 info->summary = "Tests getting a sip uri in <> brackets within a sip header.";
1202                 info->description =
1203                                 "Runs through various test situations in which a sip uri "
1204                                 "in angle brackets needs to be retrieved";
1205                 return AST_TEST_NOT_RUN;
1206         case TEST_EXECUTE:
1207                 break;
1208         }
1209
1210         /* Test 1, simple get in brackets */
1211         if (!(uri = get_in_brackets(no_name)) || strcmp(uri, in_brackets)) {
1212                 ast_test_status_update(test, "Test 1, simple get in brackets failed. %s\n", uri);
1213                 res = AST_TEST_FAIL;
1214         }
1215
1216         /* Test 2, starts with quoted string */
1217         if (!(uri = get_in_brackets(quoted_string)) || strcmp(uri, in_brackets)) {
1218                 ast_test_status_update(test, "Test 2, get in brackets with quoted string in front failed. %s\n", uri);
1219                 res = AST_TEST_FAIL;
1220         }
1221
1222         /* Test 3, missing end quote */
1223         if (!(uri = get_in_brackets(missing_end_quote)) || !strcmp(uri, in_brackets)) {
1224                 ast_test_status_update(test, "Test 3, missing end quote failed. %s\n", uri);
1225                 res = AST_TEST_FAIL;
1226         }
1227
1228         /* Test 4, starts with a name not in quotes */
1229         if (!(uri = get_in_brackets(name_no_quotes)) || strcmp(uri, in_brackets)) {
1230                 ast_test_status_update(test, "Test 4, passing name not in quotes failed. %s\n", uri);
1231                 res = AST_TEST_FAIL;
1232         }
1233
1234         /* Test 5, no end bracket, should just return everything after the first '<'  */
1235         if (!(uri = get_in_brackets(no_end_bracket)) || !strcmp(uri, in_brackets)) {
1236                 ast_test_status_update(test, "Test 5, no end bracket failed. %s\n", uri);
1237                 res = AST_TEST_FAIL;
1238         }
1239
1240         /* Test 6, NULL input  */
1241         if (get_in_brackets(NULL)) {
1242                 ast_test_status_update(test, "Test 6, NULL input failed.\n");
1243                 res = AST_TEST_FAIL;
1244         }
1245
1246         /* Test 7, no name, and no brackets. */
1247         if (!(uri = get_in_brackets(no_name_no_brackets)) || strcmp(uri, "sip:name@host")) {
1248                 ast_test_status_update(test, "Test 7 failed. %s\n", uri);
1249                 res = AST_TEST_FAIL;
1250         }
1251
1252         /* Test 8, no start bracket, but with ending bracket. */
1253         if (!(uri = get_in_brackets(missing_start_bracket)) || strcmp(uri, in_brackets)) {
1254                 ast_test_status_update(test, "Test 8 failed. %s\n", uri);
1255                 res = AST_TEST_FAIL;
1256         }
1257
1258         return res;
1259 }
1260 #endif
1261
1262 int parse_name_andor_addr(char *uri, const char *scheme, char **name,
1263                           char **user, char **pass, char **hostport,
1264                           struct uriparams *params, char **headers,
1265                           char **residue)
1266 {
1267         char buf[1024];
1268         char **residue2 = residue;
1269         char *orig_uri = uri;
1270         int ret;
1271
1272         buf[0] = '\0';
1273         if (name) {
1274                 uri = (char *) get_calleridname(uri, buf, sizeof(buf));
1275         }
1276         ret = get_in_brackets_full(uri, &uri, residue);
1277         if (ret == 0) {
1278                 /*
1279                  * The uri is in brackets so do not treat unknown trailing uri
1280                  * parameters as potential message header parameters.
1281                  */
1282                 if (residue && **residue) {
1283                         /* step over the first semicolon as per parse_uri_full residue */
1284                         *residue = *residue + 1;
1285                 }
1286                 residue2 = NULL;
1287         }
1288
1289         if (name) {
1290                 if (buf[0]) {
1291                         /*
1292                          * There is always room at orig_uri for the display-name because
1293                          * at least one character has always been removed.  A '"' or '<'
1294                          * has been removed.
1295                          */
1296                         strcpy(orig_uri, buf);
1297                         *name = orig_uri;
1298                 } else {
1299                         *name = "";
1300                 }
1301         }
1302
1303         return parse_uri_full(uri, scheme, user, pass, hostport, params, headers, residue2);
1304 }
1305
1306 #ifdef TEST_FRAMEWORK
1307 AST_TEST_DEFINE(parse_name_andor_addr_test)
1308 {
1309         int res = AST_TEST_PASS;
1310         char uri[1024];
1311         char *name, *user, *pass, *hostport, *headers, *residue;
1312         struct uriparams params;
1313
1314         struct testdata {
1315                 char *desc;
1316                 char *uri;
1317                 char *name;
1318                 char *user;
1319                 char *pass;
1320                 char *hostport;
1321                 char *headers;
1322                 char *residue;
1323                 struct uriparams params;
1324                 AST_LIST_ENTRY(testdata) list;
1325         };
1326
1327         struct testdata *testdataptr;
1328
1329         static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
1330
1331         struct testdata td1 = {
1332                 .desc = "quotes and brackets",
1333                 .uri = "\"name :@ \" <sip:user:secret@host:5060;param=discard;transport=tcp>;tag=tag",
1334                 .name =  "name :@ ",
1335                 .user = "user",
1336                 .pass = "secret",
1337                 .hostport = "host:5060",
1338                 .headers = "",
1339                 .residue = "tag=tag",
1340                 .params.transport = "tcp",
1341                 .params.lr = 0,
1342                 .params.user = ""
1343         };
1344
1345         struct testdata td2 = {
1346                 .desc = "no quotes",
1347                 .uri = "givenname familyname <sip:user:secret@host:5060;param=discard;transport=tcp>;expires=3600",
1348                 .name = "givenname familyname",
1349                 .user = "user",
1350                 .pass = "secret",
1351                 .hostport = "host:5060",
1352                 .headers = "",
1353                 .residue = "expires=3600",
1354                 .params.transport = "tcp",
1355                 .params.lr = 0,
1356                 .params.user = ""
1357         };
1358
1359         struct testdata td3 = {
1360                 .desc = "no brackets",
1361                 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;q=1",
1362                 .name = "",
1363                 .user = "user",
1364                 .pass = "secret",
1365                 .hostport = "host:5060",
1366                 .headers = "",
1367                 .residue = "q=1",
1368                 .params.transport = "tcp",
1369                 .params.lr = 0,
1370                 .params.user = ""
1371         };
1372
1373         struct testdata td4 = {
1374                 .desc = "just host",
1375                 .uri = "sips:host",
1376                 .name = "",
1377                 .user = "",
1378                 .pass = "",
1379                 .hostport = "host",
1380                 .headers = "",
1381                 .residue = "",
1382                 .params.transport = "",
1383                 .params.lr = 0,
1384                 .params.user = ""
1385         };
1386
1387
1388         AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
1389         AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
1390         AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
1391         AST_LIST_INSERT_TAIL(&testdatalist, &td4, list);
1392
1393
1394         switch (cmd) {
1395         case TEST_INIT:
1396                 info->name = "parse_name_andor_addr_test";
1397                 info->category = "/channels/chan_sip/";
1398                 info->summary = "tests parsing of name_andor_addr abnf structure";
1399                 info->description =
1400                         "Tests parsing of abnf name-andor-addr = name-addr / addr-spec "
1401                         "Verifies output matches expected behavior.";
1402                 return AST_TEST_NOT_RUN;
1403         case TEST_EXECUTE:
1404                 break;
1405         }
1406
1407         AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
1408                 name = user = pass = hostport = headers = residue = NULL;
1409                 params.transport = params.user = params.method = params.ttl = params.maddr = NULL;
1410                 params.lr = 0;
1411                 ast_copy_string(uri,testdataptr->uri,sizeof(uri));
1412                 if (parse_name_andor_addr(uri, "sip:,sips:",
1413                                           &name,
1414                                           &user,
1415                                           &pass,
1416                                           &hostport,
1417                                           &params,
1418                                           &headers,
1419                                           &residue) ||
1420                         (name && strcmp(testdataptr->name, name)) ||
1421                         (user && strcmp(testdataptr->user, user)) ||
1422                         (pass && strcmp(testdataptr->pass, pass)) ||
1423                         (hostport && strcmp(testdataptr->hostport, hostport)) ||
1424                         (headers && strcmp(testdataptr->headers, headers)) ||
1425                         (residue && strcmp(testdataptr->residue, residue)) ||
1426                         (strcmp(testdataptr->params.transport,params.transport)) ||
1427                         (strcmp(testdataptr->params.user,params.user))
1428                         ) {
1429                         ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1430                         res = AST_TEST_FAIL;
1431                 }
1432         }
1433
1434         return res;
1435 }
1436 #endif
1437
1438 int get_comma(char *in, char **out)
1439 {
1440         char *c;
1441         char *parse = in;
1442         if (out) {
1443                 *out = in;
1444         }
1445
1446         /* Skip any quoted text */
1447         while (*parse) {
1448                 if ((c = strchr(parse, '"'))) {
1449                         in = (char *)find_closing_quote((const char *)c + 1, NULL);
1450                         if (!*in) {
1451                                 ast_log(LOG_WARNING, "No closing quote found in '%s'\n", c);
1452                                 return -1;
1453                         } else {
1454                                 break;
1455                         }
1456                 } else {
1457                         break;
1458                 }
1459                 parse++;
1460         }
1461         parse = in;
1462
1463         /* Skip any userinfo components of a uri as they may contain commas */
1464         if ((c = strchr(parse,'@'))) {
1465                 parse = c+1;
1466         }
1467         if ((out) && (c = strchr(parse,','))) {
1468                 *c++ = '\0';
1469                 *out = c;
1470                 return 0;
1471         }
1472         return 1;
1473 }
1474
1475 int parse_contact_header(char *contactheader, struct contactliststruct *contactlist)
1476 {
1477         int res;
1478         int last;
1479         char *comma;
1480         char *residue;
1481         char *param;
1482         char *value;
1483         struct contact *split_contact = NULL;
1484
1485         if (*contactheader == '*') {
1486                 return 1;
1487         }
1488
1489         split_contact = ast_calloc(1, sizeof(*split_contact));
1490
1491         AST_LIST_HEAD_SET_NOLOCK(contactlist, split_contact);
1492         while ((last = get_comma(contactheader, &comma)) != -1) {
1493                 res = parse_name_andor_addr(contactheader, "sip:,sips:",
1494                         &split_contact->name, &split_contact->user,
1495                         &split_contact->pass, &split_contact->hostport,
1496                         &split_contact->params, &split_contact->headers,
1497                         &residue);
1498                 if (res == -1) {
1499                         return res;
1500                 }
1501
1502                 /* parse contact params */
1503                 split_contact->expires = split_contact->q = "";
1504
1505                 while ((value = strchr(residue,'='))) {
1506                         *value++ = '\0';
1507
1508                         param = residue;
1509                         if ((residue = strchr(value,';'))) {
1510                                 *residue++ = '\0';
1511                         } else {
1512                                 residue = "";
1513                         }
1514
1515                         if (!strcmp(param,"expires")) {
1516                                 split_contact->expires = value;
1517                         } else if (!strcmp(param,"q")) {
1518                                 split_contact->q = value;
1519                         }
1520                 }
1521
1522                 if (last) {
1523                         return 0;
1524                 }
1525                 contactheader = comma;
1526
1527                 split_contact = ast_calloc(1, sizeof(*split_contact));
1528                 AST_LIST_INSERT_TAIL(contactlist, split_contact, list);
1529         }
1530         return last;
1531 }
1532
1533 #ifdef TEST_FRAMEWORK
1534 AST_TEST_DEFINE(parse_contact_header_test)
1535 {
1536         int res = AST_TEST_PASS;
1537         char contactheader[1024];
1538         int star;
1539         struct contactliststruct contactlist;
1540         struct contactliststruct *contactlistptr=&contactlist;
1541
1542         struct testdata {
1543                 char *desc;
1544                 char *contactheader;
1545                 int star;
1546                 struct contactliststruct *contactlist;
1547
1548                 AST_LIST_ENTRY(testdata) list;
1549         };
1550
1551         struct testdata *testdataptr;
1552         struct contact *tdcontactptr;
1553         struct contact *contactptr;
1554
1555         static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
1556         struct contactliststruct contactlist1, contactlist2;
1557
1558         struct testdata td1 = {
1559                 .desc = "single contact",
1560                 .contactheader = "\"name :@;?&,\" <sip:user:secret@host:5082;param=discard;transport=tcp>;expires=3600",
1561                 .contactlist = &contactlist1,
1562                 .star = 0
1563         };
1564         struct contact contact11 = {
1565                 .name = "name :@;?&,",
1566                 .user = "user",
1567                 .pass = "secret",
1568                 .hostport = "host:5082",
1569                 .params.transport = "tcp",
1570                 .params.ttl = "",
1571                 .params.lr = 0,
1572                 .headers = "",
1573                 .expires = "3600",
1574                 .q = ""
1575         };
1576
1577         struct testdata td2 = {
1578                 .desc = "multiple contacts",
1579                 .contactheader = "sip:,user1,:,secret1,@host1;ttl=7;q=1;expires=3600,sips:host2",
1580                 .contactlist = &contactlist2,
1581                 .star = 0,
1582         };
1583         struct contact contact21 = {
1584                 .name = "",
1585                 .user = ",user1,",
1586                 .pass = ",secret1,",
1587                 .hostport = "host1",
1588                 .params.transport = "",
1589                 .params.ttl = "7",
1590                 .params.lr = 0,
1591                 .headers = "",
1592                 .expires = "3600",
1593                 .q = "1"
1594         };
1595         struct contact contact22 = {
1596                 .name = "",
1597                 .user = "",
1598                 .pass = "",
1599                 .hostport = "host2",
1600                 .params.transport = "",
1601                 .params.ttl = "",
1602                 .params.lr = 0,
1603                 .headers = "",
1604                 .expires = "",
1605                 .q = ""
1606         };
1607
1608         struct testdata td3 = {
1609                 .desc = "star - all contacts",
1610                 .contactheader = "*",
1611                 .star = 1,
1612                 .contactlist = NULL
1613         };
1614
1615         AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
1616         AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
1617         AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
1618
1619         AST_LIST_HEAD_SET_NOLOCK(&contactlist1, &contact11);
1620
1621         AST_LIST_HEAD_SET_NOLOCK(&contactlist2, &contact21);
1622         AST_LIST_INSERT_TAIL(&contactlist2, &contact22, list);
1623
1624
1625         switch (cmd) {
1626         case TEST_INIT:
1627                 info->name = "parse_contact_header_test";
1628                 info->category = "/channels/chan_sip/";
1629                 info->summary = "tests parsing of sip contact header";
1630                 info->description =
1631                         "Tests parsing of a contact header including those with multiple contacts "
1632                         "Verifies output matches expected behavior.";
1633                 return AST_TEST_NOT_RUN;
1634         case TEST_EXECUTE:
1635                 break;
1636         }
1637
1638         AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
1639                 ast_copy_string(contactheader,testdataptr->contactheader,sizeof(contactheader));
1640                 star = parse_contact_header(contactheader,contactlistptr);
1641                 if (testdataptr->star) {
1642                         /* expecting star rather than list of contacts */
1643                         if (!star) {
1644                                 ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1645                                 res = AST_TEST_FAIL;
1646                                 break;
1647                         }
1648                 } else {
1649                         contactptr = AST_LIST_FIRST(contactlistptr);
1650                         AST_LIST_TRAVERSE(testdataptr->contactlist, tdcontactptr, list) {
1651                                 if (!contactptr ||
1652                                         strcmp(tdcontactptr->name, contactptr->name) ||
1653                                         strcmp(tdcontactptr->user, contactptr->user) ||
1654                                         strcmp(tdcontactptr->pass, contactptr->pass) ||
1655                                         strcmp(tdcontactptr->hostport, contactptr->hostport) ||
1656                                         strcmp(tdcontactptr->headers, contactptr->headers) ||
1657                                         strcmp(tdcontactptr->expires, contactptr->expires) ||
1658                                         strcmp(tdcontactptr->q, contactptr->q) ||
1659                                         strcmp(tdcontactptr->params.transport, contactptr->params.transport) ||
1660                                         strcmp(tdcontactptr->params.ttl, contactptr->params.ttl) ||
1661                                         (tdcontactptr->params.lr != contactptr->params.lr)
1662                                         ) {
1663                                         ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1664                                         res = AST_TEST_FAIL;
1665                                         break;
1666                                 }
1667
1668                                 contactptr = AST_LIST_NEXT(contactptr,list);
1669                         }
1670
1671                         while ((contactptr = AST_LIST_REMOVE_HEAD(contactlistptr,list))) {
1672                                 ast_free(contactptr);
1673                         }
1674                 }
1675         }
1676
1677         return res;
1678 }
1679 #endif
1680
1681 /*!
1682  * \brief Parse supported header in incoming packet
1683  *
1684  * \details This function parses through the options parameters and
1685  * builds a bit field representing all the SIP options in that field. When an
1686  * item is found that is not supported, it is copied to the unsupported
1687  * out buffer.
1688  *
1689  * \param options list
1690  * \param unsupported out buffer (optional)
1691  * \param unsupported_len out buffer length (optional)
1692  */
1693 unsigned int parse_sip_options(const char *options, char *unsupported, size_t unsupported_len)
1694 {
1695         char *next, *sep;
1696         char *temp;
1697         int i, found, supported;
1698         unsigned int profile = 0;
1699
1700         char *out = unsupported;
1701         size_t outlen = unsupported_len;
1702         char *cur_out = out;
1703
1704         if (ast_strlen_zero(options) )
1705                 return 0;
1706
1707         temp = ast_strdupa(options);
1708
1709         ast_debug(3, "Begin: parsing SIP \"Supported: %s\"\n", options);
1710         for (next = temp; next; next = sep) {
1711                 found = FALSE;
1712                 supported = FALSE;
1713                 if ((sep = strchr(next, ',')) != NULL) {
1714                         *sep++ = '\0';
1715                 }
1716
1717                 /* trim leading and trailing whitespace */
1718                 next = ast_strip(next);
1719
1720                 if (ast_strlen_zero(next)) {
1721                         continue; /* if there is a blank argument in there just skip it */
1722                 }
1723
1724                 ast_debug(3, "Found SIP option: -%s-\n", next);
1725                 for (i = 0; i < ARRAY_LEN(sip_options); i++) {
1726                         if (!strcasecmp(next, sip_options[i].text)) {
1727                                 profile |= sip_options[i].id;
1728                                 if (sip_options[i].supported == SUPPORTED) {
1729                                         supported = TRUE;
1730                                 }
1731                                 found = TRUE;
1732                                 ast_debug(3, "Matched SIP option: %s\n", next);
1733                                 break;
1734                         }
1735                 }
1736
1737                 /* If option is not supported, add to unsupported out buffer */
1738                 if (!supported && out && outlen) {
1739                         size_t copylen = strlen(next);
1740                         size_t cur_outlen = strlen(out);
1741                         /* Check to see if there is enough room to store this option.
1742                          * Copy length is string length plus 2 for the ',' and '\0' */
1743                         if ((cur_outlen + copylen + 2) < outlen) {
1744                                 /* if this isn't the first item, add the ',' */
1745                                 if (cur_outlen) {
1746                                         *cur_out = ',';
1747                                         cur_out++;
1748                                         cur_outlen++;
1749                                 }
1750                                 ast_copy_string(cur_out, next, (outlen - cur_outlen));
1751                                 cur_out += copylen;
1752                         }
1753                 }
1754
1755                 if (!found) {
1756                         profile |= SIP_OPT_UNKNOWN;
1757                         if (!strncasecmp(next, "x-", 2))
1758                                 ast_debug(3, "Found private SIP option, not supported: %s\n", next);
1759                         else
1760                                 ast_debug(3, "Found no match for SIP option: %s (Please file bug report!)\n", next);
1761                 }
1762         }
1763
1764         return profile;
1765 }
1766
1767 #ifdef TEST_FRAMEWORK
1768 AST_TEST_DEFINE(sip_parse_options_test)
1769 {
1770         int res = AST_TEST_PASS;
1771         char unsupported[64];
1772         unsigned int option_profile = 0;
1773         struct testdata {
1774                 char *name;
1775                 char *input_options;
1776                 char *expected_unsupported;
1777                 unsigned int expected_profile;
1778                 AST_LIST_ENTRY(testdata) list;
1779         };
1780
1781         struct testdata *testdataptr;
1782         static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
1783
1784         struct testdata test1 = {
1785                 .name = "test_all_unsupported",
1786                 .input_options = "unsupported1,,, ,unsupported2,unsupported3,unsupported4",
1787                 .expected_unsupported = "unsupported1,unsupported2,unsupported3,unsupported4",
1788                 .expected_profile = SIP_OPT_UNKNOWN,
1789         };
1790         struct testdata test2 = {
1791                 .name = "test_all_unsupported_one_supported",
1792                 .input_options = "  unsupported1, replaces,   unsupported3  , , , ,unsupported4",
1793                 .expected_unsupported = "unsupported1,unsupported3,unsupported4",
1794                 .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES
1795         };
1796         struct testdata test3 = {
1797                 .name = "test_two_supported_two_unsupported",
1798                 .input_options = ",,  timer  ,replaces     ,unsupported3,unsupported4",
1799                 .expected_unsupported = "unsupported3,unsupported4",
1800                 .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER,
1801         };
1802
1803         struct testdata test4 = {
1804                 .name = "test_all_supported",
1805                 .input_options = "timer,replaces",
1806                 .expected_unsupported = "",
1807                 .expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER,
1808         };
1809
1810         struct testdata test5 = {
1811                 .name = "test_all_supported_redundant",
1812                 .input_options = "timer,replaces,timer,replace,timer,replaces",
1813                 .expected_unsupported = "",
1814                 .expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER,
1815         };
1816         struct testdata test6 = {
1817                 .name = "test_buffer_overflow",
1818                 .input_options = "unsupported1,replaces,timer,unsupported4,unsupported_huge____"
1819                 "____________________________________,__________________________________________"
1820                 "________________________________________________",
1821                 .expected_unsupported = "unsupported1,unsupported4",
1822                 .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER,
1823         };
1824         struct testdata test7 = {
1825                 .name = "test_null_input",
1826                 .input_options = NULL,
1827                 .expected_unsupported = "",
1828                 .expected_profile = 0,
1829         };
1830         struct testdata test8 = {
1831                 .name = "test_whitespace_input",
1832                 .input_options = "         ",
1833                 .expected_unsupported = "",
1834                 .expected_profile = 0,
1835         };
1836         struct testdata test9 = {
1837                 .name = "test_whitespace_plus_option_input",
1838                 .input_options = " , , ,timer , ,  , ,        ,    ",
1839                 .expected_unsupported = "",
1840                 .expected_profile = SIP_OPT_TIMER,
1841         };
1842
1843         switch (cmd) {
1844         case TEST_INIT:
1845                 info->name = "sip_parse_options_test";
1846                 info->category = "/channels/chan_sip/";
1847                 info->summary = "Tests parsing of sip options";
1848                 info->description =
1849                                                         "Tests parsing of SIP options from supported and required "
1850                                                         "header fields.  Verifies when unsupported options are encountered "
1851                                                         "that they are appended to the unsupported out buffer and that the "
1852                                                         "correct bit field representnig the option profile is returned.";
1853                 return AST_TEST_NOT_RUN;
1854         case TEST_EXECUTE:
1855                 break;
1856         }
1857
1858         AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &test1);
1859         AST_LIST_INSERT_TAIL(&testdatalist, &test2, list);
1860         AST_LIST_INSERT_TAIL(&testdatalist, &test3, list);
1861         AST_LIST_INSERT_TAIL(&testdatalist, &test4, list);
1862         AST_LIST_INSERT_TAIL(&testdatalist, &test5, list);
1863         AST_LIST_INSERT_TAIL(&testdatalist, &test6, list);
1864         AST_LIST_INSERT_TAIL(&testdatalist, &test7, list);
1865         AST_LIST_INSERT_TAIL(&testdatalist, &test8, list);
1866         AST_LIST_INSERT_TAIL(&testdatalist, &test9, list);
1867
1868         /* Test with unsupported char buffer */
1869         AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
1870                 memset(unsupported, 0, sizeof(unsupported));
1871                 option_profile = parse_sip_options(testdataptr->input_options, unsupported, ARRAY_LEN(unsupported));
1872                 if (option_profile != testdataptr->expected_profile ||
1873                         strcmp(unsupported, testdataptr->expected_unsupported)) {
1874                         ast_test_status_update(test, "Test with output buffer \"%s\", expected unsupported: %s actual unsupported:"
1875                                 "%s expected bit profile: %x actual bit profile: %x\n",
1876                                 testdataptr->name,
1877                                 testdataptr->expected_unsupported,
1878                                 unsupported,
1879                                 testdataptr->expected_profile,
1880                                 option_profile);
1881                         res = AST_TEST_FAIL;
1882                 } else {
1883                         ast_test_status_update(test, "\"%s\" passed got expected unsupported: %s and bit profile: %x\n",
1884                                 testdataptr->name,
1885                                 unsupported,
1886                                 option_profile);
1887                 }
1888
1889                 option_profile = parse_sip_options(testdataptr->input_options, NULL, 0);
1890                 if (option_profile != testdataptr->expected_profile) {
1891                         ast_test_status_update(test, "NULL output test \"%s\", expected bit profile: %x actual bit profile: %x\n",
1892                                 testdataptr->name,
1893                                 testdataptr->expected_profile,
1894                                 option_profile);
1895                         res = AST_TEST_FAIL;
1896                 } else {
1897                         ast_test_status_update(test, "\"%s\" with NULL output buf passed, bit profile: %x\n",
1898                                 testdataptr->name,
1899                                 option_profile);
1900                 }
1901         }
1902
1903         return res;
1904 }
1905 #endif
1906
1907 /*! \brief helper routine for sip_uri_cmp to compare URI parameters
1908  *
1909  * This takes the parameters from two SIP URIs and determines
1910  * if the URIs match. The rules for parameters *suck*. Here's a breakdown
1911  * 1. If a parameter appears in both URIs, then they must have the same value
1912  *    in order for the URIs to match
1913  * 2. If one URI has a user, maddr, ttl, or method parameter, then the other
1914  *    URI must also have that parameter and must have the same value
1915  *    in order for the URIs to match
1916  * 3. All other headers appearing in only one URI are not considered when
1917  *    determining if URIs match
1918  *
1919  * \param input1 Parameters from URI 1
1920  * \param input2 Parameters from URI 2
1921  * \retval 0 URIs' parameters match
1922  * \retval nonzero URIs' parameters do not match
1923  */
1924 static int sip_uri_params_cmp(const char *input1, const char *input2)
1925 {
1926         char *params1 = NULL;
1927         char *params2 = NULL;
1928         char *pos1;
1929         char *pos2;
1930         int zerolength1 = 0;
1931         int zerolength2 = 0;
1932         int maddrmatch = 0;
1933         int ttlmatch = 0;
1934         int usermatch = 0;
1935         int methodmatch = 0;
1936
1937         if (ast_strlen_zero(input1)) {
1938                 zerolength1 = 1;
1939         } else {
1940                 params1 = ast_strdupa(input1);
1941         }
1942         if (ast_strlen_zero(input2)) {
1943                 zerolength2 = 1;
1944         } else {
1945                 params2 = ast_strdupa(input2);
1946         }
1947
1948         /* Quick optimization. If both params are zero-length, then
1949          * they match
1950          */
1951         if (zerolength1 && zerolength2) {
1952                 return 0;
1953         }
1954
1955         for (pos1 = strsep(&params1, ";"); pos1; pos1 = strsep(&params1, ";")) {
1956                 char *value1 = pos1;
1957                 char *name1 = strsep(&value1, "=");
1958                 char *params2dup = NULL;
1959                 int matched = 0;
1960                 if (!value1) {
1961                         value1 = "";
1962                 }
1963                 /* Checkpoint reached. We have the name and value parsed for param1
1964                  * We have to duplicate params2 each time through this loop
1965                  * or else the inner loop below will not work properly.
1966                  */
1967                 if (!zerolength2) {
1968                         params2dup = ast_strdupa(params2);
1969                 }
1970                 for (pos2 = strsep(&params2dup, ";"); pos2; pos2 = strsep(&params2dup, ";")) {
1971                         char *name2 = pos2;
1972                         char *value2 = strchr(pos2, '=');
1973                         if (!value2) {
1974                                 value2 = "";
1975                         } else {
1976                                 *value2++ = '\0';
1977                         }
1978                         if (!strcasecmp(name1, name2)) {
1979                                 if (strcasecmp(value1, value2)) {
1980                                         goto fail;
1981                                 } else {
1982                                         matched = 1;
1983                                         break;
1984                                 }
1985                         }
1986                 }
1987                 /* Check to see if the parameter is one of the 'must-match' parameters */
1988                 if (!strcasecmp(name1, "maddr")) {
1989                         if (matched) {
1990                                 maddrmatch = 1;
1991                         } else {
1992                                 goto fail;
1993                         }
1994                 } else if (!strcasecmp(name1, "ttl")) {
1995                         if (matched) {
1996                                 ttlmatch = 1;
1997                         } else {
1998                                 goto fail;
1999                         }
2000                 } else if (!strcasecmp(name1, "user")) {
2001                         if (matched) {
2002                                 usermatch = 1;
2003                         } else {
2004                                 goto fail;
2005                         }
2006                 } else if (!strcasecmp(name1, "method")) {
2007                         if (matched) {
2008                                 methodmatch = 1;
2009                         } else {
2010                                 goto fail;
2011                         }
2012                 }
2013         }
2014
2015         /* We've made it out of that horrible O(m*n) construct and there are no
2016          * failures yet. We're not done yet, though, because params2 could have
2017          * an maddr, ttl, user, or method header and params1 did not.
2018          */
2019         for (pos2 = strsep(&params2, ";"); pos2; pos2 = strsep(&params2, ";")) {
2020                 char *value2 = pos2;
2021                 char *name2 = strsep(&value2, "=");
2022                 if (!value2) {
2023                         value2 = "";
2024                 }
2025                 if ((!strcasecmp(name2, "maddr") && !maddrmatch) ||
2026                                 (!strcasecmp(name2, "ttl") && !ttlmatch) ||
2027                                 (!strcasecmp(name2, "user") && !usermatch) ||
2028                                 (!strcasecmp(name2, "method") && !methodmatch)) {
2029                         goto fail;
2030                 }
2031         }
2032         return 0;
2033
2034 fail:
2035         return 1;
2036 }
2037
2038 /*! \brief helper routine for sip_uri_cmp to compare URI headers
2039  *
2040  * This takes the headers from two SIP URIs and determines
2041  * if the URIs match. The rules for headers is simple. If a header
2042  * appears in one URI, then it must also appear in the other URI. The
2043  * order in which the headers appear does not matter.
2044  *
2045  * \param input1 Headers from URI 1
2046  * \param input2 Headers from URI 2
2047  * \retval 0 URI headers match
2048  * \retval nonzero URI headers do not match
2049  */
2050 static int sip_uri_headers_cmp(const char *input1, const char *input2)
2051 {
2052         char *headers1 = NULL;
2053         char *headers2 = NULL;
2054         int zerolength1 = 0;
2055         int zerolength2 = 0;
2056         int different = 0;
2057         char *header1;
2058
2059         if (ast_strlen_zero(input1)) {
2060                 zerolength1 = 1;
2061         } else {
2062                 headers1 = ast_strdupa(input1);
2063         }
2064
2065         if (ast_strlen_zero(input2)) {
2066                 zerolength2 = 1;
2067         } else {
2068                 headers2 = ast_strdupa(input2);
2069         }
2070
2071         /* If one URI contains no headers and the other
2072          * does, then they cannot possibly match
2073          */
2074         if (zerolength1 != zerolength2) {
2075                 return 1;
2076         }
2077
2078         if (zerolength1 && zerolength2)
2079                 return 0;
2080
2081         /* At this point, we can definitively state that both inputs are
2082          * not zero-length. First, one more optimization. If the length
2083          * of the headers is not equal, then we definitely have no match
2084          */
2085         if (strlen(headers1) != strlen(headers2)) {
2086                 return 1;
2087         }
2088
2089         for (header1 = strsep(&headers1, "&"); header1; header1 = strsep(&headers1, "&")) {
2090                 if (!strcasestr(headers2, header1)) {
2091                         different = 1;
2092                         break;
2093                 }
2094         }
2095
2096         return different;
2097 }
2098
2099 /*!
2100  * \brief Compare domain sections of SIP URIs
2101  *
2102  * For hostnames, a case insensitive string comparison is
2103  * used. For IP addresses, a binary comparison is used. This
2104  * is mainly because IPv6 addresses have many ways of writing
2105  * the same address.
2106  *
2107  * For specifics about IP address comparison, see the following
2108  * document: http://tools.ietf.org/html/draft-ietf-sip-ipv6-abnf-fix-05
2109  *
2110  * \param host1 The domain from the first URI
2111  * \param host2 THe domain from the second URI
2112  * \retval 0 The domains match
2113  * \retval nonzero The domains do not match
2114  */
2115 static int sip_uri_domain_cmp(const char *host1, const char *host2)
2116 {
2117         struct ast_sockaddr addr1;
2118         struct ast_sockaddr addr2;
2119         int addr1_parsed;
2120         int addr2_parsed;
2121
2122         addr1_parsed = ast_sockaddr_parse(&addr1, host1, 0);
2123         addr2_parsed = ast_sockaddr_parse(&addr2, host2, 0);
2124
2125         if (addr1_parsed != addr2_parsed) {
2126                 /* One domain was an IP address and the other had
2127                  * a host name. FAIL!
2128                  */
2129                 return 1;
2130         }
2131
2132         /* Both are host names. A string comparison will work
2133          * perfectly here. Specifying the "C" locale ensures that
2134          * The LC_CTYPE conventions use those defined in ANSI C,
2135          * i.e. ASCII.
2136          */
2137         if (!addr1_parsed) {
2138 #ifdef HAVE_XLOCALE_H
2139                 if(!c_locale) {
2140                         return strcasecmp(host1, host2);
2141                 } else {
2142                         return strcasecmp_l(host1, host2, c_locale);
2143                 }
2144 #else
2145                 return strcasecmp(host1, host2);
2146 #endif
2147         }
2148
2149         /* Both contain IP addresses */
2150         return ast_sockaddr_cmp(&addr1, &addr2);
2151 }
2152
2153 int sip_uri_cmp(const char *input1, const char *input2)
2154 {
2155         char *uri1;
2156         char *uri2;
2157         char *uri_scheme1;
2158         char *uri_scheme2;
2159         char *host1;
2160         char *host2;
2161         char *params1;
2162         char *params2;
2163         char *headers1;
2164         char *headers2;
2165
2166         /* XXX It would be really nice if we could just use parse_uri_full() here
2167          * to separate the components of the URI, but unfortunately it is written
2168          * in a way that can cause URI parameters to be discarded.
2169          */
2170
2171         if (!input1 || !input2) {
2172                 return 1;
2173         }
2174
2175         uri1 = ast_strdupa(input1);
2176         uri2 = ast_strdupa(input2);
2177
2178         ast_uri_decode(uri1, ast_uri_sip_user);
2179         ast_uri_decode(uri2, ast_uri_sip_user);
2180
2181         uri_scheme1 = strsep(&uri1, ":");
2182         uri_scheme2 = strsep(&uri2, ":");
2183
2184         if (strcmp(uri_scheme1, uri_scheme2)) {
2185                 return 1;
2186         }
2187
2188         /* This function is tailored for SIP and SIPS URIs. There's no
2189          * need to check uri_scheme2 since we have determined uri_scheme1
2190          * and uri_scheme2 are equivalent already.
2191          */
2192         if (strcmp(uri_scheme1, "sip") && strcmp(uri_scheme1, "sips")) {
2193                 return 1;
2194         }
2195
2196         if (ast_strlen_zero(uri1) || ast_strlen_zero(uri2)) {
2197                 return 1;
2198         }
2199
2200         if ((host1 = strchr(uri1, '@'))) {
2201                 *host1++ = '\0';
2202         }
2203         if ((host2 = strchr(uri2, '@'))) {
2204                 *host2++ = '\0';
2205         }
2206
2207         /* Check for mismatched username and passwords. This is the
2208          * only case-sensitive comparison of a SIP URI
2209          */
2210         if ((host1 && !host2) ||
2211                         (host2 && !host1) ||
2212                         (host1 && host2 && strcmp(uri1, uri2))) {
2213                 return 1;
2214         }
2215
2216         if (!host1) {
2217                 host1 = uri1;
2218         }
2219         if (!host2) {
2220                 host2 = uri2;
2221         }
2222
2223         /* Strip off the parameters and headers so we can compare
2224          * host and port
2225          */
2226
2227         if ((params1 = strchr(host1, ';'))) {
2228                 *params1++ = '\0';
2229         }
2230         if ((params2 = strchr(host2, ';'))) {
2231                 *params2++ = '\0';
2232         }
2233
2234         /* Headers come after parameters, but there may be headers without
2235          * parameters, thus the S_OR
2236          */
2237         if ((headers1 = strchr(S_OR(params1, host1), '?'))) {
2238                 *headers1++ = '\0';
2239         }
2240         if ((headers2 = strchr(S_OR(params2, host2), '?'))) {
2241                 *headers2++ = '\0';
2242         }
2243
2244         if (sip_uri_domain_cmp(host1, host2)) {
2245                 return 1;
2246         }
2247
2248         /* Headers have easier rules to follow, so do those first */
2249         if (sip_uri_headers_cmp(headers1, headers2)) {
2250                 return 1;
2251         }
2252
2253         /* And now the parameters. Ugh */
2254         return sip_uri_params_cmp(params1, params2);
2255 }
2256
2257 #define URI_CMP_MATCH 0
2258 #define URI_CMP_NOMATCH 1
2259
2260 #ifdef TEST_FRAMEWORK
2261 AST_TEST_DEFINE(sip_uri_cmp_test)
2262 {
2263         static const struct {
2264                 const char *uri1;
2265                 const char *uri2;
2266                 int expected_result;
2267         } uri_cmp_tests [] = {
2268                 /* These are identical, so they match */
2269                 { "sip:bob@example.com", "sip:bob@example.com", URI_CMP_MATCH },
2270                 /* Different usernames. No match */
2271                 { "sip:alice@example.com", "sip:bob@example.com", URI_CMP_NOMATCH },
2272                 /* Different hosts. No match */
2273                 { "sip:bob@example.com", "sip:bob@examplez.com", URI_CMP_NOMATCH },
2274                 /* Now start using IP addresses. Identical, so they match */
2275                 { "sip:bob@1.2.3.4", "sip:bob@1.2.3.4", URI_CMP_MATCH },
2276                 /* Two identical IPv4 addresses represented differently. Match */
2277                 { "sip:bob@1.2.3.4", "sip:bob@001.002.003.004", URI_CMP_MATCH },
2278                 /* Logically equivalent IPv4 Address and hostname. No Match */
2279                 { "sip:bob@127.0.0.1", "sip:bob@localhost", URI_CMP_NOMATCH },
2280                 /* Logically equivalent IPv6 address and hostname. No Match */
2281                 { "sip:bob@[::1]", "sip:bob@localhost", URI_CMP_NOMATCH },
2282                 /* Try an IPv6 one as well */
2283                 { "sip:bob@[2001:db8::1234]", "sip:bob@[2001:db8::1234]", URI_CMP_MATCH },
2284                 /* Two identical IPv6 addresses represented differently. Match */
2285                 { "sip:bob@[2001:db8::1234]", "sip:bob@[2001:0db8::1234]", URI_CMP_MATCH },
2286                 /* Different ports. No match */
2287                 { "sip:bob@1.2.3.4:5060", "sip:bob@1.2.3.4:5061", URI_CMP_NOMATCH },
2288                 /* Same port logically, but only one address specifies it. No match */
2289                 { "sip:bob@1.2.3.4:5060", "sip:bob@1.2.3.4", URI_CMP_NOMATCH },
2290                 /* And for safety, try with IPv6 */
2291                 { "sip:bob@[2001:db8:1234]:5060", "sip:bob@[2001:db8:1234]", URI_CMP_NOMATCH },
2292                 /* User comparison is case sensitive. No match */
2293                 { "sip:bob@example.com", "sip:BOB@example.com", URI_CMP_NOMATCH },
2294                 /* Host comparison is case insensitive. Match */
2295                 { "sip:bob@example.com", "sip:bob@EXAMPLE.COM", URI_CMP_MATCH },
2296                 /* Add headers to the URI. Identical, so they match */
2297                 { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com?header1=value1&header2=value2", URI_CMP_MATCH },
2298                 /* Headers in URI 1 are not in URI 2. No Match */
2299                 { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com", URI_CMP_NOMATCH },
2300                 /* Header present in both URIs does not have matching values. No match */
2301                 { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com?header1=value1&header2=value3", URI_CMP_NOMATCH },
2302                 /* Add parameters to the URI. Identical so they match */
2303                 { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com;param1=value1;param2=value2", URI_CMP_MATCH },
2304                 /* Same parameters in both URIs but appear in different order. Match */
2305                 { "sip:bob@example.com;param2=value2;param1=value1", "sip:bob@example.com;param1=value1;param2=value2", URI_CMP_MATCH },
2306                 /* params in URI 1 are not in URI 2. Match */
2307                 { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com", URI_CMP_MATCH },
2308                 /* param present in both URIs does not have matching values. No match */
2309                 { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com;param1=value1;param2=value3", URI_CMP_NOMATCH },
2310                 /* URI 1 has a maddr param but URI 2 does not. No match */
2311                 { "sip:bob@example.com;param1=value1;maddr=192.168.0.1", "sip:bob@example.com;param1=value1", URI_CMP_NOMATCH },
2312                 /* URI 1 and URI 2 both have identical maddr params. Match */
2313                 { "sip:bob@example.com;param1=value1;maddr=192.168.0.1", "sip:bob@example.com;param1=value1;maddr=192.168.0.1", URI_CMP_MATCH },
2314                 /* URI 1 is a SIPS URI and URI 2 is a SIP URI. No Match */
2315                 { "sips:bob@example.com", "sip:bob@example.com", URI_CMP_NOMATCH },
2316                 /* No URI schemes. No match */
2317                 { "bob@example.com", "bob@example.com", URI_CMP_NOMATCH },
2318                 /* Crashiness tests. Just an address scheme. No match */
2319                 { "sip", "sips", URI_CMP_NOMATCH },
2320                 /* Still just an address scheme. Even though they're the same, No match */
2321                 { "sip", "sip", URI_CMP_NOMATCH },
2322                 /* Empty strings. No match */
2323                 { "", "", URI_CMP_NOMATCH },
2324                 /* An empty string and a NULL. No match */
2325                 { "", NULL, URI_CMP_NOMATCH },
2326         };
2327         int i;
2328         int test_res = AST_TEST_PASS;
2329         switch (cmd) {
2330         case TEST_INIT:
2331                 info->name = "sip_uri_cmp_test";
2332                 info->category = "/channels/chan_sip/";
2333                 info->summary = "Tests comparison of SIP URIs";
2334                 info->description = "Several would-be tricky URI comparisons are performed";
2335                 return AST_TEST_NOT_RUN;
2336         case TEST_EXECUTE:
2337                 break;
2338         }
2339
2340         for (i = 0; i < ARRAY_LEN(uri_cmp_tests); ++i) {
2341                 int cmp_res1;
2342                 int cmp_res2;
2343                 if ((cmp_res1 = sip_uri_cmp(uri_cmp_tests[i].uri1, uri_cmp_tests[i].uri2))) {
2344                         /* URI comparison may return -1 or +1 depending on the failure. Standardize
2345                          * the return value to be URI_CMP_NOMATCH on any failure
2346                          */
2347                         cmp_res1 = URI_CMP_NOMATCH;
2348                 }
2349                 if (cmp_res1 != uri_cmp_tests[i].expected_result) {
2350                         ast_test_status_update(test, "Unexpected comparison result for URIs %s and %s. "
2351                                         "Expected %s but got %s\n", uri_cmp_tests[i].uri1, uri_cmp_tests[i].uri2,
2352                                         uri_cmp_tests[i].expected_result == URI_CMP_MATCH ? "Match" : "No Match",
2353                                         cmp_res1 == URI_CMP_MATCH ? "Match" : "No Match");
2354                         test_res = AST_TEST_FAIL;
2355                 }
2356
2357                 /* All URI comparisons are commutative, so for the sake of being thorough, we'll
2358                  * rerun the comparison with the parameters reversed
2359                  */
2360                 if ((cmp_res2 = sip_uri_cmp(uri_cmp_tests[i].uri2, uri_cmp_tests[i].uri1))) {
2361                         /* URI comparison may return -1 or +1 depending on the failure. Standardize
2362                          * the return value to be URI_CMP_NOMATCH on any failure
2363                          */
2364                         cmp_res2 = URI_CMP_NOMATCH;
2365                 }
2366                 if (cmp_res2 != uri_cmp_tests[i].expected_result) {
2367                         ast_test_status_update(test, "Unexpected comparison result for URIs %s and %s. "
2368                                         "Expected %s but got %s\n", uri_cmp_tests[i].uri2, uri_cmp_tests[i].uri1,
2369                                         uri_cmp_tests[i].expected_result == URI_CMP_MATCH ? "Match" : "No Match",
2370                                         cmp_res2 == URI_CMP_MATCH ? "Match" : "No Match");
2371                         test_res = AST_TEST_FAIL;
2372                 }
2373         }
2374
2375         return test_res;
2376 }
2377 #endif
2378
2379 void free_via(struct sip_via *v)
2380 {
2381         if (!v) {
2382                 return;
2383         }
2384
2385         ast_free(v->via);
2386         ast_free(v);
2387 }
2388
2389 struct sip_via *parse_via(const char *header)
2390 {
2391         struct sip_via *v = ast_calloc(1, sizeof(*v));
2392         char *via, *parm;
2393
2394         if (!v) {
2395                 return NULL;
2396         }
2397
2398         v->via = ast_strdup(header);
2399         v->ttl = 1;
2400
2401         via = v->via;
2402
2403         if (ast_strlen_zero(via)) {
2404                 ast_log(LOG_ERROR, "received request without a Via header\n");
2405                 free_via(v);
2406                 return NULL;
2407         }
2408
2409         /* seperate the first via-parm */
2410         via = strsep(&via, ",");
2411
2412         /* chop off sent-protocol */
2413         v->protocol = strsep(&via, " \t\r\n");
2414         if (ast_strlen_zero(v->protocol)) {
2415                 ast_log(LOG_ERROR, "missing sent-protocol in Via header\n");
2416                 free_via(v);
2417                 return NULL;
2418         }
2419         v->protocol = ast_skip_blanks(v->protocol);
2420
2421         if (via) {
2422                 via = ast_skip_blanks(via);
2423         }
2424
2425         /* chop off sent-by */
2426         v->sent_by = strsep(&via, "; \t\r\n");
2427         if (ast_strlen_zero(v->sent_by)) {
2428                 ast_log(LOG_ERROR, "missing sent-by in Via header\n");
2429                 free_via(v);
2430                 return NULL;
2431         }
2432         v->sent_by = ast_skip_blanks(v->sent_by);
2433
2434         /* store the port, we have to handle ipv6 addresses containing ':'
2435          * characters gracefully */
2436         if (((parm = strchr(v->sent_by, ']')) && *(++parm) == ':') || (!(parm = strchr(v->sent_by, ']')) && (parm = strchr(v->sent_by, ':')))) {
2437                 char *endptr;
2438
2439                 v->port = strtol(++parm, &endptr, 10);
2440         }
2441
2442         /* evaluate any via-parms */
2443         while ((parm = strsep(&via, "; \t\r\n"))) {
2444                 char *c;
2445                 if ((c = strstr(parm, "maddr="))) {
2446                         v->maddr = ast_skip_blanks(c + sizeof("maddr=") - 1);
2447                 } else if ((c = strstr(parm, "branch="))) {
2448                         v->branch = ast_skip_blanks(c + sizeof("branch=") - 1);
2449                 } else if ((c = strstr(parm, "ttl="))) {
2450                         char *endptr;
2451                         c = ast_skip_blanks(c + sizeof("ttl=") - 1);
2452                         v->ttl = strtol(c, &endptr, 10);
2453
2454                         /* make sure we got a valid ttl value */
2455                         if (c == endptr) {
2456                                 v->ttl = 1;
2457                         }
2458                 }
2459         }
2460
2461         return v;
2462 }
2463
2464 #ifdef TEST_FRAMEWORK
2465 AST_TEST_DEFINE(parse_via_test)
2466 {
2467         int res = AST_TEST_PASS;
2468         int i = 1;
2469         struct sip_via *via;
2470         struct testdata {
2471                 char *in;
2472                 char *expected_protocol;
2473                 char *expected_branch;
2474                 char *expected_sent_by;
2475                 char *expected_maddr;
2476                 unsigned int expected_port;
2477                 unsigned char expected_ttl;
2478                 int expected_null;
2479                 AST_LIST_ENTRY(testdata) list;
2480         };
2481         struct testdata *testdataptr;
2482         static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
2483         struct testdata t1 = {
2484                 .in = "SIP/2.0/UDP host:port;branch=thebranch",
2485                 .expected_protocol = "SIP/2.0/UDP",
2486                 .expected_sent_by = "host:port",
2487                 .expected_branch = "thebranch",
2488         };
2489         struct testdata t2 = {
2490                 .in = "SIP/2.0/UDP host:port",
2491                 .expected_protocol = "SIP/2.0/UDP",
2492                 .expected_sent_by = "host:port",
2493                 .expected_branch = "",
2494         };
2495         struct testdata t3 = {
2496                 .in = "SIP/2.0/UDP",
2497                 .expected_null = 1,
2498         };
2499         struct testdata t4 = {
2500                 .in = "BLAH/BLAH/BLAH host:port;branch=",
2501                 .expected_protocol = "BLAH/BLAH/BLAH",
2502                 .expected_sent_by = "host:port",
2503                 .expected_branch = "",
2504         };
2505         struct testdata t5 = {
2506                 .in = "SIP/2.0/UDP host:5060;branch=thebranch;maddr=224.0.0.1;ttl=1",
2507                 .expected_protocol = "SIP/2.0/UDP",
2508                 .expected_sent_by = "host:5060",
2509                 .expected_port = 5060,
2510                 .expected_branch = "thebranch",
2511                 .expected_maddr = "224.0.0.1",
2512                 .expected_ttl = 1,
2513         };
2514         struct testdata t6 = {
2515                 .in = "SIP/2.0/UDP      host:5060;\n   branch=thebranch;\r\n  maddr=224.0.0.1;   ttl=1",
2516                 .expected_protocol = "SIP/2.0/UDP",
2517                 .expected_sent_by = "host:5060",
2518                 .expected_port = 5060,
2519                 .expected_branch = "thebranch",
2520                 .expected_maddr = "224.0.0.1",
2521                 .expected_ttl = 1,
2522         };
2523         struct testdata t7 = {
2524                 .in = "SIP/2.0/UDP [::1]:5060",
2525                 .expected_protocol = "SIP/2.0/UDP",
2526                 .expected_sent_by = "[::1]:5060",
2527                 .expected_port = 5060,
2528                 .expected_branch = "",
2529         };
2530         switch (cmd) {
2531         case TEST_INIT:
2532                 info->name = "parse_via_test";
2533                 info->category = "/channels/chan_sip/";
2534                 info->summary = "Tests parsing the Via header";
2535                 info->description =
2536                                 "Runs through various test situations in which various "
2537                                 " parameters parameter must be extracted from a VIA header";
2538                 return AST_TEST_NOT_RUN;
2539         case TEST_EXECUTE:
2540                 break;
2541         }
2542
2543         AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &t1);
2544         AST_LIST_INSERT_TAIL(&testdatalist, &t2, list);
2545         AST_LIST_INSERT_TAIL(&testdatalist, &t3, list);
2546         AST_LIST_INSERT_TAIL(&testdatalist, &t4, list);
2547         AST_LIST_INSERT_TAIL(&testdatalist, &t5, list);
2548         AST_LIST_INSERT_TAIL(&testdatalist, &t6, list);
2549         AST_LIST_INSERT_TAIL(&testdatalist, &t7, list);
2550
2551
2552         AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
2553                 via = parse_via(testdataptr->in);
2554                 if (!via) {
2555                         if (!testdataptr->expected_null) {
2556                                 ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2557                                         "failed to parse header\n",
2558                                 i, testdataptr->in);
2559                                 res = AST_TEST_FAIL;
2560                         }
2561                         i++;
2562                         continue;
2563                 }
2564
2565                 if (testdataptr->expected_null) {
2566                         ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2567                                 "successfully parased invalid via header\n",
2568                         i, testdataptr->in);
2569                         res = AST_TEST_FAIL;
2570                         free_via(via);
2571                         i++;
2572                         continue;
2573                 }
2574
2575                 if ((ast_strlen_zero(via->protocol) && !ast_strlen_zero(testdataptr->expected_protocol))
2576                         || (!ast_strlen_zero(via->protocol) && strcmp(via->protocol, testdataptr->expected_protocol))) {
2577
2578                         ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2579                                 "parsed protocol = \"%s\"\n"
2580                                 "expected = \"%s\"\n"
2581                                 "failed to parse protocol\n",
2582                         i, testdataptr->in, via->protocol, testdataptr->expected_protocol);
2583                         res = AST_TEST_FAIL;
2584                 }
2585
2586                 if ((ast_strlen_zero(via->sent_by) && !ast_strlen_zero(testdataptr->expected_sent_by))
2587                         || (!ast_strlen_zero(via->sent_by) && strcmp(via->sent_by, testdataptr->expected_sent_by))) {
2588
2589                         ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2590                                 "parsed sent_by = \"%s\"\n"
2591                                 "expected = \"%s\"\n"
2592                                 "failed to parse sent-by\n",
2593                         i, testdataptr->in, via->sent_by, testdataptr->expected_sent_by);
2594                         res = AST_TEST_FAIL;
2595                 }
2596
2597                 if (testdataptr->expected_port && testdataptr->expected_port != via->port) {
2598                         ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2599                                 "parsed port = \"%u\"\n"
2600                                 "expected = \"%u\"\n"
2601                                 "failed to parse port\n",
2602                         i, testdataptr->in, via->port, testdataptr->expected_port);
2603                         res = AST_TEST_FAIL;
2604                 }
2605
2606                 if ((ast_strlen_zero(via->branch) && !ast_strlen_zero(testdataptr->expected_branch))
2607                         || (!ast_strlen_zero(via->branch) && strcmp(via->branch, testdataptr->expected_branch))) {
2608
2609                         ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2610                                 "parsed branch = \"%s\"\n"
2611                                 "expected = \"%s\"\n"
2612                                 "failed to parse branch\n",
2613                         i, testdataptr->in, via->branch, testdataptr->expected_branch);
2614                         res = AST_TEST_FAIL;
2615                 }
2616
2617                 if ((ast_strlen_zero(via->maddr) && !ast_strlen_zero(testdataptr->expected_maddr))
2618                         || (!ast_strlen_zero(via->maddr) && strcmp(via->maddr, testdataptr->expected_maddr))) {
2619
2620                         ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2621                                 "parsed maddr = \"%s\"\n"
2622                                 "expected = \"%s\"\n"
2623                                 "failed to parse maddr\n",
2624                         i, testdataptr->in, via->maddr, testdataptr->expected_maddr);
2625                         res = AST_TEST_FAIL;
2626                 }
2627
2628                 if (testdataptr->expected_ttl && testdataptr->expected_ttl != via->ttl) {
2629                         ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
2630                                 "parsed ttl = \"%d\"\n"
2631                                 "expected = \"%d\"\n"
2632                                 "failed to parse ttl\n",
2633                         i, testdataptr->in, via->ttl, testdataptr->expected_ttl);
2634                         res = AST_TEST_FAIL;
2635                 }
2636
2637                 free_via(via);
2638                 i++;
2639         }
2640         return res;
2641 }
2642 #endif
2643
2644 void sip_request_parser_register_tests(void)
2645 {
2646         AST_TEST_REGISTER(get_calleridname_test);
2647         AST_TEST_REGISTER(sip_parse_uri_test);
2648         AST_TEST_REGISTER(get_in_brackets_test);
2649         AST_TEST_REGISTER(get_name_and_number_test);
2650         AST_TEST_REGISTER(sip_parse_uri_full_test);
2651         AST_TEST_REGISTER(parse_name_andor_addr_test);
2652         AST_TEST_REGISTER(parse_contact_header_test);
2653         AST_TEST_REGISTER(sip_parse_options_test);
2654         AST_TEST_REGISTER(sip_uri_cmp_test);
2655         AST_TEST_REGISTER(parse_via_test);
2656 }
2657 void sip_request_parser_unregister_tests(void)
2658 {
2659         AST_TEST_UNREGISTER(sip_parse_uri_test);
2660         AST_TEST_UNREGISTER(get_calleridname_test);
2661         AST_TEST_UNREGISTER(get_in_brackets_test);
2662         AST_TEST_UNREGISTER(get_name_and_number_test);
2663         AST_TEST_UNREGISTER(sip_parse_uri_full_test);
2664         AST_TEST_UNREGISTER(parse_name_andor_addr_test);
2665         AST_TEST_UNREGISTER(parse_contact_header_test);
2666         AST_TEST_UNREGISTER(sip_parse_options_test);
2667         AST_TEST_UNREGISTER(sip_uri_cmp_test);
2668         AST_TEST_UNREGISTER(parse_via_test);
2669 }
2670
2671 int sip_reqresp_parser_init(void)
2672 {
2673 #ifdef HAVE_XLOCALE_H
2674         c_locale = newlocale(LC_CTYPE_MASK, "C", NULL);
2675         if (!c_locale) {
2676                 return -1;
2677         }
2678 #endif
2679         return 0;
2680 }
2681
2682 void sip_reqresp_parser_exit(void)
2683 {
2684 #ifdef HAVE_XLOCALE_H
2685         if (c_locale) {
2686                 freelocale(c_locale);
2687                 c_locale = NULL;
2688         }
2689 #endif
2690 }