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