media formats: re-architect handling of media for performance improvements
[asterisk/asterisk.git] / channels / sip / reqresp_parser.c
index 7893aac..88aea07 100644 (file)
@@ -45,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
@@ -79,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;
                        }
@@ -93,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, '@'))) {
@@ -375,6 +413,51 @@ AST_TEST_DEFINE(sip_parse_uri_full_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);
@@ -386,7 +469,9 @@ AST_TEST_DEFINE(sip_parse_uri_full_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:
@@ -407,7 +492,7 @@ AST_TEST_DEFINE(sip_parse_uri_full_test)
                params.lr = 0;
 
                ast_copy_string(uri,testdataptr->uri,sizeof(uri));
-               if (parse_uri_full(uri, "sip:,sips:", &user,
+               if (parse_uri_full(uri, "sip:,sips:,tel:", &user,
                                   &pass, &hostport,
                                   &params,
                                   &headers,
@@ -460,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:
@@ -588,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;
 }
 
@@ -806,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;
@@ -948,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;
@@ -1509,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);
+                       }
                }
        }
 
@@ -2432,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;