Index: channels/chan_sip.c
===================================================================
--- channels/chan_sip.c	(revision 237917)
+++ channels/chan_sip.c	(working copy)
@@ -14584,45 +14584,117 @@
 /*! \brief  Get caller id name from SIP headers */
 static 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;
+	/* no data at all or no storage room? */
+	if (input[0] == '\0' || input[0] == '<' || outputsize <= 1) {
+		if (outputsize > 0)
+			*output = '\0';
+		return output;
+	}
+	/* if from-spec (input) is an addr-spec, we'll also have no data.
+	 * we'll find out later. */
+	/* make room for NUL */
+	outputsize -= 1;
 
-	if (!end || end == input)	/* we require a part in brackets */
-		return NULL;
+	/* get_header already trimmed the HCOLON spaces for us, so token 0 will
+	 * not be a space. */
+	/* quoted-string rules */
+	if (input[0] == '"') {
+		++input;
+		while (outputsize > 0) {
+			/* ifs ordered on likelyhood */
+			/* qdtext (WSP without LWS) */
+			if (*input == 0x9 || ((unsigned char)*input >= 0x20 && *input != 0x22
+				&& *input != 0x5c && *input != 0x7f)) {
+				/* (we skip checks for utf8 validity) */
+				*output++ = *input++;
+				outputsize -= 1;
+			/* end of quoted-string */
+			} else if (*input == '"') {
+				break;
+			/* quoted-pair */
+			} else if (*input == '\\') {
+				++input;
+				if ((unsigned char)*input > 0x7f || *input == 0xa || *input == 0xd
+					|| *input == 0x0) /* can't afford to ignore terminating NUL :( */
+					goto get_calleridname_fail;
+				*output++ = *input++;
+				outputsize -= 1;
+#if 0
+			/* qdtext (LWS) */
+			} else if (*input == 0xd) {
+				/* horrible: should loop here and check that next is LF and then SP/HTAB or infinitely more CRLF */
+#endif
+			/* else, invalid */
+			} else {
+				goto get_calleridname_fail;
+			}
+		}
+		/* terminate outbuf */
+		*output = '\0';
+		return orig_output;
+	}
 
-	end--; /* move just before "<" */
+	/* either an addr-spec or tokenLWS-combo */
+	/* cannot start with LWS */
+	if (input[0] == 0x9 || input[0] == 0xd || input[0] == ' ') {
+		goto get_calleridname_fail;
+	}
+	while (outputsize > 0) {
+		/* 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;
+		/* end of tokenLWS-combo */
+		} else if (*input == '<') {
+			/* we could assert that the previous char is LWS, but we don't care */
+			break;
+#if 0
+		/* LWS */
+		} else if (*input == 0xd) {
+			/* horrible: should loop here and check that next is LF and then SP/HTAB or infinitely more CRLF */
+#endif
+		/* else, invalid */
+		} else {
+			goto get_calleridname_fail;
+		}
+	}
+	/* set NUL 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 orig_output;
 
-	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;
-	}
-	return output;
+get_calleridname_fail:
+	*orig_output = '\0';
+	return NULL; /* null to designate parse error */
 }
 
 
