media formats: re-architect handling of media for performance improvements
[asterisk/asterisk.git] / channels / sip / reqresp_parser.c
index b646f7b..88aea07 100644 (file)
  * \brief sip request parsing functions and unit tests
  */
 
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
+
 #include "asterisk.h"
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
@@ -41,6 +45,7 @@ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
        char *endparams = NULL;
        char *c = NULL;
        int error = 0;
+       int teluri_scheme = 0;
 
        /*
         * Initialize requested strings - some functions don't care if parse_uri fails
@@ -75,6 +80,7 @@ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
                for (; !ast_strlen_zero(cur); cur = strsep(&scheme2, ",")) {
                        l = strlen(cur);
                        if (!strncasecmp(uri, cur, l)) {
+                               teluri_scheme = !strncasecmp(uri, "tel:", 4);   /* TEL URI */
                                uri += l;
                                break;
                        }
@@ -89,6 +95,42 @@ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
                /* if we don't want to split around hostport, keep everything as a
                 * userinfo - cos thats how old parse_uri operated*/
                userinfo = uri;
+       } else if (teluri_scheme) {
+               /*
+                * tel: TEL URI INVITE RFC 3966 patch
+                * See http://www.ietf.org/rfc/rfc3966.txt
+                *
+                * Once the full RFC 3966 parsing is implemented,
+                * the ext= or isub= parameters would be extracted from userinfo.
+                * When this kind of subaddressing would be implemented, the userinfo must be further parsed.
+                * Those parameters would be used for ISDN or PSTN local extensions.
+                *
+                * Current restrictions:
+                * We currently consider the ";isub=" or the ";ext=" as part of the userinfo (unparsed).
+                */
+
+               if ((c = strstr(uri, ";phone-context="))) {
+                       /*
+                        * Local number with context or domain.
+                        * ext= or isub= TEL URI parameters should be upfront.
+                        * All other parameters should come after the ";phone-context=" parameter.
+                        * If other parameters would occur before ";phone-context=" they will be ignored.
+                        */
+
+                        *c = '\0';
+                        userinfo = uri;
+                        uri = c + 15;
+                       *hostport = uri;
+                } else if ('+' == uri[0]) {
+                       /* Global number without context or domain; possibly followed by RFC 3966 and optional other parameters. */
+
+                        userinfo = uri;
+                       *hostport = uri;
+               } else {
+                       ast_debug(1, "No RFC 3966 global number or context found in '%s'; returning local number anyway\n", uri);
+                        userinfo = uri;                /* Return local number anyway */
+                       error = -1;
+               }
        } else {
                char *dom = "";
                if ((c = strchr(uri, '@'))) {
@@ -217,7 +259,7 @@ int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
 }
 
 
-AST_TEST_DEFINE(sip_parse_uri_fully_test)
+AST_TEST_DEFINE(sip_parse_uri_full_test)
 {
        int res = AST_TEST_PASS;
        char uri[1024];
@@ -227,12 +269,6 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        struct testdata {
                char *desc;
                char *uri;
-               char **userptr;
-               char **passptr;
-               char **hostportptr;
-               char **headersptr;
-               char **residueptr;
-               struct uriparams *paramsptr;
                char *user;
                char *pass;
                char *hostport;
@@ -250,12 +286,6 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        struct testdata td1 = {
                .desc = "no headers",
                .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=residue",
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .user = "user",
                .pass = "secret",
                .hostport = "host:5060",
@@ -269,12 +299,6 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        struct testdata td2 = {
                .desc = "with headers",
                .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=discard2?header=blah&header2=blah2;param3=residue",
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .user = "user",
                .pass = "secret",
                .hostport = "host:5060",
@@ -288,12 +312,6 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        struct testdata td3 = {
                .desc = "difficult user",
                .uri = "sip:-_.!~*'()&=+$,;?/:secret@host:5060;transport=tcp",
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .user = "-_.!~*'()&=+$,;?/",
                .pass = "secret",
                .hostport = "host:5060",
@@ -307,12 +325,6 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        struct testdata td4 = {
                .desc = "difficult pass",
                .uri = "sip:user:-_.!~*'()&=+$,@host:5060;transport=tcp",
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .user = "user",
                .pass = "-_.!~*'()&=+$,",
                .hostport = "host:5060",
@@ -326,12 +338,6 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        struct testdata td5 = {
                .desc = "difficult host",
                .uri = "sip:user:secret@1-1.a-1.:5060;transport=tcp",
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .user = "user",
                .pass = "secret",
                .hostport = "1-1.a-1.:5060",
@@ -345,12 +351,6 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        struct testdata td6 = {
                .desc = "difficult params near transport",
                .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$;transport=tcp",
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .user = "user",
                .pass = "secret",
                .hostport = "host:5060",
@@ -364,12 +364,6 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        struct testdata td7 = {
                .desc = "difficult params near headers",
                .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$?header=blah&header2=blah2;-_.!~*'()[]/:&+$=residue",
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .user = "user",
                .pass = "secret",
                .hostport = "host:5060",
@@ -383,12 +377,6 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        struct testdata td8 = {
                .desc = "lr parameter",
                .uri = "sip:user:secret@host:5060;param=discard;lr?header=blah",
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .user = "user",
                .pass = "secret",
                .hostport = "host:5060",
@@ -402,12 +390,6 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        struct testdata td9 = {
                .desc = "alternative lr parameter",
                .uri = "sip:user:secret@host:5060;param=discard;lr=yes?header=blah",
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .user = "user",
                .pass = "secret",
                .hostport = "host:5060",
@@ -421,12 +403,6 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        struct testdata td10 = {
                .desc = "no lr parameter",
                .uri = "sip:user:secret@host:5060;paramlr=lr;lr=no;lr=off;lr=0;lr=;=lr;lrextra;lrparam2=lr?header=blah",
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .user = "user",
                .pass = "secret",
                .hostport = "host:5060",
@@ -437,6 +413,51 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
                .params.user = ""
        };
 
+       /* RFC 3966 TEL URI INVITE */
+       struct testdata td11 = {
+               .desc = "tel local number",
+               .uri = "tel:0987654321;phone-context=+32987654321",
+               .user = "0987654321",
+               .pass = "",
+               .hostport = "+32987654321",
+               .headers = "",
+               .residue = "",
+               .params.transport = "",
+               .params.lr = 0,
+               .params.user = ""
+       };
+
+       struct testdata td12 = {
+               .desc = "tel global number",
+               .uri = "tel:+32987654321",
+               .user = "+32987654321",
+               .pass = "",
+               .hostport = "+32987654321",
+               .headers = "",
+               .residue = "",
+               .params.transport = "",
+               .params.lr = 0,
+               .params.user = ""
+       };
+
+       /*
+        * Once the full RFC 3966 parsing is implemented,
+        * only the ext= or isub= parameters would be extracted from .user
+        * Then the ;param=discard would be ignored,
+        * and the .user would only contain "0987654321"
+        */
+       struct testdata td13 = {
+               .desc = "tel local number",
+               .uri = "tel:0987654321;ext=1234;param=discard;phone-context=+32987654321;transport=udp;param2=discard2?header=blah&header2=blah2;param3=residue",
+               .user = "0987654321;ext=1234;param=discard",
+               .pass = "",
+               .hostport = "+32987654321",
+               .headers = "header=blah&header2=blah2",
+               .residue = "param3=residue",
+               .params.transport = "udp",
+               .params.lr = 0,
+               .params.user = ""
+       };
 
        AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
        AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
@@ -448,7 +469,9 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
        AST_LIST_INSERT_TAIL(&testdatalist, &td8, list);
        AST_LIST_INSERT_TAIL(&testdatalist, &td9, list);
        AST_LIST_INSERT_TAIL(&testdatalist, &td10, list);
-
+       AST_LIST_INSERT_TAIL(&testdatalist, &td11, list);
+       AST_LIST_INSERT_TAIL(&testdatalist, &td12, list);
+       AST_LIST_INSERT_TAIL(&testdatalist, &td13, list);
 
        switch (cmd) {
        case TEST_INIT:
@@ -469,19 +492,19 @@ AST_TEST_DEFINE(sip_parse_uri_fully_test)
                params.lr = 0;
 
                ast_copy_string(uri,testdataptr->uri,sizeof(uri));
-               if (parse_uri_full(uri, "sip:,sips:", testdataptr->userptr,
-                                  testdataptr->passptr, testdataptr->hostportptr,
-                                  testdataptr->paramsptr,
-                                  testdataptr->headersptr,
-                                  testdataptr->residueptr) ||
-                       ((testdataptr->userptr) && strcmp(testdataptr->user, user)) ||
-                       ((testdataptr->passptr) && strcmp(testdataptr->pass, pass)) ||
-                       ((testdataptr->hostportptr) && strcmp(testdataptr->hostport, hostport)) ||
-                       ((testdataptr->headersptr) && strcmp(testdataptr->headers, headers)) ||
-                       ((testdataptr->residueptr) && strcmp(testdataptr->residue, residue)) ||
-                       ((testdataptr->paramsptr) && strcmp(testdataptr->params.transport,params.transport)) ||
-                       ((testdataptr->paramsptr) && (testdataptr->params.lr != params.lr)) ||
-                       ((testdataptr->paramsptr) && strcmp(testdataptr->params.user,params.user))
+               if (parse_uri_full(uri, "sip:,sips:,tel:", &user,
+                                  &pass, &hostport,
+                                  &params,
+                                  &headers,
+                                  &residue) ||
+                       (user && strcmp(testdataptr->user, user)) ||
+                       (pass && strcmp(testdataptr->pass, pass)) ||
+                       (hostport && strcmp(testdataptr->hostport, hostport)) ||
+                       (headers && strcmp(testdataptr->headers, headers)) ||
+                       (residue && strcmp(testdataptr->residue, residue)) ||
+                       (strcmp(testdataptr->params.transport,params.transport)) ||
+                       (testdataptr->params.lr != params.lr) ||
+                       (strcmp(testdataptr->params.user,params.user))
                ) {
                                ast_test_status_update(test, "Sub-Test: %s, failed.\n", testdataptr->desc);
                                res = AST_TEST_FAIL;
@@ -522,6 +545,7 @@ AST_TEST_DEFINE(sip_parse_uri_test)
        char uri9[] = "sip:host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
        char uri10[] = "host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
        char uri11[] = "host";
+       char uri12[] = "tel:911";       /* TEL URI Local number without context or global number */
 
        switch (cmd) {
        case TEST_INIT:
@@ -650,6 +674,17 @@ AST_TEST_DEFINE(sip_parse_uri_test)
                res = AST_TEST_FAIL;
        }
 
+       /* Test 12, simple URI */
+       name = pass = hostport = transport = NULL;
+       if (!parse_uri(uri12, "sip:,sips:,tel:", &name, &pass, &hostport, &transport) ||
+                       strcmp(name, "911")      ||     /* We return local number anyway */
+                       !ast_strlen_zero(pass)      ||
+                       !ast_strlen_zero(hostport)      ||      /* No global number nor context */
+                       !ast_strlen_zero(transport)) {
+               ast_test_status_update(test, "Test 12: TEL URI INVITE failed.\n");
+               res = AST_TEST_FAIL;
+       }
+
        return res;
 }
 
@@ -868,7 +903,7 @@ AST_TEST_DEFINE(get_calleridname_test)
 int get_name_and_number(const char *hdr, char **name, char **number)
 {
        char header[256];
-       char tmp_name[50];
+       char tmp_name[256];
        char *tmp_number = NULL;
        char *hostport = NULL;
        char *dummy = NULL;
@@ -1010,6 +1045,59 @@ AST_TEST_DEFINE(get_name_and_number_test)
        return res;
 }
 
+int get_in_brackets_const(const char *src,const char **start,int *length)
+{
+       const char *parse = src;
+       const char *first_bracket;
+       const char *second_bracket;
+
+       if (start == NULL) {
+               return -1;
+       }
+       if (length == NULL) {
+               return -1;
+       }
+       *start = NULL;
+       *length = -1;
+       if (ast_strlen_zero(src)) {
+               return 1;
+       }
+
+       /*
+        * Skip any quoted text until we find the part in brackets.
+        * On any error give up and return -1
+        */
+       while ( (first_bracket = strchr(parse, '<')) ) {
+               const char *first_quote = strchr(parse, '"');
+               first_bracket++;
+               if (!first_quote || first_quote >= first_bracket) {
+                       break; /* no need to look at quoted part */
+               }
+               /* the bracket is within quotes, so ignore it */
+               parse = find_closing_quote(first_quote + 1, NULL);
+               if (!*parse) {
+                       ast_log(LOG_WARNING, "No closing quote found in '%s'\n", src);
+                       return  -1;
+               }
+               parse++;
+       }
+
+       /* Require a first bracket.  Unlike get_in_brackets_full, this procedure is passed a const,
+        * so it can expect a pointer to an original value */
+       if (!first_bracket) {
+               ast_log(LOG_WARNING, "No opening bracket found in '%s'\n", src);
+               return 1;
+       }
+
+       if ((second_bracket = strchr(first_bracket, '>'))) {
+               *start = first_bracket;
+               *length = second_bracket - first_bracket;
+               return 0;
+       }
+       ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", src);
+       return -1;
+}
+
 int get_in_brackets_full(char *tmp,char **out,char **residue)
 {
        const char *parse = tmp;
@@ -1145,7 +1233,7 @@ AST_TEST_DEFINE(get_in_brackets_test)
        }
 
        /* Test 6, NULL input  */
-       if ((uri = get_in_brackets(NULL))) {
+       if (get_in_brackets(NULL)) {
                ast_test_status_update(test, "Test 6, NULL input failed.\n");
                res = AST_TEST_FAIL;
        }
@@ -1220,13 +1308,6 @@ AST_TEST_DEFINE(parse_name_andor_addr_test)
        struct testdata {
                char *desc;
                char *uri;
-               char **nameptr;
-               char **userptr;
-               char **passptr;
-               char **hostportptr;
-               char **headersptr;
-               char **residueptr;
-               struct uriparams *paramsptr;
                char *name;
                char *user;
                char *pass;
@@ -1244,13 +1325,6 @@ AST_TEST_DEFINE(parse_name_andor_addr_test)
        struct testdata td1 = {
                .desc = "quotes and brackets",
                .uri = "\"name :@ \" <sip:user:secret@host:5060;param=discard;transport=tcp>;tag=tag",
-               .nameptr = &name,
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .name =  "name :@ ",
                .user = "user",
                .pass = "secret",
@@ -1265,13 +1339,6 @@ AST_TEST_DEFINE(parse_name_andor_addr_test)
        struct testdata td2 = {
                .desc = "no quotes",
                .uri = "givenname familyname <sip:user:secret@host:5060;param=discard;transport=tcp>;expires=3600",
-               .nameptr = &name,
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .name = "givenname familyname",
                .user = "user",
                .pass = "secret",
@@ -1286,13 +1353,6 @@ AST_TEST_DEFINE(parse_name_andor_addr_test)
        struct testdata td3 = {
                .desc = "no brackets",
                .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;q=1",
-               .nameptr = &name,
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .name = "",
                .user = "user",
                .pass = "secret",
@@ -1307,13 +1367,6 @@ AST_TEST_DEFINE(parse_name_andor_addr_test)
        struct testdata td4 = {
                .desc = "just host",
                .uri = "sips:host",
-               .nameptr = &name,
-               .userptr = &user,
-               .passptr = &pass,
-               .hostportptr = &hostport,
-               .headersptr = &headers,
-               .residueptr = &residue,
-               .paramsptr = &params,
                .name = "",
                .user = "",
                .pass = "",
@@ -1351,21 +1404,21 @@ AST_TEST_DEFINE(parse_name_andor_addr_test)
                params.lr = 0;
                ast_copy_string(uri,testdataptr->uri,sizeof(uri));
                if (parse_name_andor_addr(uri, "sip:,sips:",
-                                         testdataptr->nameptr,
-                                         testdataptr->userptr,
-                                         testdataptr->passptr,
-                                         testdataptr->hostportptr,
-                                         testdataptr->paramsptr,
-                                         testdataptr->headersptr,
-                                         testdataptr->residueptr) ||
-                       ((testdataptr->nameptr) && strcmp(testdataptr->name, name)) ||
-                       ((testdataptr->userptr) && strcmp(testdataptr->user, user)) ||
-                       ((testdataptr->passptr) && strcmp(testdataptr->pass, pass)) ||
-                       ((testdataptr->hostportptr) && strcmp(testdataptr->hostport, hostport)) ||
-                       ((testdataptr->headersptr) && strcmp(testdataptr->headers, headers)) ||
-                       ((testdataptr->residueptr) && strcmp(testdataptr->residue, residue)) ||
-                       ((testdataptr->paramsptr) && strcmp(testdataptr->params.transport,params.transport)) ||
-                       ((testdataptr->paramsptr) && strcmp(testdataptr->params.user,params.user))
+                                         &name,
+                                         &user,
+                                         &pass,
+                                         &hostport,
+                                         &params,
+                                         &headers,
+                                         &residue) ||
+                       (name && strcmp(testdataptr->name, name)) ||
+                       (user && strcmp(testdataptr->user, user)) ||
+                       (pass && strcmp(testdataptr->pass, pass)) ||
+                       (hostport && strcmp(testdataptr->hostport, hostport)) ||
+                       (headers && strcmp(testdataptr->headers, headers)) ||
+                       (residue && strcmp(testdataptr->residue, residue)) ||
+                       (strcmp(testdataptr->params.transport,params.transport)) ||
+                       (strcmp(testdataptr->params.user,params.user))
                        ) {
                        ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
                        res = AST_TEST_FAIL;
@@ -1606,6 +1659,10 @@ AST_TEST_DEFINE(parse_contact_header_test)
 
                                contactptr = AST_LIST_NEXT(contactptr,list);
                        }
+
+                       while ((contactptr = AST_LIST_REMOVE_HEAD(contactlistptr,list))) {
+                               ast_free(contactptr);
+                       }
                }
        }
 
@@ -1620,9 +1677,9 @@ AST_TEST_DEFINE(parse_contact_header_test)
  * item is found that is not supported, it is copied to the unsupported
  * out buffer.
  *
- * \param option list
+ * \param options list
  * \param unsupported out buffer (optional)
- * \param unsupported out buffer length (optional)
+ * \param unsupported_len out buffer length (optional)
  */
 unsigned int parse_sip_options(const char *options, char *unsupported, size_t unsupported_len)
 {
@@ -2529,8 +2586,8 @@ AST_TEST_DEFINE(parse_via_test)
 
                if (testdataptr->expected_port && testdataptr->expected_port != via->port) {
                        ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
-                               "parsed port = \"%d\"\n"
-                               "expected = \"%d\"\n"
+                               "parsed port = \"%u\"\n"
+                               "expected = \"%u\"\n"
                                "failed to parse port\n",
                        i, testdataptr->in, via->port, testdataptr->expected_port);
                        res = AST_TEST_FAIL;
@@ -2579,7 +2636,7 @@ void sip_request_parser_register_tests(void)
        AST_TEST_REGISTER(sip_parse_uri_test);
        AST_TEST_REGISTER(get_in_brackets_test);
        AST_TEST_REGISTER(get_name_and_number_test);
-       AST_TEST_REGISTER(sip_parse_uri_fully_test);
+       AST_TEST_REGISTER(sip_parse_uri_full_test);
        AST_TEST_REGISTER(parse_name_andor_addr_test);
        AST_TEST_REGISTER(parse_contact_header_test);
        AST_TEST_REGISTER(sip_parse_options_test);
@@ -2592,7 +2649,7 @@ void sip_request_parser_unregister_tests(void)
        AST_TEST_UNREGISTER(get_calleridname_test);
        AST_TEST_UNREGISTER(get_in_brackets_test);
        AST_TEST_UNREGISTER(get_name_and_number_test);
-       AST_TEST_UNREGISTER(sip_parse_uri_fully_test);
+       AST_TEST_UNREGISTER(sip_parse_uri_full_test);
        AST_TEST_UNREGISTER(parse_name_andor_addr_test);
        AST_TEST_UNREGISTER(parse_contact_header_test);
        AST_TEST_UNREGISTER(sip_parse_options_test);