Add routines for parsing SIP URIs consistently.
[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 #include "asterisk.h"
23
24 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
25
26 #include "include/sip.h"
27 #include "include/sip_utils.h"
28 #include "include/reqresp_parser.h"
29
30 /*! \brief * parses a URI in its components.*/
31 int parse_uri_full(char *uri, const char *scheme, char **user, char **pass, char **host, char **port, struct uriparams *params, char **headers, char **residue)
32 {
33         char *userinfo = NULL;
34         char *parameters = NULL;
35         char *endparams = NULL;
36         char *c = NULL;
37         int error = 0;
38
39         /* check for valid input */
40         if (ast_strlen_zero(uri)) {
41                 return -1;
42         }
43
44         if (scheme) {
45                 int l;
46                 char *scheme2 = ast_strdupa(scheme);
47                 char *cur = strsep(&scheme2, ",");
48                 for (; !ast_strlen_zero(cur); cur = strsep(&scheme2, ",")) {
49                         l = strlen(cur);
50                         if (!strncasecmp(uri, cur, l)) {
51                                 uri += l;
52                                 break;
53                         }
54                 }
55                 if (ast_strlen_zero(cur)) {
56                         ast_debug(1, "No supported scheme found in '%s' using the scheme[s] %s\n", uri, scheme);
57                         error = -1;
58                 }
59         }
60
61         if (!host) {
62                 /* if we don't want to split around host, keep everything as a userinfo - cos thats how old parse_uri operated*/
63                 userinfo = uri;
64         } else {
65                 char *hostport;
66                 if ((c = strchr(uri, '@'))) {
67                         *c++ = '\0';
68                         hostport = c;
69                         userinfo = uri;
70                         uri = hostport; /* userinfo can contain ? and ; chars so step forward before looking for params and headers */
71                 } else {
72                         /* domain-only URI, according to the SIP RFC. */
73                         hostport = uri;
74                         userinfo = "";
75                         }
76
77                 if (port && (c = strchr(hostport, ':'))) {      /* Remove :port */
78                         *c++ = '\0';
79                         *port = c;
80                         uri = c;
81                 } else if (port) {
82                         *port = "";
83                 }
84
85                 *host = hostport;
86         }
87
88         if (pass && (c = strchr(userinfo, ':'))) {        /* user:password */
89                 *c++ = '\0';
90                 *pass = c;
91         } else if (pass) {
92                 *pass = "";
93         }
94
95         if (user) {
96                 *user = userinfo;
97         }
98
99         parameters = uri;
100         /* strip [?headers] from end of uri  - even if no header pointer exists*/
101         if ((c = strrchr(uri, '?'))) {
102                         *c++ = '\0';
103                 uri = c;
104                 if (headers) {
105                         *headers = c;
106                 }
107                 if ((c = strrchr(uri, ';'))) {
108                         *c++ = '\0';
109                 } else {
110                         c = strrchr(uri, '\0');
111                 }
112                 uri = c; /* residue */
113
114
115         } else if (headers) {
116                 *headers = "";
117         }
118
119         /* parse parameters */
120         endparams = strchr(parameters,'\0');
121         if ((c = strchr(parameters, ';'))) {
122                         *c++ = '\0';
123                 parameters = c;
124         } else {
125                 parameters = endparams;
126                 }
127
128         if (params) {
129                 char *rem = parameters; /* unparsed or unrecognised remainder */
130                 char *label;
131                 char *value;
132                 int lr = 0;
133
134                 params->transport = "";
135                 params->user = "";
136                 params->method = "";
137                 params->ttl = "";
138                 params->maddr = "";
139                 params->lr = 0;
140
141                 rem = parameters;
142
143                 while ((value = strchr(parameters, '=')) || (lr = !strncmp(parameters, "lr", 2))) {
144                         /* The while condition will not continue evaluation to set lr if it matches "lr=" */
145                         if (lr) {
146                                 value = parameters;
147                         } else {
148                                 *value++ = '\0';
149                         }
150                         label = parameters;
151                         if ((c = strchr(value, ';'))) {
152                         *c++ = '\0';
153                                 parameters = c;
154                         } else {
155                                 parameters = endparams;
156                 }
157
158                         if (!strcmp(label, "transport")) {
159                                 if (params) {params->transport=value;}
160                                 rem = parameters;
161                         } else if (!strcmp(label, "user")) {
162                                 if (params) {params->user=value;}
163                                 rem = parameters;
164                         } else if (!strcmp(label, "method")) {
165                                 if (params) {params->method=value;}
166                                 rem = parameters;
167                         } else if (!strcmp(label, "ttl")) {
168                                 if (params) {params->ttl=value;}
169                                 rem = parameters;
170                         } else if (!strcmp(label, "maddr")) {
171                                 if (params) {params->maddr=value;}
172                                 rem = parameters;
173                         /* 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 */
174                         } else if ((!strcmp(label, "lr") && strcmp(value, "no") && strcmp(value, "off") && strcmp(value, "0") && strcmp(value, "")) || ((lr) && strcmp(value, "lr"))) {
175                                 if (params) {params->lr=1;}
176                                 rem = parameters;
177                         } else {
178                                 value--;
179                                 *value = '=';
180                                 if(c) {
181                                 c--;
182                                 *c = ';';
183         }
184                         }
185                 }
186                 if (rem > uri) { /* no headers */
187                         uri = rem;
188                 }
189
190         }
191
192         if (residue) {
193                 *residue = uri;
194         }
195
196         return error;
197 }
198
199
200 AST_TEST_DEFINE(sip_parse_uri_fully_test)
201 {
202         int res = AST_TEST_PASS;
203         char uri[1024];
204         char *user, *pass, *host, *port, *headers, *residue;
205         struct uriparams params;
206
207         struct testdata {
208                 char *desc;
209                 char *uri;
210                 char **userptr;
211                 char **passptr;
212                 char **hostptr;
213                 char **portptr;
214                 char **headersptr;
215                 char **residueptr;
216                 struct uriparams *paramsptr;
217                 char *user;
218                 char *pass;
219                 char *host;
220                 char *port;
221                 char *headers;
222                 char *residue;
223                 struct uriparams params;
224                 AST_LIST_ENTRY(testdata) list;
225         };
226
227
228         struct testdata *testdataptr;
229
230         static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
231
232         struct testdata td1 = {
233                 .desc = "no headers",
234                 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=residue",
235                 .userptr = &user,
236                 .passptr = &pass,
237                 .hostptr = &host,
238                 .portptr = &port,
239                 .headersptr = &headers,
240                 .residueptr = &residue,
241                 .paramsptr = &params,
242                 .user = "user",
243                 .pass = "secret",
244                 .host = "host",
245                 .port = "5060",
246                 .headers = "",
247             .residue = "param2=residue",
248                 .params.transport = "tcp",
249                 .params.lr = 0,
250                 .params.user = ""
251         };
252
253         struct testdata td2 = {
254                 .desc = "with headers",
255                 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=discard2?header=blah&header2=blah2;param3=residue",
256                 .userptr = &user,
257                 .passptr = &pass,
258                 .hostptr = &host,
259                 .portptr = &port,
260                 .headersptr = &headers,
261                 .residueptr = &residue,
262                 .paramsptr = &params,
263                 .user = "user",
264                 .pass = "secret",
265                 .host = "host",
266                 .port = "5060",
267                 .headers = "header=blah&header2=blah2",
268                 .residue = "param3=residue",
269                 .params.transport = "tcp",
270                 .params.lr = 0,
271                 .params.user = ""
272         };
273
274         struct testdata td3 = {
275                 .desc = "difficult user",
276                 .uri = "sip:-_.!~*'()&=+$,;?/:secret@host:5060;transport=tcp",
277                 .userptr = &user,
278                 .passptr = &pass,
279                 .hostptr = &host,
280                 .portptr = &port,
281                 .headersptr = &headers,
282                 .residueptr = &residue,
283                 .paramsptr = &params,
284                 .user = "-_.!~*'()&=+$,;?/",
285                 .pass = "secret",
286                 .host = "host",
287                 .port = "5060",
288                 .headers = "",
289                 .residue = "",
290                 .params.transport = "tcp",
291                 .params.lr = 0,
292                 .params.user = ""
293         };
294
295         struct testdata td4 = {
296                 .desc = "difficult pass",
297                 .uri = "sip:user:-_.!~*'()&=+$,@host:5060;transport=tcp",
298                 .userptr = &user,
299                 .passptr = &pass,
300                 .hostptr = &host,
301                 .portptr = &port,
302                 .headersptr = &headers,
303                 .residueptr = &residue,
304                 .paramsptr = &params,
305                 .user = "user",
306                 .pass = "-_.!~*'()&=+$,",
307                 .host = "host",
308                 .port = "5060",
309                 .headers = "",
310                 .residue = "",
311                 .params.transport = "tcp",
312                 .params.lr = 0,
313                 .params.user = ""
314         };
315
316         struct testdata td5 = {
317                 .desc = "difficult host",
318                 .uri = "sip:user:secret@1-1.a-1.:5060;transport=tcp",
319                 .userptr = &user,
320                 .passptr = &pass,
321                 .hostptr = &host,
322                 .portptr = &port,
323                 .headersptr = &headers,
324                 .residueptr = &residue,
325                 .paramsptr = &params,
326                 .user = "user",
327                 .pass = "secret",
328                 .host = "1-1.a-1.",
329                 .port = "5060",
330                 .headers = "",
331                 .residue = "",
332                 .params.transport = "tcp",
333                 .params.lr = 0,
334                 .params.user = ""
335         };
336
337         struct testdata td6 = {
338                 .desc = "difficult params near transport",
339                 .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$;transport=tcp",
340                 .userptr = &user,
341                 .passptr = &pass,
342                 .hostptr = &host,
343                 .portptr = &port,
344                 .headersptr = &headers,
345                 .residueptr = &residue,
346                 .paramsptr = &params,
347                 .user = "user",
348                 .pass = "secret",
349                 .host = "host",
350                 .port = "5060",
351                 .headers = "",
352                 .residue = "",
353                 .params.transport = "tcp",
354                 .params.lr = 0,
355                 .params.user = ""
356         };
357
358         struct testdata td7 = {
359                 .desc = "difficult params near headers",
360                 .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$?header=blah&header2=blah2;-_.!~*'()[]/:&+$=residue",
361                 .userptr = &user,
362                 .passptr = &pass,
363                 .hostptr = &host,
364                 .portptr = &port,
365                 .headersptr = &headers,
366                 .residueptr = &residue,
367                 .paramsptr = &params,
368                 .user = "user",
369                 .pass = "secret",
370                 .host = "host",
371                 .port = "5060",
372                 .headers = "header=blah&header2=blah2",
373                 .residue = "-_.!~*'()[]/:&+$=residue",
374                 .params.transport = "",
375                 .params.lr = 0,
376                 .params.user = ""
377         };
378
379         struct testdata td8 = {
380                 .desc = "lr parameter",
381                 .uri = "sip:user:secret@host:5060;param=discard;lr?header=blah",
382                 .userptr = &user,
383                 .passptr = &pass,
384                 .hostptr = &host,
385                 .portptr = &port,
386                 .headersptr = &headers,
387                 .residueptr = &residue,
388                 .paramsptr = &params,
389                 .user = "user",
390                 .pass = "secret",
391                 .host = "host",
392                 .port = "5060",
393                 .headers = "header=blah",
394                 .residue = "",
395                 .params.transport = "",
396                 .params.lr = 1,
397                 .params.user = ""
398         };
399
400         struct testdata td9 = {
401                 .desc = "alternative lr parameter",
402                 .uri = "sip:user:secret@host:5060;param=discard;lr=yes?header=blah",
403                 .userptr = &user,
404                 .passptr = &pass,
405                 .hostptr = &host,
406                 .portptr = &port,
407                 .headersptr = &headers,
408                 .residueptr = &residue,
409                 .paramsptr = &params,
410                 .user = "user",
411                 .pass = "secret",
412                 .host = "host",
413                 .port = "5060",
414                 .headers = "header=blah",
415                 .residue = "",
416                 .params.transport = "",
417                 .params.lr = 1,
418                 .params.user = ""
419         };
420
421         struct testdata td10 = {
422                 .desc = "no lr parameter",
423                 .uri = "sip:user:secret@host:5060;paramlr=lr;lr=no;lr=off;lr=0;lr=;=lr;lrextra;lrparam2=lr?header=blah",
424                 .userptr = &user,
425                 .passptr = &pass,
426                 .hostptr = &host,
427                 .portptr = &port,
428                 .headersptr = &headers,
429                 .residueptr = &residue,
430                 .paramsptr = &params,
431                 .user = "user",
432                 .pass = "secret",
433                 .host = "host",
434                 .port = "5060",
435                 .headers = "header=blah",
436                 .residue = "",
437                 .params.transport = "",
438                 .params.lr = 0,
439                 .params.user = ""
440         };
441
442
443         AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
444         AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
445         AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
446         AST_LIST_INSERT_TAIL(&testdatalist, &td4, list);
447         AST_LIST_INSERT_TAIL(&testdatalist, &td5, list);
448         AST_LIST_INSERT_TAIL(&testdatalist, &td6, list);
449         AST_LIST_INSERT_TAIL(&testdatalist, &td7, list);
450         AST_LIST_INSERT_TAIL(&testdatalist, &td8, list);
451         AST_LIST_INSERT_TAIL(&testdatalist, &td9, list);
452         AST_LIST_INSERT_TAIL(&testdatalist, &td10, list);
453
454
455         switch (cmd) {
456         case TEST_INIT:
457                 info->name = "sip_uri_full_parse_test";
458                 info->category = "channels/chan_sip/";
459                 info->summary = "tests sip full uri parsing";
460                 info->description =
461                         "Tests full parsing of various URIs "
462                         "Verifies output matches expected behavior.";
463                 return AST_TEST_NOT_RUN;
464         case TEST_EXECUTE:
465                 break;
466         }
467
468         AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
469                 user = pass = host = port = headers = residue = NULL;
470                 params.transport = params.user = params.method = params.ttl = params.maddr = NULL;
471                 params.lr = 0;
472
473                 ast_copy_string(uri,testdataptr->uri,sizeof(uri));
474                 if (parse_uri_full(uri, "sip:,sips:", testdataptr->userptr, testdataptr->passptr, testdataptr->hostptr, testdataptr->portptr, testdataptr->paramsptr, testdataptr->headersptr, testdataptr->residueptr) ||
475                         ((testdataptr->userptr) && strcmp(testdataptr->user, user)) ||
476                         ((testdataptr->passptr) && strcmp(testdataptr->pass, pass)) ||
477                         ((testdataptr->hostptr) && strcmp(testdataptr->host, host)) ||
478                         ((testdataptr->portptr) && strcmp(testdataptr->port, port)) ||
479                         ((testdataptr->headersptr) && strcmp(testdataptr->headers, headers)) ||
480                         ((testdataptr->residueptr) && strcmp(testdataptr->residue, residue)) ||
481                         ((testdataptr->paramsptr) && strcmp(testdataptr->params.transport,params.transport)) ||
482                         ((testdataptr->paramsptr) && (testdataptr->params.lr != params.lr)) ||
483                         ((testdataptr->paramsptr) && strcmp(testdataptr->params.user,params.user))
484                 ) {
485                                 ast_test_status_update(test, "Sub-Test: %s, failed.\n", testdataptr->desc);
486                                 res = AST_TEST_FAIL;
487                 }
488         }
489
490
491         return res;
492 }
493
494
495 int parse_uri(char *uri, const char *scheme, char **user, char **pass, char **host, char **port, char **transport) {
496         int ret;
497         char *headers;
498         struct uriparams params;
499
500         headers = NULL;
501         ret = parse_uri_full(uri, scheme, user, pass, host, port, &params, &headers, NULL);
502         if (transport) {
503                 *transport=params.transport;
504         }
505         return ret;
506 }
507
508 AST_TEST_DEFINE(sip_parse_uri_test)
509 {
510         int res = AST_TEST_PASS;
511         char *name, *pass, *domain, *port, *transport;
512         char uri1[] = "sip:name@host";
513         char uri2[] = "sip:name@host;transport=tcp";
514         char uri3[] = "sip:name:secret@host;transport=tcp";
515         char uri4[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
516         /* test 5 is for NULL input */
517         char uri6[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
518         char uri7[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
519
520         switch (cmd) {
521         case TEST_INIT:
522                 info->name = "sip_uri_parse_test";
523                 info->category = "channels/chan_sip/";
524                 info->summary = "tests sip uri parsing";
525                 info->description =
526                                                         "Tests parsing of various URIs "
527                                                         "Verifies output matches expected behavior.";
528                 return AST_TEST_NOT_RUN;
529         case TEST_EXECUTE:
530                 break;
531         }
532
533         /* Test 1, simple URI */
534         name = pass = domain = port = transport = NULL;
535         if (parse_uri(uri1, "sip:,sips:", &name, &pass, &domain, &port, &transport) ||
536                         strcmp(name, "name")        ||
537                         !ast_strlen_zero(pass)      ||
538                         strcmp(domain, "host")      ||
539                         !ast_strlen_zero(port)      ||
540                         !ast_strlen_zero(transport)) {
541                 ast_test_status_update(test, "Test 1: simple uri failed. \n");
542                 res = AST_TEST_FAIL;
543         }
544
545         /* Test 2, add tcp transport */
546         name = pass = domain = port = transport = NULL;
547         if (parse_uri(uri2, "sip:,sips:", &name, &pass, &domain, &port, &transport) ||
548                         strcmp(name, "name")        ||
549                         !ast_strlen_zero(pass)      ||
550                         strcmp(domain, "host")    ||
551                         !ast_strlen_zero(port)      ||
552                         strcmp(transport, "tcp")) {
553                 ast_test_status_update(test, "Test 2: uri with addtion of tcp transport failed. \n");
554                 res = AST_TEST_FAIL;
555         }
556
557         /* Test 3, add secret */
558         name = pass = domain = port = transport = NULL;
559         if (parse_uri(uri3, "sip:,sips:", &name, &pass, &domain, &port, &transport) ||
560                         strcmp(name, "name")        ||
561                         strcmp(pass, "secret")      ||
562                         strcmp(domain, "host")    ||
563                         !ast_strlen_zero(port)      ||
564                         strcmp(transport, "tcp")) {
565                 ast_test_status_update(test, "Test 3: uri with addition of secret failed.\n");
566                 res = AST_TEST_FAIL;
567         }
568
569         /* Test 4, add port and unparsed header field*/
570         name = pass = domain = port = transport = NULL;
571         if (parse_uri(uri4, "sip:,sips:", &name, &pass, &domain, &port, &transport) ||
572                         strcmp(name, "name")        ||
573                         strcmp(pass, "secret")      ||
574                         strcmp(domain, "host")    ||
575                         strcmp(port, "port")      ||
576                         strcmp(transport, "tcp")) {
577                 ast_test_status_update(test, "Test 4: add port and unparsed header field failed.\n");
578                 res = AST_TEST_FAIL;
579         }
580
581         /* Test 5, verify parse_uri does not crash when given a NULL uri */
582         name = pass = domain = port = transport = NULL;
583         if (!parse_uri(NULL, "sip:,sips:", &name, &pass, &domain, &port, &transport)) {
584                 ast_test_status_update(test, "Test 5: passing a NULL uri failed.\n");
585                 res = AST_TEST_FAIL;
586         }
587
588         /* Test 6, verify parse_uri does not crash when given a NULL output parameters */
589         name = pass = domain = port = transport = NULL;
590         if (parse_uri(uri6, "sip:,sips:", NULL, NULL, NULL, NULL, NULL)) {
591                 ast_test_status_update(test, "Test 6: passing NULL output parameters failed.\n");
592                 res = AST_TEST_FAIL;
593         }
594
595         /* Test 7, verify parse_uri returns user:secret and domain:port when no port or secret output parameters are supplied. */
596         name = pass = domain = port = transport = NULL;
597         if (parse_uri(uri7, "sip:,sips:", &name, NULL, &domain, NULL, NULL) ||
598                         strcmp(name, "name:secret")        ||
599                         strcmp(domain, "host:port")) {
600
601                 ast_test_status_update(test, "Test 7: providing no port and secret output parameters failed.\n");
602                 res = AST_TEST_FAIL;
603         }
604         return res;
605 }
606
607 /*! \brief  Get caller id name from SIP headers, copy into output buffer
608  *
609  *  \retval input string pointer placed after display-name field if possible
610  */
611 const char *get_calleridname(const char *input, char *output, size_t outputsize)
612 {
613         /* From RFC3261:
614          * 
615          * From           =  ( "From" / "f" ) HCOLON from-spec
616          * from-spec      =  ( name-addr / addr-spec ) *( SEMI from-param )
617          * name-addr      =  [ display-name ] LAQUOT addr-spec RAQUOT
618          * display-name   =  *(token LWS)/ quoted-string
619          * token          =  1*(alphanum / "-" / "." / "!" / "%" / "*"
620          *                     / "_" / "+" / "`" / "'" / "~" )
621          * quoted-string  =  SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE
622          * qdtext         =  LWS / %x21 / %x23-5B / %x5D-7E
623          *                     / UTF8-NONASCII
624          * quoted-pair    =  "\" (%x00-09 / %x0B-0C / %x0E-7F)
625          *
626          * HCOLON         = *WSP ":" SWS
627          * SWS            = [LWS]
628          * LWS            = *[*WSP CRLF] 1*WSP
629          * WSP            = (SP / HTAB)
630          *
631          * Deviations from it:
632          * - following CRLF's in LWS is not done (here at least)
633          * - ascii NUL is never legal as it terminates the C-string
634          * - utf8-nonascii is not checked for validity
635          */
636         char *orig_output = output;
637         const char *orig_input = input;
638
639         /* clear any empty characters in the beginning */
640         input = ast_skip_blanks(input);
641
642         /* no data at all or no storage room? */
643         if (!input || *input == '<' || !outputsize || !output) {
644                 return orig_input;
645         }
646
647         /* make sure the output buffer is initilized */
648         *orig_output = '\0';
649
650         /* make room for '\0' at the end of the output buffer */
651         outputsize--;
652
653         /* quoted-string rules */
654         if (input[0] == '"') {
655                 input++; /* skip the first " */
656
657                 for (;((outputsize > 0) && *input); input++) {
658                         if (*input == '"') {  /* end of quoted-string */
659                                 break;
660                         } else if (*input == 0x5c) { /* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) */
661                                 input++;
662                                 if (!*input || (unsigned char)*input > 0x7f || *input == 0xa || *input == 0xd) {
663                                         continue;  /* not a valid quoted-pair, so skip it */
664                                 }
665                         } else if (((*input != 0x9) && ((unsigned char) *input < 0x20)) ||
666                                     (*input == 0x7f)) {
667                                 continue; /* skip this invalid character. */
668                         }
669
670                         *output++ = *input;
671                         outputsize--;
672                 }
673
674                 /* if this is successful, input should be at the ending quote */
675                 if (!input || *input != '"') {
676                         ast_log(LOG_WARNING, "No ending quote for display-name was found\n");
677                         *orig_output = '\0';
678                         return orig_input;
679                 }
680
681                 /* make sure input is past the last quote */
682                 input++;
683
684                 /* terminate outbuf */
685                 *output = '\0';
686         } else {  /* either an addr-spec or tokenLWS-combo */
687                 for (;((outputsize > 0) && *input); input++) {
688                         /* token or WSP (without LWS) */
689                         if ((*input >= '0' && *input <= '9') || (*input >= 'A' && *input <= 'Z')
690                                 || (*input >= 'a' && *input <= 'z') || *input == '-' || *input == '.'
691                                 || *input == '!' || *input == '%' || *input == '*' || *input == '_'
692                                 || *input == '+' || *input == '`' || *input == '\'' || *input == '~'
693                                 || *input == 0x9 || *input == ' ') {
694                                 *output++ = *input;
695                                 outputsize -= 1;
696                         } else if (*input == '<') {   /* end of tokenLWS-combo */
697                                 /* we could assert that the previous char is LWS, but we don't care */
698                                 break;
699                         } else if (*input == ':') {
700                                 /* This invalid character which indicates this is addr-spec rather than display-name. */
701                                 *orig_output = '\0';
702                                 return orig_input;
703                         } else {         /* else, invalid character we can skip. */
704                                 continue;    /* skip this character */
705                         }
706                 }
707
708                 /* set NULL while trimming trailing whitespace */
709                 do {
710                         *output-- = '\0';
711                 } while (*output == 0x9 || *output == ' '); /* we won't go past orig_output as first was a non-space */
712         }
713
714         return input;
715 }
716
717 AST_TEST_DEFINE(get_calleridname_test)
718 {
719         int res = AST_TEST_PASS;
720         const char *in1 = "\" quoted-text internal \\\" quote \"<stuff>";
721         const char *in2 = " token text with no quotes <stuff>";
722         const char *overflow1 = " \"quoted-text overflow 1234567890123456789012345678901234567890\" <stuff>";
723         const char *noendquote = " \"quoted-text no end <stuff>";
724         const char *addrspec = " \"sip:blah@blah <stuff>";
725         const char *after_dname;
726         char dname[40];
727
728         switch (cmd) {
729         case TEST_INIT:
730                 info->name = "sip_get_calleridname_test";
731                 info->category = "channels/chan_sip/";
732                 info->summary = "decodes callerid name from sip header";
733                 info->description = "Decodes display-name field of sip header.  Checks for valid output and expected failure cases.";
734                 return AST_TEST_NOT_RUN;
735         case TEST_EXECUTE:
736                 break;
737         }
738
739         /* quoted-text with backslash escaped quote */
740         after_dname = get_calleridname(in1, dname, sizeof(dname));
741         ast_test_status_update(test, "display-name1: %s\nafter: %s\n", dname, after_dname);
742         if (strcmp(dname, " quoted-text internal \" quote ")) {
743                 ast_test_status_update(test, "display-name1 test failed\n");
744                 res = AST_TEST_FAIL;
745         }
746
747         /* token text */
748         after_dname = get_calleridname(in2, dname, sizeof(dname));
749         ast_test_status_update(test, "display-name2: %s\nafter: %s\n", dname, after_dname);
750         if (strcmp(dname, "token text with no quotes")) {
751                 ast_test_status_update(test, "display-name2 test failed\n");
752                 res = AST_TEST_FAIL;
753         }
754
755         /* quoted-text buffer overflow */
756         after_dname = get_calleridname(overflow1, dname, sizeof(dname));
757         ast_test_status_update(test, "overflow display-name1: %s\nafter: %s\n", dname, after_dname);
758         if (*dname != '\0' && after_dname != overflow1) {
759                 ast_test_status_update(test, "overflow display-name1 test failed\n");
760                 res = AST_TEST_FAIL;
761         }
762
763         /* quoted-text buffer with no terminating end quote */
764         after_dname = get_calleridname(noendquote, dname, sizeof(dname));
765         ast_test_status_update(test, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname);
766         if (*dname != '\0' && after_dname != noendquote) {
767                 ast_test_status_update(test, "no end quote for quoted-text display-name failed\n");
768                 res = AST_TEST_FAIL;
769         }
770
771         /* addr-spec rather than display-name. */
772         after_dname = get_calleridname(addrspec, dname, sizeof(dname));
773         ast_test_status_update(test, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname);
774         if (*dname != '\0' && after_dname != addrspec) {
775                 ast_test_status_update(test, "detection of addr-spec failed\n");
776                 res = AST_TEST_FAIL;
777         }
778
779         return res;
780 }
781
782 int get_name_and_number(const char *hdr, char **name, char **number)
783 {
784         char header[256];
785         char tmp_name[50] = { 0, };
786         char *tmp_number = NULL;
787         char *domain = NULL;
788         char *dummy = NULL;
789
790         if (!name || !number || ast_strlen_zero(hdr)) {
791                 return -1;
792         }
793
794         *number = NULL;
795         *name = NULL;
796         ast_copy_string(header, hdr, sizeof(header));
797
798         /* strip the display-name portion off the beginning of the header. */
799         get_calleridname(header, tmp_name, sizeof(tmp_name));
800
801         /* get uri within < > brackets */
802         tmp_number = get_in_brackets(header);
803
804         /* parse out the number here */
805         if (parse_uri(tmp_number, "sip:,sips:", &tmp_number, &dummy, &domain, &dummy, NULL) || ast_strlen_zero(tmp_number)) {
806                 ast_log(LOG_ERROR, "can not parse name and number from sip header.\n");
807                 return -1;
808         }
809
810         /* number is not option, and must be present at this point */
811         *number = ast_strdup(tmp_number);
812         ast_uri_decode(*number);
813
814         /* name is optional and may not be present at this point */
815         if (!ast_strlen_zero(tmp_name)) {
816                 *name = ast_strdup(tmp_name);
817         }
818
819         return 0;
820 }
821
822 AST_TEST_DEFINE(get_name_and_number_test)
823 {
824         int res = AST_TEST_PASS;
825         char *name = NULL;
826         char *number = NULL;
827         const char *in1 = "NAME <sip:NUMBER@place>";
828         const char *in2 = "\"NA><ME\" <sip:NUMBER@place>";
829         const char *in3 = "NAME";
830         const char *in4 = "<sip:NUMBER@place>";
831         const char *in5 = "This is a screwed up string <sip:LOLCLOWNS<sip:>@place>";
832
833         switch (cmd) {
834         case TEST_INIT:
835                 info->name = "sip_get_name_and_number_test";
836                 info->category = "channels/chan_sip/";
837                 info->summary = "Tests getting name and number from sip header";
838                 info->description =
839                                 "Runs through various test situations in which a name and "
840                                 "and number can be retrieved from a sip header.";
841                 return AST_TEST_NOT_RUN;
842         case TEST_EXECUTE:
843                 break;
844         }
845
846         /* Test 1. get name and number */
847         number = name = NULL;
848         if ((get_name_and_number(in1, &name, &number)) ||
849                 strcmp(name, "NAME") ||
850                 strcmp(number, "NUMBER")) {
851
852                 ast_test_status_update(test, "Test 1, simple get name and number failed.\n");
853                 res = AST_TEST_FAIL;
854         }
855         ast_free(name);
856         ast_free(number);
857
858         /* Test 2. get quoted name and number */
859         number = name = NULL;
860         if ((get_name_and_number(in2, &name, &number)) ||
861                 strcmp(name, "NA><ME") ||
862                 strcmp(number, "NUMBER")) {
863
864                 ast_test_status_update(test, "Test 2, get quoted name and number failed.\n");
865                 res = AST_TEST_FAIL;
866         }
867         ast_free(name);
868         ast_free(number);
869
870         /* Test 3. name only */
871         number = name = NULL;
872         if (!(get_name_and_number(in3, &name, &number))) {
873
874                 ast_test_status_update(test, "Test 3, get name only was expected to fail but did not.\n");
875                 res = AST_TEST_FAIL;
876         }
877         ast_free(name);
878         ast_free(number);
879
880         /* Test 4. number only */
881         number = name = NULL;
882         if ((get_name_and_number(in4, &name, &number)) ||
883                 !ast_strlen_zero(name) ||
884                 strcmp(number, "NUMBER")) {
885
886                 ast_test_status_update(test, "Test 4, get number with no name present failed.\n");
887                 res = AST_TEST_FAIL;
888         }
889         ast_free(name);
890         ast_free(number);
891
892         /* Test 5. malformed string, since number can not be parsed, this should return an error.  */
893         number = name = NULL;
894         if (!(get_name_and_number(in5, &name, &number)) ||
895                 !ast_strlen_zero(name) ||
896                 !ast_strlen_zero(number)) {
897
898                 ast_test_status_update(test, "Test 5, processing malformed string failed.\n");
899                 res = AST_TEST_FAIL;
900         }
901         ast_free(name);
902         ast_free(number);
903
904         /* Test 6. NULL output parameters */
905         number = name = NULL;
906         if (!(get_name_and_number(in5, NULL, NULL))) {
907
908                 ast_test_status_update(test, "Test 6, NULL output parameters failed.\n");
909                 res = AST_TEST_FAIL;
910         }
911
912         /* Test 7. NULL input parameter */
913         number = name = NULL;
914         if (!(get_name_and_number(NULL, &name, &number)) ||
915                 !ast_strlen_zero(name) ||
916                 !ast_strlen_zero(number)) {
917
918                 ast_test_status_update(test, "Test 7, NULL input parameter failed.\n");
919                 res = AST_TEST_FAIL;
920         }
921         ast_free(name);
922         ast_free(number);
923
924         return res;
925 }
926
927 int get_in_brackets_full(char *tmp,char **out,char **residue)
928 {
929         const char *parse = tmp;
930         char *first_bracket;
931         char *second_bracket;
932
933         if (out) {
934                 *out = "";
935         }
936         if (residue) {
937                 *residue = "";
938         }
939
940
941         if (ast_strlen_zero(tmp)) {
942                 return 1;
943         }
944
945         /*
946          * Skip any quoted text until we find the part in brackets.
947         * On any error give up and return -1
948         */
949         while ( (first_bracket = strchr(parse, '<')) ) {
950                 char *first_quote = strchr(parse, '"');
951                 first_bracket++;
952                 if (!first_quote || first_quote >= first_bracket) {
953                         break; /* no need to look at quoted part */
954                 }
955                 /* the bracket is within quotes, so ignore it */
956                 parse = find_closing_quote(first_quote + 1, NULL);
957                 if (!*parse) {
958                         ast_log(LOG_WARNING, "No closing quote found in '%s'\n", tmp);
959                         return  -1;
960                 }
961                 parse++;
962         }
963
964         /* If no first bracket then still look for a second bracket as some other parsing functions
965         may overwrite first bracket with NULL when terminating a token based display-name. As this
966         only affects token based display-names there is no danger of brackets being in quotes */
967         if (first_bracket) {
968                 parse = first_bracket;
969                 } else {
970                 parse = tmp;
971         }
972
973         if ((second_bracket = strchr(parse, '>'))) {
974                 *second_bracket++ = '\0';
975                 if (out) {
976                         *out = first_bracket;
977                 }
978                 if (residue) {
979                         *residue = second_bracket;
980                 }
981                 return 0;
982         }
983
984         if ((first_bracket)) {
985                         ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
986                 return -1;
987                 }
988
989         if (out) {
990                 *out = tmp;
991         }
992         return 1;
993 }
994
995 char *get_in_brackets(char *tmp)
996 {
997         char *out;
998         if((get_in_brackets_full(tmp, &out, NULL))) {
999         return tmp;
1000         } else {
1001                 return out;
1002 }
1003 }
1004
1005 AST_TEST_DEFINE(get_in_brackets_test)
1006 {
1007         int res = AST_TEST_PASS;
1008         char *in_brackets = "<sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1009         char no_name[] = "<sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1010         char quoted_string[] = "\"I'm a quote stri><ng\" <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1011         char missing_end_quote[] = "\"I'm a quote string <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1012         char name_no_quotes[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
1013         char no_end_bracket[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah";
1014         char *uri = NULL;
1015
1016         switch (cmd) {
1017         case TEST_INIT:
1018                 info->name = "sip_get_in_brackets_test";
1019                 info->category = "channels/chan_sip/";
1020                 info->summary = "Tests getting a sip uri in <> brackets within a sip header.";
1021                 info->description =
1022                                 "Runs through various test situations in which a sip uri "
1023                                 "in angle brackets needs to be retrieved";
1024                 return AST_TEST_NOT_RUN;
1025         case TEST_EXECUTE:
1026                 break;
1027         }
1028
1029         /* Test 1, simple get in brackets */
1030         if (!(uri = get_in_brackets(no_name)) || !(strcmp(uri, in_brackets))) {
1031
1032                 ast_test_status_update(test, "Test 1, simple get in brackets failed.\n");
1033                 res = AST_TEST_FAIL;
1034         }
1035
1036         /* Test 2, starts with quoted string */
1037         if (!(uri = get_in_brackets(quoted_string)) || !(strcmp(uri, in_brackets))) {
1038
1039                 ast_test_status_update(test, "Test 2, get in brackets with quoted string in front failed.\n");
1040                 res = AST_TEST_FAIL;
1041         }
1042
1043         /* Test 3, missing end quote */
1044         if (!(uri = get_in_brackets(missing_end_quote)) || !(strcmp(uri, in_brackets))) {
1045
1046                 ast_test_status_update(test, "Test 3, missing end quote failed.\n");
1047                 res = AST_TEST_FAIL;
1048         }
1049
1050         /* Test 4, starts with a name not in quotes */
1051         if (!(uri = get_in_brackets(name_no_quotes)) || !(strcmp(uri, in_brackets))) {
1052
1053                 ast_test_status_update(test, "Test 4, passing name not in quotes failed.\n");
1054                 res = AST_TEST_FAIL;
1055         }
1056
1057         /* Test 5, no end bracket, should just return everything after the first '<'  */
1058         if (!(uri = get_in_brackets(no_end_bracket)) || !(strcmp(uri, in_brackets))) {
1059
1060                 ast_test_status_update(test, "Test 5, no end bracket failed.\n");
1061                 res = AST_TEST_FAIL;
1062         }
1063
1064         /* Test 6, NULL input  */
1065         if ((uri = get_in_brackets(NULL))) {
1066
1067                 ast_test_status_update(test, "Test 6, NULL input failed.\n");
1068                 res = AST_TEST_FAIL;
1069         }
1070
1071
1072         return res;
1073 }
1074
1075
1076 int parse_name_andor_addr(char *uri, const char *scheme, char **name, char **user, char **pass, char **host, char **port, struct uriparams *params, char **headers, char **residue)
1077 {
1078         static char buf[1024];
1079         char **residue2=residue;
1080         int ret;
1081         if (name) {
1082                 get_calleridname(uri,buf,sizeof(buf));
1083                 *name = buf;
1084         }
1085         ret = get_in_brackets_full(uri,&uri,residue);
1086         if (ret == 0) { /* uri is in brackets so do not treat unknown trailing uri parameters as potential messageheader parameters */
1087                 *residue = *residue + 1; /* step over the first semicolon so as per parse uri residue */
1088                 residue2 = NULL;
1089         }
1090
1091         return parse_uri_full(uri, scheme, user, pass, host, port, params, headers, residue2);
1092 }
1093
1094 AST_TEST_DEFINE(parse_name_andor_addr_test)
1095 {
1096         int res = AST_TEST_PASS;
1097         char uri[1024];
1098         char *name, *user, *pass, *host, *port, *headers, *residue;
1099         struct uriparams params;
1100
1101         struct testdata {
1102                 char *desc;
1103                 char *uri;
1104                 char **nameptr;
1105                 char **userptr;
1106                 char **passptr;
1107                 char **hostptr;
1108                 char **portptr;
1109                 char **headersptr;
1110                 char **residueptr;
1111                 struct uriparams *paramsptr;
1112                 char *name;
1113                 char *user;
1114                 char *pass;
1115                 char *host;
1116                 char *port;
1117                 char *headers;
1118                 char *residue;
1119                 struct uriparams params;
1120                 AST_LIST_ENTRY(testdata) list;
1121         };
1122
1123         struct testdata *testdataptr;
1124
1125         static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
1126
1127         struct testdata td1 = {
1128                 .desc = "quotes and brackets",
1129                 .uri = "\"name :@ \" <sip:user:secret@host:5060;param=discard;transport=tcp>;tag=tag",
1130                 .nameptr = &name,
1131                 .userptr = &user,
1132                 .passptr = &pass,
1133                 .hostptr = &host,
1134                 .portptr = &port,
1135                 .headersptr = &headers,
1136                 .residueptr = &residue,
1137                 .paramsptr = &params,
1138                 .name =  "name :@ ",
1139                 .user = "user",
1140                 .pass = "secret",
1141                 .host = "host",
1142                 .port = "5060",
1143                 .headers = "",
1144                 .residue = "tag=tag",
1145                 .params.transport = "tcp",
1146                 .params.lr = 0,
1147                 .params.user = ""
1148         };
1149
1150         struct testdata td2 = {
1151                 .desc = "no quotes",
1152                 .uri = "givenname familyname <sip:user:secret@host:5060;param=discard;transport=tcp>;expires=3600",
1153                 .nameptr = &name,
1154                 .userptr = &user,
1155                 .passptr = &pass,
1156                 .hostptr = &host,
1157                 .portptr = &port,
1158                 .headersptr = &headers,
1159                 .residueptr = &residue,
1160                 .paramsptr = &params,
1161                 .name = "givenname familyname",
1162                 .user = "user",
1163                 .pass = "secret",
1164                 .host = "host",
1165                 .port = "5060",
1166                 .headers = "",
1167                 .residue = "expires=3600",
1168                 .params.transport = "tcp",
1169                 .params.lr = 0,
1170                 .params.user = ""
1171         };
1172
1173         struct testdata td3 = {
1174                 .desc = "no brackets",
1175                 .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;q=1",
1176                 .nameptr = &name,
1177                 .userptr = &user,
1178                 .passptr = &pass,
1179                 .hostptr = &host,
1180                 .portptr = &port,
1181                 .headersptr = &headers,
1182                 .residueptr = &residue,
1183                 .paramsptr = &params,
1184                 .name = "",
1185                 .user = "user",
1186                 .pass = "secret",
1187                 .host = "host",
1188                 .port = "5060",
1189                 .headers = "",
1190                 .residue = "q=1",
1191                 .params.transport = "tcp",
1192                 .params.lr = 0,
1193                 .params.user = ""
1194         };
1195
1196         struct testdata td4 = {
1197                 .desc = "just host",
1198                 .uri = "sips:host",
1199                 .nameptr = &name,
1200                 .userptr = &user,
1201                 .passptr = &pass,
1202                 .hostptr = &host,
1203                 .portptr = &port,
1204                 .headersptr = &headers,
1205                 .residueptr = &residue,
1206                 .paramsptr = &params,
1207                 .name = "",
1208                 .user = "",
1209                 .pass = "",
1210                 .host = "host",
1211                 .port = "",
1212                 .headers = "",
1213                 .residue = "",
1214                 .params.transport = "",
1215                 .params.lr = 0,
1216                 .params.user = ""
1217         };
1218
1219
1220         AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
1221         AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
1222         AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
1223         AST_LIST_INSERT_TAIL(&testdatalist, &td4, list);
1224
1225
1226         switch (cmd) {
1227         case TEST_INIT:
1228                 info->name = "parse_name_andor_addr_test";
1229                 info->category = "channels/chan_sip/";
1230                 info->summary = "tests parsing of name_andor_addr abnf structure";
1231                 info->description =
1232                         "Tests parsing of abnf name-andor-addr = name-addr / addr-spec "
1233                         "Verifies output matches expected behavior.";
1234                 return AST_TEST_NOT_RUN;
1235         case TEST_EXECUTE:
1236                 break;
1237         }
1238
1239         AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
1240                 name = user = pass = host = port = headers = residue = NULL;
1241                 params.transport = params.user = params.method = params.ttl = params.maddr = NULL;
1242                 params.lr = 0;
1243         ast_copy_string(uri,testdataptr->uri,sizeof(uri));
1244                 if (parse_name_andor_addr(uri, "sip:,sips:", testdataptr->nameptr, testdataptr->userptr, testdataptr->passptr, testdataptr->hostptr, testdataptr->portptr, testdataptr->paramsptr, testdataptr->headersptr, testdataptr->residueptr) ||
1245                         ((testdataptr->nameptr) && strcmp(testdataptr->name, name)) ||
1246                         ((testdataptr->userptr) && strcmp(testdataptr->user, user)) ||
1247                         ((testdataptr->passptr) && strcmp(testdataptr->pass, pass)) ||
1248                         ((testdataptr->hostptr) && strcmp(testdataptr->host, host)) ||
1249                         ((testdataptr->portptr) && strcmp(testdataptr->port, port)) ||
1250                         ((testdataptr->headersptr) && strcmp(testdataptr->headers, headers)) ||
1251                         ((testdataptr->residueptr) && strcmp(testdataptr->residue, residue)) ||
1252                         ((testdataptr->paramsptr) && strcmp(testdataptr->params.transport,params.transport)) ||
1253                         ((testdataptr->paramsptr) && strcmp(testdataptr->params.user,params.user))
1254                 ) {
1255                                 ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1256                                 res = AST_TEST_FAIL;
1257                 }
1258         }
1259
1260
1261         return res;
1262 }
1263
1264 int get_comma(char *in, char **out) {
1265         char *c;
1266         char *parse = in;
1267         if (out) {
1268                 *out = in;
1269         }
1270
1271         /* Skip any quoted text */
1272         while (*parse) {
1273                 if ((c = strchr(parse, '"'))) {
1274                         in = (char *)find_closing_quote((const char *)c + 1, NULL);
1275                         if (!*in) {
1276                                 ast_log(LOG_WARNING, "No closing quote found in '%s'\n", c);
1277                                 return -1;
1278                         } else {
1279                                 break;
1280                         }
1281                 } else {
1282                         break;
1283                 }
1284                 parse++;
1285         }
1286         parse = in;
1287
1288         /* Skip any userinfo components of a uri as they may contain commas */
1289         if ((c = strchr(parse,'@'))) {
1290                 parse = c+1;
1291         }
1292         if ((out) && (c = strchr(parse,','))) {
1293                 *c++ = '\0';
1294                 *out = c;
1295                 return 0;
1296         }
1297         return 1;
1298 }
1299
1300 int parse_contact_header(char *contactheader, struct contactliststruct *contactlist) {
1301         int res;
1302         int last;
1303         char *comma;
1304         char *residue;
1305         char *param;
1306         char *value;
1307         struct contact *contact=NULL;
1308
1309         if (*contactheader == '*') {
1310                 return 1;
1311         }
1312
1313         contact = malloc(sizeof(*contact));
1314
1315         AST_LIST_HEAD_SET_NOLOCK(contactlist, contact);
1316         while ((last = get_comma(contactheader,&comma)) != -1) {
1317
1318                 res = parse_name_andor_addr(contactheader,"sip:,sips:",&contact->name,&contact->user,&contact->pass,&contact->host,&contact->port,&contact->params,&contact->headers,&residue);
1319                 if (res == -1) {
1320                         return res;
1321                 }
1322
1323                 /* parse contact params */
1324                 contact->expires = contact->q = "";
1325
1326                 while ((value = strchr(residue,'='))) {
1327                         *value++ = '\0';
1328
1329                         param = residue;
1330                         if ((residue = strchr(value,';'))) {
1331                                 *residue++ = '\0';
1332                         } else {
1333                                 residue = "";
1334                         }
1335
1336                         if (!strcmp(param,"expires")) {
1337                                 contact->expires = value;
1338                         } else if (!strcmp(param,"q")) {
1339                                 contact->q = value;
1340                         }
1341                 }
1342
1343                 if(last) {
1344                         return 0;
1345                 }
1346                 contactheader = comma;
1347
1348                 contact = malloc(sizeof(*contact));
1349                 AST_LIST_INSERT_TAIL(contactlist, contact, list);
1350
1351         }
1352         return last;
1353 }
1354
1355 AST_TEST_DEFINE(parse_contact_header_test)
1356 {
1357         int res = AST_TEST_PASS;
1358         char contactheader[1024];
1359         int star;
1360         struct contactliststruct contactlist;
1361         struct contactliststruct *contactlistptr=&contactlist;
1362
1363         struct testdata {
1364                 char *desc;
1365                 char *contactheader;
1366                 int star;
1367                 struct contactliststruct *contactlist;
1368
1369                 AST_LIST_ENTRY(testdata) list;
1370         };
1371
1372         struct testdata *testdataptr;
1373         struct contact *tdcontactptr;
1374         struct contact *contactptr;
1375
1376         static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
1377         struct contactliststruct contactlist1, contactlist2;
1378
1379         struct testdata td1 = {
1380                 .desc = "single contact",
1381                 .contactheader = "\"name :@;?&,\" <sip:user:secret@host:5082;param=discard;transport=tcp>;expires=3600",
1382                 .contactlist = &contactlist1,
1383                 .star = 0
1384         };
1385         struct contact contact11 = {
1386                 .name = "name :@;?&,",
1387                 .user = "user",
1388                 .pass = "secret",
1389                 .host = "host",
1390                 .port = "5082",
1391                 .params.transport = "tcp",
1392                 .params.ttl = "",
1393                 .params.lr = 0,
1394                 .headers = "",
1395                 .expires = "3600",
1396                 .q = ""
1397         };
1398
1399         struct testdata td2 = {
1400                 .desc = "multiple contacts",
1401                 .contactheader = "sip:,user1,:,secret1,@host1;ttl=7;q=1;expires=3600,sips:host2",
1402                 .contactlist = &contactlist2,
1403                 .star = 0,
1404         };
1405         struct contact contact21 = {
1406                 .name = "",
1407                 .user = ",user1,",
1408                 .pass = ",secret1,",
1409                 .host = "host1",
1410                 .port = "",
1411                 .params.transport = "",
1412                 .params.ttl = "7",
1413                 .params.lr = 0,
1414                 .headers = "",
1415                 .expires = "3600",
1416                 .q = "1"
1417         };
1418         struct contact contact22 = {
1419                 .name = "",
1420                 .user = "",
1421                 .pass = "",
1422                 .host = "host2",
1423                 .port = "",
1424                 .params.transport = "",
1425                 .params.ttl = "",
1426                 .params.lr = 0,
1427                 .headers = "",
1428                 .expires = "",
1429                 .q = ""
1430         };
1431
1432         struct testdata td3 = {
1433                 .desc = "star - all contacts",
1434                 .contactheader = "*",
1435                 .star = 1,
1436                 .contactlist = NULL
1437         };
1438
1439         AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
1440         AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
1441         AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
1442
1443         AST_LIST_HEAD_SET_NOLOCK(&contactlist1, &contact11);
1444
1445         AST_LIST_HEAD_SET_NOLOCK(&contactlist2, &contact21);
1446         AST_LIST_INSERT_TAIL(&contactlist2, &contact22, list);
1447
1448
1449         switch (cmd) {
1450         case TEST_INIT:
1451                 info->name = "parse_contact_header_test";
1452                 info->category = "channels/chan_sip/";
1453                 info->summary = "tests parsing of sip contact header";
1454                 info->description =
1455                         "Tests parsing of a contact header including those with multiple contacts "
1456                         "Verifies output matches expected behavior.";
1457                 return AST_TEST_NOT_RUN;
1458         case TEST_EXECUTE:
1459                 break;
1460         }
1461
1462         AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
1463                 ast_copy_string(contactheader,testdataptr->contactheader,sizeof(contactheader));
1464                 star = parse_contact_header(contactheader,contactlistptr);
1465                 if (testdataptr->star) {
1466                         /* expecting star rather than list of contacts */
1467                         if (!star) {
1468                                 ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1469                                 res = AST_TEST_FAIL;
1470                                 break;
1471                         }
1472                 } else {
1473                         contactptr = AST_LIST_FIRST(contactlistptr);
1474                         AST_LIST_TRAVERSE(testdataptr->contactlist, tdcontactptr, list) {
1475                                 if (!contactptr ||
1476                                         strcmp(tdcontactptr->name, contactptr->name) ||
1477                                         strcmp(tdcontactptr->user, contactptr->user) ||
1478                                         strcmp(tdcontactptr->pass, contactptr->pass) ||
1479                                         strcmp(tdcontactptr->host, contactptr->host) ||
1480                                         strcmp(tdcontactptr->port, contactptr->port) ||
1481                                         strcmp(tdcontactptr->headers, contactptr->headers) ||
1482                                         strcmp(tdcontactptr->expires, contactptr->expires) ||
1483                                         strcmp(tdcontactptr->q, contactptr->q) ||
1484                                         strcmp(tdcontactptr->params.transport, contactptr->params.transport) ||
1485                                         strcmp(tdcontactptr->params.ttl, contactptr->params.ttl) ||
1486                                         (tdcontactptr->params.lr != contactptr->params.lr)
1487                                 ) {
1488                                         ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
1489                                         res = AST_TEST_FAIL;
1490                                         break;
1491                                 }
1492
1493                         contactptr = AST_LIST_NEXT(contactptr,list);
1494                         }
1495                 }
1496
1497         }
1498
1499         return res;
1500 }
1501
1502 void sip_request_parser_register_tests(void)
1503 {
1504         AST_TEST_REGISTER(get_calleridname_test);
1505         AST_TEST_REGISTER(sip_parse_uri_test);
1506         AST_TEST_REGISTER(get_in_brackets_test);
1507         AST_TEST_REGISTER(get_name_and_number_test);
1508         AST_TEST_REGISTER(sip_parse_uri_fully_test);
1509         AST_TEST_REGISTER(parse_name_andor_addr_test);
1510         AST_TEST_REGISTER(parse_contact_header_test);
1511 }
1512 void sip_request_parser_unregister_tests(void)
1513 {
1514         AST_TEST_UNREGISTER(sip_parse_uri_test);
1515         AST_TEST_UNREGISTER(get_calleridname_test);
1516         AST_TEST_UNREGISTER(get_in_brackets_test);
1517         AST_TEST_UNREGISTER(get_name_and_number_test);
1518         AST_TEST_UNREGISTER(sip_parse_uri_fully_test);
1519         AST_TEST_UNREGISTER(parse_name_andor_addr_test);
1520         AST_TEST_UNREGISTER(parse_contact_header_test);
1521 }