Newer
Older
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2010, Digium, Inc.
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \brief sip request parsing functions and unit tests
*/
<support_level>extended</support_level>
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "include/sip.h"
#include "include/sip_utils.h"
int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
char **hostport, struct uriparams *params, char **headers,
char *userinfo = NULL;
char *parameters = NULL;
char *endparams = NULL;
char *c = NULL;
int teluri_scheme = 0;
/*
* Initialize requested strings - some functions don't care if parse_uri fails
* and will attempt to use string pointers passed into parse_uri even after a
* parse_uri failure
*/
if (user) {
*user = "";
}
if (pass) {
*pass = "";
}
if (hostport) {
*hostport = "";
}
if (headers) {
*headers = "";
}
if (residue) {
*residue = "";
}
/* check for valid input */
if (ast_strlen_zero(uri)) {
return -1;
}
if (scheme) {
int l;
char *scheme2 = ast_strdupa(scheme);
char *cur = strsep(&scheme2, ",");
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;
}
}
if (ast_strlen_zero(cur)) {
ast_debug(1, "No supported scheme found in '%s' using the scheme[s] %s\n", uri, scheme);
error = -1;
}
}
if (!hostport) {
/* if we don't want to split around hostport, keep everything as a
* userinfo - cos thats how old parse_uri operated*/
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
} 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;
}
if ((c = strchr(uri, '@'))) {
*c++ = '\0';
uri = c; /* userinfo can contain ? and ; chars so step forward before looking for params and headers */
} else {
/* domain-only URI, according to the SIP RFC. */
if (pass && (c = strchr(userinfo, ':'))) { /* user:password */
*c++ = '\0';
*pass = c;
} else if (pass) {
*pass = "";
}
if (user) {
*user = userinfo;
}
parameters = uri;
/* strip [?headers] from end of uri - even if no header pointer exists*/
if ((c = strrchr(uri, '?'))) {
*c++ = '\0';
uri = c;
if (headers) {
*headers = c;
if ((c = strrchr(uri, ';'))) {
*c++ = '\0';
} else {
c = strrchr(uri, '\0');
}
uri = c; /* residue */
} else if (headers) {
*headers = "";
}
/* parse parameters */
endparams = strchr(parameters,'\0');
if ((c = strchr(parameters, ';'))) {
*c++ = '\0';
parameters = c;
} else {
parameters = endparams;
if (params) {
char *rem = parameters; /* unparsed or unrecognised remainder */
char *label;
char *value;
int lr = 0;
params->transport = "";
params->user = "";
params->method = "";
params->ttl = "";
params->maddr = "";
params->lr = 0;
rem = parameters;
while ((value = strchr(parameters, '=')) || (lr = !strncmp(parameters, "lr", 2))) {
/* The while condition will not continue evaluation to set lr if it matches "lr=" */
if (lr) {
value = parameters;
} else {
*value++ = '\0';
}
label = parameters;
if ((c = strchr(value, ';'))) {
*c++ = '\0';
parameters = c;
} else {
parameters = endparams;
if (!strcmp(label, "transport")) {
params->transport = value;
rem = parameters;
} else if (!strcmp(label, "user")) {
params->user = value;
rem = parameters;
} else if (!strcmp(label, "method")) {
params->method = value;
rem = parameters;
} else if (!strcmp(label, "ttl")) {
params->ttl = value;
rem = parameters;
} else if (!strcmp(label, "maddr")) {
params->maddr = value;
rem = parameters;
/* 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 */
} else if ((!strcmp(label, "lr") && strcmp(value, "no") && strcmp(value, "off") && strcmp(value, "0") && strcmp(value, "")) || ((lr) && strcmp(value, "lr"))) {
rem = parameters;
} else {
value--;
*value = '=';
if (c) {
c--;
*c = ';';
}
}
}
if (rem > uri) { /* no headers */
uri = rem;
}
}
if (residue) {
*residue = uri;
#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(sip_parse_uri_full_test)
{
int res = AST_TEST_PASS;
char uri[1024];
char *user, *pass, *hostport, *headers, *residue;
struct uriparams params;
struct testdata {
char *desc;
char *uri;
char *user;
char *pass;
char *headers;
char *residue;
struct uriparams params;
AST_LIST_ENTRY(testdata) list;
};
struct testdata *testdataptr;
static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
struct testdata td1 = {
.desc = "no headers",
.uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=residue",
.user = "user",
.pass = "secret",
.params.transport = "tcp",
.params.lr = 0,
.params.user = ""
};
struct testdata td2 = {
.desc = "with headers",
.uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=discard2?header=blah&header2=blah2;param3=residue",
.user = "user",
.pass = "secret",
.headers = "header=blah&header2=blah2",
.residue = "param3=residue",
.params.transport = "tcp",
.params.lr = 0,
.params.user = ""
};
struct testdata td3 = {
.desc = "difficult user",
.uri = "sip:-_.!~*'()&=+$,;?/:secret@host:5060;transport=tcp",
.user = "-_.!~*'()&=+$,;?/",
.pass = "secret",
.headers = "",
.residue = "",
.params.transport = "tcp",
.params.lr = 0,
.params.user = ""
};
struct testdata td4 = {
.desc = "difficult pass",
.uri = "sip:user:-_.!~*'()&=+$,@host:5060;transport=tcp",
.user = "user",
.pass = "-_.!~*'()&=+$,",
.headers = "",
.residue = "",
.params.transport = "tcp",
.params.lr = 0,
.params.user = ""
};
struct testdata td5 = {
.desc = "difficult host",
.uri = "sip:user:secret@1-1.a-1.:5060;transport=tcp",
.user = "user",
.pass = "secret",
.headers = "",
.residue = "",
.params.transport = "tcp",
.params.lr = 0,
.params.user = ""
};
struct testdata td6 = {
.desc = "difficult params near transport",
.uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$;transport=tcp",
.user = "user",
.pass = "secret",
.headers = "",
.residue = "",
.params.transport = "tcp",
.params.lr = 0,
.params.user = ""
};
struct testdata td7 = {
.desc = "difficult params near headers",
.uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$?header=blah&header2=blah2;-_.!~*'()[]/:&+$=residue",
.user = "user",
.pass = "secret",
.headers = "header=blah&header2=blah2",
.residue = "-_.!~*'()[]/:&+$=residue",
.params.transport = "",
.params.lr = 0,
.params.user = ""
};
struct testdata td8 = {
.desc = "lr parameter",
.uri = "sip:user:secret@host:5060;param=discard;lr?header=blah",
.user = "user",
.pass = "secret",
.headers = "header=blah",
.residue = "",
.params.transport = "",
.params.lr = 1,
.params.user = ""
};
struct testdata td9 = {
.desc = "alternative lr parameter",
.uri = "sip:user:secret@host:5060;param=discard;lr=yes?header=blah",
.user = "user",
.pass = "secret",
.headers = "header=blah",
.residue = "",
.params.transport = "",
.params.lr = 1,
.params.user = ""
};
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",
.user = "user",
.pass = "secret",
.headers = "header=blah",
.residue = "",
.params.transport = "",
.params.lr = 0,
.params.user = ""
};
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
/* 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);
AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
AST_LIST_INSERT_TAIL(&testdatalist, &td4, list);
AST_LIST_INSERT_TAIL(&testdatalist, &td5, list);
AST_LIST_INSERT_TAIL(&testdatalist, &td6, list);
AST_LIST_INSERT_TAIL(&testdatalist, &td7, list);
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:
info->name = "sip_uri_full_parse_test";
Tilghman Lesher
committed
info->category = "/channels/chan_sip/";
info->summary = "tests sip full uri parsing";
info->description =
"Tests full parsing of various URIs "
"Verifies output matches expected behavior.";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
user = pass = hostport = headers = residue = NULL;
params.transport = params.user = params.method = params.ttl = params.maddr = NULL;
params.lr = 0;
ast_copy_string(uri,testdataptr->uri,sizeof(uri));
if (parse_uri_full(uri, "sip:,sips:,tel:", &user,
&pass, &hostport,
¶ms,
&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;
}
}
return res;
}
int parse_uri(char *uri, const char *scheme, char **user, char **pass,
char **hostport, char **transport) {
int ret;
char *headers;
struct uriparams params;
headers = NULL;
ret = parse_uri_full(uri, scheme, user, pass, hostport, ¶ms, &headers, NULL);
if (transport) {
*transport=params.transport;
}
return ret;
}
#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(sip_parse_uri_test)
{
int res = AST_TEST_PASS;
char *name, *pass, *hostport, *transport;
char uri1[] = "sip:name@host";
char uri2[] = "sip:name@host;transport=tcp";
char uri3[] = "sip:name:secret@host;transport=tcp";
char uri4[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
/* test 5 is for NULL input */
char uri6[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
char uri7[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
char uri8[] = "sip:host";
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:
info->name = "sip_uri_parse_test";
Tilghman Lesher
committed
info->category = "/channels/chan_sip/";
info->summary = "tests sip uri parsing";
info->description =
David Vossel
committed
"Tests parsing of various URIs "
"Verifies output matches expected behavior.";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
/* Test 1, simple URI */
name = pass = hostport = transport = NULL;
if (parse_uri(uri1, "sip:,sips:", &name, &pass, &hostport, &transport) ||
strcmp(name, "name") ||
!ast_strlen_zero(pass) ||
!ast_strlen_zero(transport)) {
ast_test_status_update(test, "Test 1: simple uri failed. \n");
res = AST_TEST_FAIL;
}
/* Test 2, add tcp transport */
name = pass = hostport = transport = NULL;
if (parse_uri(uri2, "sip:,sips:", &name, &pass, &hostport, &transport) ||
strcmp(name, "name") ||
!ast_strlen_zero(pass) ||
strcmp(transport, "tcp")) {
ast_test_status_update(test, "Test 2: uri with addtion of tcp transport failed. \n");
res = AST_TEST_FAIL;
}
/* Test 3, add secret */
name = pass = hostport = transport = NULL;
if (parse_uri(uri3, "sip:,sips:", &name, &pass, &hostport, &transport) ||
strcmp(name, "name") ||
strcmp(pass, "secret") ||
strcmp(transport, "tcp")) {
ast_test_status_update(test, "Test 3: uri with addition of secret failed.\n");
res = AST_TEST_FAIL;
}
/* Test 4, add port and unparsed header field*/
name = pass = hostport = transport = NULL;
if (parse_uri(uri4, "sip:,sips:", &name, &pass, &hostport, &transport) ||
strcmp(name, "name") ||
strcmp(pass, "secret") ||
strcmp(hostport, "host:port") ||
strcmp(transport, "tcp")) {
ast_test_status_update(test, "Test 4: add port and unparsed header field failed.\n");
res = AST_TEST_FAIL;
}
/* Test 5, verify parse_uri does not crash when given a NULL uri */
name = pass = hostport = transport = NULL;
if (!parse_uri(NULL, "sip:,sips:", &name, &pass, &hostport, &transport)) {
ast_test_status_update(test, "Test 5: passing a NULL uri failed.\n");
res = AST_TEST_FAIL;
}
/* Test 6, verify parse_uri does not crash when given a NULL output parameters */
name = pass = hostport = transport = NULL;
if (parse_uri(uri6, "sip:,sips:", NULL, NULL, NULL, NULL)) {
ast_test_status_update(test, "Test 6: passing NULL output parameters failed.\n");
/* Test 7, verify parse_uri returns user:secret and hostport when no port or secret output parameters are supplied. */
name = pass = hostport = transport = NULL;
if (parse_uri(uri7, "sip:,sips:", &name, NULL, &hostport, NULL) ||
strcmp(name, "name:secret") ||
strcmp(hostport, "host:port")) {
ast_test_status_update(test, "Test 7: providing no port and secret output parameters failed.\n");
res = AST_TEST_FAIL;
}
/* Test 8, verify parse_uri can handle a hostport only uri */
name = pass = hostport = transport = NULL;
if (parse_uri(uri8, "sip:,sips:", &name, &pass, &hostport, &transport) ||
strcmp(hostport, "host") ||
!ast_strlen_zero(name)) {
ast_test_status_update(test, "Test 8: add port and unparsed header field failed.\n");
res = AST_TEST_FAIL;
}
/* Test 9, add port and unparsed header field with hostport only uri*/
name = pass = hostport = transport = NULL;
if (parse_uri(uri9, "sip:,sips:", &name, &pass, &hostport, &transport) ||
!ast_strlen_zero(name) ||
!ast_strlen_zero(pass) ||
strcmp(hostport, "host:port") ||
ast_test_status_update(test, "Test 9: hostport only uri failed \n");
res = AST_TEST_FAIL;
}
/* Test 10, handle invalid/missing "sip:,sips:" scheme
* we expect parse_uri to return an error, but still parse
* the results correctly here */
name = pass = hostport = transport = NULL;
if (!parse_uri(uri10, "sip:,sips:", &name, &pass, &hostport, &transport) ||
!ast_strlen_zero(name) ||
!ast_strlen_zero(pass) ||
strcmp(hostport, "host:port") ||
strcmp(transport, "tcp")) {
ast_test_status_update(test, "Test 10: missing \"sip:sips:\" scheme failed\n");
res = AST_TEST_FAIL;
}
/* Test 11, simple hostport only URI with missing scheme
* we expect parse_uri to return an error, but still parse
* the results correctly here */
name = pass = hostport = transport = NULL;
if (!parse_uri(uri11, "sip:,sips:", &name, &pass, &hostport, &transport) ||
!ast_strlen_zero(name) ||
!ast_strlen_zero(pass) ||
!ast_strlen_zero(transport)) {
ast_test_status_update(test, "Test 11: simple uri with missing scheme failed. \n");
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;
}
/*! \brief Get caller id name from SIP headers, copy into output buffer
*
* \retval input string pointer placed after display-name field if possible
*/
const char *get_calleridname(const char *input, char *output, size_t outputsize)
{
/* From RFC3261:
* From = ( "From" / "f" ) HCOLON from-spec
* from-spec = ( name-addr / addr-spec ) *( SEMI from-param )
* name-addr = [ display-name ] LAQUOT addr-spec RAQUOT
* display-name = *(token LWS)/ quoted-string
* token = 1*(alphanum / "-" / "." / "!" / "%" / "*"
* / "_" / "+" / "`" / "'" / "~" )
* quoted-string = SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE
* qdtext = LWS / %x21 / %x23-5B / %x5D-7E
* / UTF8-NONASCII
* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F)
*
* HCOLON = *WSP ":" SWS
* SWS = [LWS]
* LWS = *[*WSP CRLF] 1*WSP
* WSP = (SP / HTAB)
*
* Deviations from it:
* - following CRLF's in LWS is not done (here at least)
* - ascii NUL is never legal as it terminates the C-string
* - utf8-nonascii is not checked for validity
*/
char *orig_output = output;
const char *orig_input = input;
if (!output || !outputsize) {
/* Bad output parameters. Should never happen. */
return input;
}
/* clear any empty characters in the beginning */
input = ast_skip_blanks(input);
/* make sure the output buffer is initilized */
*orig_output = '\0';
/* make room for '\0' at the end of the output buffer */
--outputsize;
/* no data at all or no display name? */
if (!input || *input == '<') {
return input;
}
/* quoted-string rules */
if (input[0] == '"') {
input++; /* skip the first " */
for (; *input; ++input) {
if (*input == '"') { /* end of quoted-string */
break;
} else if (*input == 0x5c) { /* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) */
++input;
if (!*input) {
break;
}
if ((unsigned char) *input > 0x7f || *input == 0xa || *input == 0xd) {
continue; /* not a valid quoted-pair, so skip it */
}
} else if ((*input != 0x9 && (unsigned char) *input < 0x20)
|| *input == 0x7f) {
continue; /* skip this invalid character. */
}
if (0 < outputsize) {
/* We still have room for the output display-name. */
*output++ = *input;
--outputsize;
}
}
/* if this is successful, input should be at the ending quote */
if (*input != '"') {
ast_log(LOG_WARNING, "No ending quote for display-name was found\n");
*orig_output = '\0';
return orig_input;
}
/* make sure input is past the last quote */
/* terminate output */
*output = '\0';
} else { /* either an addr-spec or tokenLWS-combo */
for (; *input; ++input) {
/* token or WSP (without LWS) */
if ((*input >= '0' && *input <= '9') || (*input >= 'A' && *input <= 'Z')
|| (*input >= 'a' && *input <= 'z') || *input == '-' || *input == '.'
|| *input == '!' || *input == '%' || *input == '*' || *input == '_'
|| *input == '+' || *input == '`' || *input == '\'' || *input == '~'
|| *input == 0x9 || *input == ' ') {
if (0 < outputsize) {
/* We still have room for the output display-name. */
*output++ = *input;
--outputsize;
}
} else if (*input == '<') { /* end of tokenLWS-combo */
/* we could assert that the previous char is LWS, but we don't care */
break;
} else if (*input == ':') {
/* This invalid character which indicates this is addr-spec rather than display-name. */
*orig_output = '\0';
return orig_input;
} else { /* else, invalid character we can skip. */
continue; /* skip this character */
}
}
if (*input != '<') { /* if we never found the start of addr-spec then this is invalid */
*orig_output = '\0';
return orig_input;
}
/* terminate output while trimming any trailing whitespace */
} while (orig_output <= output && (*output == 0x9 || *output == ' '));
#ifdef TEST_FRAMEWORK
AST_TEST_DEFINE(get_calleridname_test)
{
int res = AST_TEST_PASS;
const char *in1 = " \" quoted-text internal \\\" quote \"<stuff>";
const char *in2 = " token text with no quotes <stuff>";
const char *overflow1 = " \"quoted-text overflow 1234567890123456789012345678901234567890\" <stuff>";
const char *overflow2 = " non-quoted text overflow 1234567890123456789012345678901234567890 <stuff>";
const char *noendquote = " \"quoted-text no end <stuff>";
const char *addrspec = " sip:blah@blah";
const char *no_quotes_no_brackets = "blah@blah";
const char *after_dname;
char dname[40];
switch (cmd) {
case TEST_INIT:
info->name = "sip_get_calleridname_test";
Tilghman Lesher
committed
info->category = "/channels/chan_sip/";
info->summary = "decodes callerid name from sip header";
info->description = "Decodes display-name field of sip header. Checks for valid output and expected failure cases.";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
/* quoted-text with backslash escaped quote */
after_dname = get_calleridname(in1, dname, sizeof(dname));
ast_test_status_update(test, "display-name1: %s\nafter: %s\n", dname, after_dname);
if (strcmp(dname, " quoted-text internal \" quote ")) {
ast_test_status_update(test, "display-name1 test failed\n");
res = AST_TEST_FAIL;
}
/* token text */
after_dname = get_calleridname(in2, dname, sizeof(dname));
ast_test_status_update(test, "display-name2: %s\nafter: %s\n", dname, after_dname);
ast_test_status_update(test, "display-name2 test failed\n");
res = AST_TEST_FAIL;
}
/* quoted-text buffer overflow */
after_dname = get_calleridname(overflow1, dname, sizeof(dname));
ast_test_status_update(test, "overflow display-name1: %s\nafter: %s\n", dname, after_dname);
if (strcmp(dname, "quoted-text overflow 123456789012345678")) {
ast_test_status_update(test, "overflow display-name1 test failed\n");
/* non-quoted-text buffer overflow */
after_dname = get_calleridname(overflow2, dname, sizeof(dname));
ast_test_status_update(test, "overflow display-name2: %s\nafter: %s\n", dname, after_dname);
if (strcmp(dname, "non-quoted text overflow 12345678901234")) {
ast_test_status_update(test, "overflow display-name2 test failed\n");
res = AST_TEST_FAIL;
}
/* quoted-text buffer with no terminating end quote */
after_dname = get_calleridname(noendquote, dname, sizeof(dname));
ast_test_status_update(test, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname);
ast_test_status_update(test, "no end quote for quoted-text display-name failed\n");
res = AST_TEST_FAIL;
}
/* addr-spec rather than display-name. */
after_dname = get_calleridname(addrspec, dname, sizeof(dname));
ast_test_status_update(test, "addr-spec display-name1: %s\nafter: %s\n", dname, after_dname);
ast_test_status_update(test, "detection of addr-spec failed\n");
/* no quotes, no brackets */
after_dname = get_calleridname(no_quotes_no_brackets, dname, sizeof(dname));
ast_test_status_update(test, "no_quotes_no_brackets display-name1: %s\nafter: %s\n", dname, after_dname);
if (*dname != '\0' && after_dname != no_quotes_no_brackets) {
ast_test_status_update(test, "detection of addr-spec failed\n");
res = AST_TEST_FAIL;
}
int get_name_and_number(const char *hdr, char **name, char **number)
{
char header[256];
char tmp_name[256];
char *tmp_number = 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, &hostport, 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, ast_uri_sip_user);
/* name is optional and may not be present at this point */
if (!ast_strlen_zero(tmp_name)) {
*name = ast_strdup(tmp_name);
}
return 0;
}
#ifdef TEST_FRAMEWORK
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";
Tilghman Lesher
committed
info->category = "/channels/chan_sip/";
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
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");