Index: channels/chan_sip.c
===================================================================
--- channels/chan_sip.c	(revision 240267)
+++ channels/chan_sip.c	(working copy)
@@ -240,6 +240,7 @@
 #include "asterisk/utils.h"
 #include "asterisk/file.h"
 #include "asterisk/astobj.h"
+#include "asterisk/test.h"
 /*
    Uncomment the define below,  if you are having refcount related memory leaks.
    With this uncommented, this module will generate a file, /tmp/refs, which contains
@@ -2683,7 +2684,7 @@
 static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req);
 static int set_address_from_contact(struct sip_pvt *pvt);
 static void check_via(struct sip_pvt *p, struct sip_request *req);
-static char *get_calleridname(const char *input, char *output, size_t outputsize);
+static const char *get_calleridname(const char *input, char *output, size_t outputsize);
 static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
 static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, char **number, int *reason);
 static int get_destination(struct sip_pvt *p, struct sip_request *oreq);
@@ -14540,51 +14541,168 @@
 	}
 }
 
-/*! \brief  Get caller id name from SIP headers */
-static char *get_calleridname(const char *input, char *output, size_t outputsize)
+/*! \brief  Get caller id name from SIP headers, copy into output buffer
+ *
+ *  \retval input string pointer placed after display-name field if possible
+ */
+static const char *get_calleridname(const char *input, char *output, size_t outputsize)
 {
-	const char *end = strchr(input, '<');	/* first_bracket */
-	const char *tmp = strchr(input, '"');	/* first quote */
-	int bytes = 0;
-	int maxbytes = outputsize - 1;
+	/* 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 (!end || end == input)	/* we require a part in brackets */
-		return NULL;
+	/* clear any empty characters in the beginning */
+	input = ast_skip_blanks(input);
 
-	end--; /* move just before "<" */
+	/* no data at all or no storage room? */
+	if (!input || *input == '<' || !outputsize || !output) {
+		return orig_input;
+	}
 
-	if (tmp && tmp <= end) {
-		/* The quote (tmp) precedes the bracket (end+1).
-		 * Find the matching quote and return the content.
-		 */
-		end = strchr(tmp+1, '"');
-		if (!end)
-			return NULL;
-		bytes = (int) (end - tmp);
-		/* protect the output buffer */
-		if (bytes > maxbytes)
-			bytes = maxbytes;
-		ast_copy_string(output, tmp + 1, bytes);
-	} else {
-		/* No quoted string, or it is inside brackets. */
-		/* clear the empty characters in the begining*/
-		input = ast_skip_blanks(input);
-		/* clear the empty characters in the end */
-		while(*end && *end < 33 && end > input)
-			end--;
-		if (end >= input) {
-			bytes = (int) (end - input) + 2;
-			/* protect the output buffer */
-			if (bytes > maxbytes)
-				bytes = maxbytes;
-			ast_copy_string(output, input, bytes);
-		} else
-			return NULL;
+	/* make sure the output buffer is initilized */
+	*orig_output = '\0';
+
+	/* make room for '\0' at the end of the output buffer */
+	outputsize--;
+
+	/* quoted-string rules */
+	if (input[0] == '"') {
+		input++; /* skip the first " */
+
+		for (;((outputsize > 0) && *input); input++) {
+			if (*input == '"') {  /* end of quoted-string */
+				break;
+			} else if (*input == 0x5c) { /* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) */
+				input++;
+				if (!*input || (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. */
+			}
+
+			*output++ = *input;
+			outputsize--;;
+		}
+
+		/* if this is successful, input should be at the ending quote */
+		if (!input || *input != '"') {
+			ast_log(LOG_WARNING, "No ending quote for display-name was found");
+			*orig_output = '\0';
+			return orig_input;
+		}
+
+		/* make sure input is past the last quote */
+		input++;
+
+		/* terminate outbuf */
+		*output = '\0';
+	} else {  /* either an addr-spec or tokenLWS-combo */
+		for (;((outputsize > 0) && *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 == ' ') {
+				*output++ = *input;
+				outputsize -= 1;
+			} else if (*input == '<') {   /* end of tokenLWS-combo */
+				/* we could assert that the previous char is LWS, but we don't care */
+				break;
+			} else {         /* else, invalid */
+				continue;    /* skip this character */
+			}
+		}
+
+		/* set NULL while trimming trailing whitespace */
+		do {
+			*output-- = '\0';
+		} while (*output == 0x9 || *output == ' '); /* we won't go past orig_output as first was a non-space */
 	}
-	return output;
+
+	return input;
 }
 
+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 *noendquote = " \"quoted-text no end <stuff>";
+	const char *after_dname;
+	char dname[40];
 
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "sip_get_calleridname_test";
+		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(&args->status_update, "display-name1: %s\nafter: %s\n", dname, after_dname);
+	if (strcmp(dname, " quoted-text internal \" quote ")) {
+		ast_test_status_update(&args->status_update, "display-name1 test failed\n");
+		res = AST_TEST_FAIL;
+	}
+
+	/* token text */
+	after_dname = get_calleridname(in2, dname, sizeof(dname));
+	ast_test_status_update(&args->status_update, "display-name2: %s\nafter: %s\n", dname, after_dname);
+	if (strcmp(dname, "token text with no quotes")) {
+		ast_test_status_update(&args->status_update, "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(&args->status_update, "overflow display-name1: %s\nafter: %s\n", dname, after_dname);
+	if (*dname != '\0' && after_dname != overflow1) {
+		ast_test_status_update(&args->status_update, "overflow display-name1 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(&args->status_update, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname);
+	if (*dname != '\0' && after_dname != noendquote) {
+		ast_test_status_update(&args->status_update, "no end quote for quoted-text display-name failed\n");
+		res = AST_TEST_FAIL;
+	}
+	return res;
+}
+
+
 /*! \brief Validate device authentication */
 static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
 	struct sip_request *req, int sipmethod, struct sockaddr_in *sin,
@@ -14757,11 +14875,11 @@
 					      int sipmethod, const char *uri, enum xmittype reliable,
 					      struct sockaddr_in *sin, struct sip_peer **authpeer)
 {
-	char from[256];
+	char from[256] = { 0, };
 	char *dummy;	/* dummy return value for parse_uri */
 	char *domain;	/* dummy return value for parse_uri */
 	char *of;
-	enum check_auth_result res;
+	enum check_auth_result res = AUTH_DONT_KNOW;
 	char calleridname[50];
 	char *uri2 = ast_strdupa(uri);
 
@@ -14770,9 +14888,13 @@
 	ast_copy_string(from, get_header(req, "From"), sizeof(from));
 	/* XXX here tries to map the username for invite things */
 	memset(calleridname, 0, sizeof(calleridname));
-	get_calleridname(from, calleridname, sizeof(calleridname));
 
-	SIP_PEDANTIC_DECODE(calleridname);
+	/* strip the display-name portion off the beginning of the FROM header. */
+	if (!(of = (char *) get_calleridname(from, calleridname, sizeof(calleridname)))) {
+		ast_log(LOG_ERROR, "FROM header can not be parsed \n");
+		return res;
+	}
+
 	if (calleridname[0])
 		ast_string_field_set(p, cid_name, calleridname);
 
@@ -14791,7 +14913,7 @@
 			build_contact(p);
 	}
 
-	of = get_in_brackets(from);
+	of = get_in_brackets(of);
 
 	/* save the URI part of the From header */
 	ast_string_field_set(p, from, of);
@@ -26837,6 +26959,18 @@
 	AST_CLI_DEFINE(sip_show_tcp, "List TCP Connections")
 };
 
+/*! \brief SIP test registration */
+static void sip_register_tests(void)
+{
+	AST_TEST_REGISTER(get_calleridname_test);
+}
+
+/*! \brief SIP test registration */
+static void sip_unregister_tests(void)
+{
+	AST_TEST_UNREGISTER(get_calleridname_test);
+}
+
 /*! \brief PBX load module - initialization */
 static int load_module(void)
 {
@@ -26927,6 +27061,9 @@
 		"lastms", RQ_INTEGER4, 11,
 		SENTINEL);
 
+
+	sip_register_tests();
+
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
@@ -27050,6 +27187,8 @@
 	ast_unload_realtime("sipregs");
 	ast_unload_realtime("sippeers");
 
+	sip_unregister_tests();
+
 	return 0;
 }
 
