diff options
author | dvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-02-15 15:45:02 +0000 |
---|---|---|
committer | dvossel <dvossel@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-02-15 15:45:02 +0000 |
commit | d39a409a7e70847fa440f81efb5a926c2bb161f0 (patch) | |
tree | 72b0e2d48f3f92adf9294911b59271a48180b958 /channels/sip | |
parent | e8b816ba2e4bd13fc7731c0adfd62cf34367e374 (diff) |
chan_sip parse code refactoring plus two new unit tests
Code Refactoring Changes
- read_to_parts() moved to reqresp_parser.c and has been renamed as
get_name_and_number()
- get_in_brackets() moved to reqresp_parser.c
- find_closing_quotes() added to sip_utils.h
Logic Changes
- get_name_and_number() now uses parse_uri() and get_calleridname()
for parsing. Before this change only names within quotes were
found, when names not within quotes are possible.
New Unit Tests
-sip_get_name_and_number_test
-sip_get_in_brackets_test
(closes issue #16707)
Reported by: Nick_Lewis
Patches:
issue16706.diff uploaded by dvossel (license 671)
Review: https://reviewboard.asterisk.org/r/499/
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@246627 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels/sip')
-rw-r--r-- | channels/sip/include/reqresp_parser.h | 25 | ||||
-rw-r--r-- | channels/sip/include/sip_utils.h | 7 | ||||
-rw-r--r-- | channels/sip/reqresp_parser.c | 259 |
3 files changed, 291 insertions, 0 deletions
diff --git a/channels/sip/include/reqresp_parser.h b/channels/sip/include/reqresp_parser.h index e724c98dd..d322e7e55 100644 --- a/channels/sip/include/reqresp_parser.h +++ b/channels/sip/include/reqresp_parser.h @@ -52,6 +52,31 @@ int parse_uri(char *uri, const char *scheme, char **ret_name, char **pass, char const char *get_calleridname(const char *input, char *output, size_t outputsize); /*! + * \brief Get name and number from sip header + * + * \note name and number point to malloced memory on return and must be + * freed. If name or number is not found, they will be returned as NULL. + * + * \retval 0 success + * \retval -1 failure + */ +int get_name_and_number(const char *hdr, char **name, char **number); + +/*! \brief Pick out text in brackets from character string + * \return pointer to terminated stripped string + * \param tmp input string that will be modified + * + * Examples: + * \verbatim + * "foo" <bar> valid input, returns bar + * foo returns the whole string + * < "foo ... > returns the string between brackets + * < "foo... bogus (missing closing bracket), returns the whole string + * \endverbatim + */ +char *get_in_brackets(char *tmp); + +/*! * \brief register request parsing tests */ void sip_request_parser_register_tests(void); diff --git a/channels/sip/include/sip_utils.h b/channels/sip/include/sip_utils.h index 86eb81933..549f9392f 100644 --- a/channels/sip/include/sip_utils.h +++ b/channels/sip/include/sip_utils.h @@ -33,4 +33,11 @@ */ unsigned int port_str2int(const char *pt, unsigned int standard); +/*! \brief Locate closing quote in a string, skipping escaped quotes. + * optionally with a limit on the search. + * start must be past the first quote. + */ +const char *find_closing_quote(const char *start, const char *lim); + + #endif diff --git a/channels/sip/reqresp_parser.c b/channels/sip/reqresp_parser.c index 803286c6b..df2138596 100644 --- a/channels/sip/reqresp_parser.c +++ b/channels/sip/reqresp_parser.c @@ -24,6 +24,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "include/sip.h" +#include "include/sip_utils.h" #include "include/reqresp_parser.h" /*! \brief * parses a URI in its components.*/ @@ -389,14 +390,272 @@ AST_TEST_DEFINE(get_calleridname_test) return res; } +int get_name_and_number(const char *hdr, char **name, char **number) +{ + char header[256]; + char tmp_name[50] = { 0, }; + char *tmp_number = NULL; + char *domain = NULL; + char *dummy = NULL; + + if (!name || !number || ast_strlen_zero(hdr)) { + return -1; + } + + *number = NULL; + *name = NULL; + ast_copy_string(header, hdr, sizeof(header)); + + /* strip the display-name portion off the beginning of the header. */ + get_calleridname(header, tmp_name, sizeof(tmp_name)); + + /* get uri within < > brackets */ + tmp_number = get_in_brackets(header); + + /* parse out the number here */ + if (parse_uri(tmp_number, "sip:,sips:", &tmp_number, &dummy, &domain, &dummy, NULL) || ast_strlen_zero(tmp_number)) { + ast_log(LOG_ERROR, "can not parse name and number from sip header.\n"); + return -1; + } + + /* number is not option, and must be present at this point */ + *number = ast_strdup(tmp_number); + ast_uri_decode(*number); + + /* name is optional and may not be present at this point */ + if (!ast_strlen_zero(tmp_name)) { + *name = ast_strdup(tmp_name); + } + + return 0; +} + +AST_TEST_DEFINE(get_name_and_number_test) +{ + int res = AST_TEST_PASS; + char *name = NULL; + char *number = NULL; + const char *in1 = "NAME <sip:NUMBER@place>"; + const char *in2 = "\"NA><ME\" <sip:NUMBER@place>"; + const char *in3 = "NAME"; + const char *in4 = "<sip:NUMBER@place>"; + const char *in5 = "This is a screwed up string <sip:LOLCLOWNS<sip:>@place>"; + + switch (cmd) { + case TEST_INIT: + info->name = "sip_get_name_and_number_test"; + info->category = "channels/chan_sip/"; + info->summary = "Tests getting name and number from sip header"; + info->description = + "Runs through various test situations in which a name and " + "and number can be retrieved from a sip header."; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + /* Test 1. get name and number */ + number = name = NULL; + if ((get_name_and_number(in1, &name, &number)) || + strcmp(name, "NAME") || + strcmp(number, "NUMBER")) { + + ast_test_status_update(test, "Test 1, simple get name and number failed.\n"); + res = AST_TEST_FAIL; + } + ast_free(name); + ast_free(number); + + /* Test 2. get quoted name and number */ + number = name = NULL; + if ((get_name_and_number(in2, &name, &number)) || + strcmp(name, "NA><ME") || + strcmp(number, "NUMBER")) { + + ast_test_status_update(test, "Test 2, get quoted name and number failed.\n"); + res = AST_TEST_FAIL; + } + ast_free(name); + ast_free(number); + + /* Test 3. name only */ + number = name = NULL; + if (!(get_name_and_number(in3, &name, &number))) { + + ast_test_status_update(test, "Test 3, get name only was expected to fail but did not.\n"); + res = AST_TEST_FAIL; + } + ast_free(name); + ast_free(number); + + /* Test 4. number only */ + number = name = NULL; + if ((get_name_and_number(in4, &name, &number)) || + !ast_strlen_zero(name) || + strcmp(number, "NUMBER")) { + + ast_test_status_update(test, "Test 4, get number with no name present failed.\n"); + res = AST_TEST_FAIL; + } + ast_free(name); + ast_free(number); + + /* Test 5. malformed string, since number can not be parsed, this should return an error. */ + number = name = NULL; + if (!(get_name_and_number(in5, &name, &number)) || + !ast_strlen_zero(name) || + !ast_strlen_zero(number)) { + + ast_test_status_update(test, "Test 5, processing malformed string failed.\n"); + res = AST_TEST_FAIL; + } + ast_free(name); + ast_free(number); + + /* Test 6. NULL output parameters */ + number = name = NULL; + if (!(get_name_and_number(in5, NULL, NULL))) { + + ast_test_status_update(test, "Test 6, NULL output parameters failed.\n"); + res = AST_TEST_FAIL; + } + + /* Test 7. NULL input parameter */ + number = name = NULL; + if (!(get_name_and_number(NULL, &name, &number)) || + !ast_strlen_zero(name) || + !ast_strlen_zero(number)) { + + ast_test_status_update(test, "Test 7, NULL input parameter failed.\n"); + res = AST_TEST_FAIL; + } + ast_free(name); + ast_free(number); + + return res; +} + +char *get_in_brackets(char *tmp) +{ + const char *parse = tmp; + char *first_bracket; + + + if (ast_strlen_zero(tmp)) { + return tmp; + } + + /* + * Skip any quoted text until we find the part in brackets. + * On any error give up and return the full string. + */ + while ( (first_bracket = strchr(parse, '<')) ) { + char *first_quote = strchr(parse, '"'); + + 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", tmp); + break; + } + parse++; + } + if (first_bracket) { + char *second_bracket = strchr(first_bracket + 1, '>'); + if (second_bracket) { + *second_bracket = '\0'; + tmp = first_bracket + 1; + } else { + ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp); + } + } + + return tmp; +} + +AST_TEST_DEFINE(get_in_brackets_test) +{ + int res = AST_TEST_PASS; + char *in_brackets = "<sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>"; + char no_name[] = "<sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>"; + char quoted_string[] = "\"I'm a quote stri><ng\" <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>"; + char missing_end_quote[] = "\"I'm a quote string <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>"; + char name_no_quotes[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>"; + char no_end_bracket[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah"; + char *uri = NULL; + + switch (cmd) { + case TEST_INIT: + info->name = "sip_get_in_brackets_test"; + info->category = "channels/chan_sip/"; + info->summary = "Tests getting a sip uri in <> brackets within a sip header."; + info->description = + "Runs through various test situations in which a sip uri " + "in angle brackets needs to be retrieved"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + /* Test 1, simple get in brackets */ + if (!(uri = get_in_brackets(no_name)) || !(strcmp(uri, in_brackets))) { + + ast_test_status_update(test, "Test 1, simple get in brackets failed.\n"); + res = AST_TEST_FAIL; + } + + /* Test 2, starts with quoted string */ + if (!(uri = get_in_brackets(quoted_string)) || !(strcmp(uri, in_brackets))) { + + ast_test_status_update(test, "Test 2, get in brackets with quoted string in front failed.\n"); + res = AST_TEST_FAIL; + } + + /* Test 3, missing end quote */ + if (!(uri = get_in_brackets(missing_end_quote)) || !(strcmp(uri, in_brackets))) { + + ast_test_status_update(test, "Test 3, missing end quote failed.\n"); + res = AST_TEST_FAIL; + } + + /* Test 4, starts with a name not in quotes */ + if (!(uri = get_in_brackets(name_no_quotes)) || !(strcmp(uri, in_brackets))) { + + ast_test_status_update(test, "Test 4, passing name not in quotes failed.\n"); + res = AST_TEST_FAIL; + } + + /* Test 5, no end bracket, should just return everything after the first '<' */ + if (!(uri = get_in_brackets(no_end_bracket)) || !(strcmp(uri, in_brackets))) { + + ast_test_status_update(test, "Test 5, no end bracket failed.\n"); + res = AST_TEST_FAIL; + } + + /* Test 6, NULL input */ + if ((uri = get_in_brackets(NULL))) { + + ast_test_status_update(test, "Test 6, NULL input failed.\n"); + res = AST_TEST_FAIL; + } + + + return res; +} void sip_request_parser_register_tests(void) { AST_TEST_REGISTER(get_calleridname_test); AST_TEST_REGISTER(sip_parse_uri_test); + AST_TEST_REGISTER(get_in_brackets_test); + AST_TEST_REGISTER(get_name_and_number_test); } void sip_request_parser_unregister_tests(void) { AST_TEST_UNREGISTER(sip_parse_uri_test); AST_TEST_UNREGISTER(get_calleridname_test); + AST_TEST_UNREGISTER(get_in_brackets_test); + AST_TEST_UNREGISTER(get_name_and_number_test); } |