diff --git a/netwerk/mime/nsMIMEHeaderParamImpl.cpp b/netwerk/mime/nsMIMEHeaderParamImpl.cpp index 42c75cd0664a..454e0b23a24b 100644 --- a/netwerk/mime/nsMIMEHeaderParamImpl.cpp +++ b/netwerk/mime/nsMIMEHeaderParamImpl.cpp @@ -1091,6 +1091,43 @@ void CopyRawHeader(const char *aInput, uint32_t aLen, } } +nsresult DecodeQOrBase64Str(const char *aEncoded, size_t aLen, char aQOrBase64, + const char *aCharset, nsACString &aResult) +{ + char *decodedText; + NS_ASSERTION(aQOrBase64 == 'Q' || aQOrBase64 == 'B', "Should be 'Q' or 'B'"); + if(aQOrBase64 == 'Q') + decodedText = DecodeQ(aEncoded, aLen); + else if (aQOrBase64 == 'B') { + decodedText = PL_Base64Decode(aEncoded, aLen, nullptr); + } else { + return NS_ERROR_INVALID_ARG; + } + + if (!decodedText) { + return NS_ERROR_INVALID_ARG; + } + + nsresult rv; + nsCOMPtr + cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID, &rv)); + nsAutoCString utf8Text; + if (NS_SUCCEEDED(rv)) { + // skip ASCIIness/UTF8ness test if aCharset is 7bit non-ascii charset. + rv = cvtUTF8->ConvertStringToUTF8(nsDependentCString(decodedText), + aCharset, + IS_7BIT_NON_ASCII_CHARSET(aCharset), + true, 1, utf8Text); + } + PR_Free(decodedText); + if (NS_FAILED(rv)) { + return rv; + } + aResult.Append(utf8Text); + + return NS_OK; +} + static const char especials[] = "()<>@,;:\\\"/[]?.="; // |decode_mime_part2_str| taken from comi18n.c @@ -1103,14 +1140,13 @@ nsresult DecodeRFC2047Str(const char *aHeader, const char *aDefaultCharset, bool aOverrideCharset, nsACString &aResult) { const char *p, *q = nullptr, *r; - char *decodedText; const char *begin; // tracking pointer for where we are in the input buffer int32_t isLastEncodedWord = 0; const char *charsetStart, *charsetEnd; - char charset[80]; - - // initialize charset name to an empty string - charset[0] = '\0'; + nsAutoCString prevCharset, curCharset; + nsAutoCString encodedText; + char prevEncoding = '\0', curEncoding; + nsresult rv; begin = aHeader; @@ -1155,15 +1191,9 @@ nsresult DecodeRFC2047Str(const char *aHeader, const char *aDefaultCharset, charsetEnd = q; } - // Check for too-long charset name - if (uint32_t(charsetEnd - charsetStart) >= sizeof(charset)) - goto badsyntax; - - memcpy(charset, charsetStart, charsetEnd - charsetStart); - charset[charsetEnd - charsetStart] = 0; - q++; - if (*q != 'Q' && *q != 'q' && *q != 'B' && *q != 'b') + curEncoding = nsCRT::ToUpper(*q); + if (curEncoding != 'Q' && curEncoding != 'B') goto badsyntax; if (q[1] != '?') @@ -1182,55 +1212,88 @@ nsresult DecodeRFC2047Str(const char *aHeader, const char *aDefaultCharset, continue; } - if(*q == 'Q' || *q == 'q') - decodedText = DecodeQ(q + 2, r - (q + 2)); - else { + curCharset.Assign(charsetStart, charsetEnd - charsetStart); + // Override charset if requested. Never override labeled UTF-8. + // Use default charset instead of UNKNOWN-8BIT + if ((aOverrideCharset && 0 != nsCRT::strcasecmp(curCharset.get(), "UTF-8")) + || (aDefaultCharset && 0 == nsCRT::strcasecmp(curCharset.get(), "UNKNOWN-8BIT")) + ) { + curCharset = aDefaultCharset; + } + + const char *R; + R = r; + if (curEncoding == 'B') { // bug 227290. ignore an extraneous '=' at the end. // (# of characters in B-encoded part has to be a multiple of 4) int32_t n = r - (q + 2); - n -= (n % 4 == 1 && !PL_strncmp(r - 3, "===", 3)) ? 1 : 0; - decodedText = PL_Base64Decode(q + 2, n, nullptr); + R -= (n % 4 == 1 && !PL_strncmp(r - 3, "===", 3)) ? 1 : 0; + } + // Bug 493544. Don't decode the encoded text until it ends + if (R[-1] != '=' + && (prevCharset.IsEmpty() + || (curCharset == prevCharset && curEncoding == prevEncoding)) + ) { + encodedText.Append(q + 2, R - (q + 2)); + prevCharset = curCharset; + prevEncoding = curEncoding; + + begin = r + 2; + isLastEncodedWord = 1; + continue; } - if (decodedText == nullptr) - goto badsyntax; - - // Override charset if requested. Never override labeled UTF-8. - // Use default charset instead of UNKNOWN-8BIT - if ((aOverrideCharset && 0 != nsCRT::strcasecmp(charset, "UTF-8")) || - (aDefaultCharset && 0 == nsCRT::strcasecmp(charset, "UNKNOWN-8BIT"))) { - PL_strncpy(charset, aDefaultCharset, sizeof(charset) - 1); - charset[sizeof(charset) - 1] = '\0'; + bool bDecoded; // If the current line has been decoded. + bDecoded = false; + if (!encodedText.IsEmpty()) { + if (curCharset == prevCharset && curEncoding == prevEncoding) { + encodedText.Append(q + 2, R - (q + 2)); + bDecoded = true; + } + rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(), + prevEncoding, prevCharset.get(), aResult); + if (NS_FAILED(rv)) { + aResult.Append(encodedText); + } + encodedText.Truncate(); + prevCharset.Truncate(); } - - { - nsCOMPtr - cvtUTF8(do_GetService(NS_UTF8CONVERTERSERVICE_CONTRACTID)); - nsAutoCString utf8Text; - // skip ASCIIness/UTF8ness test if aCharset is 7bit non-ascii charset. - if (cvtUTF8 && - NS_SUCCEEDED( - cvtUTF8->ConvertStringToUTF8(nsDependentCString(decodedText), - charset, - IS_7BIT_NON_ASCII_CHARSET(charset), - true, 1, utf8Text))) { - aResult.Append(utf8Text); - } else { - aResult.Append(REPLACEMENT_CHAR); + if (!bDecoded) { + rv = DecodeQOrBase64Str(q + 2, R - (q + 2), curEncoding, + curCharset.get(), aResult); + if (NS_FAILED(rv)) { + aResult.Append(encodedText); } } - PR_Free(decodedText); + begin = r + 2; isLastEncodedWord = 1; continue; badsyntax: + if (!encodedText.IsEmpty()) { + rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(), + prevEncoding, prevCharset.get(), aResult); + if (NS_FAILED(rv)) { + aResult.Append(encodedText); + } + encodedText.Truncate(); + prevCharset.Truncate(); + } // copy the part before the encoded-word aResult.Append(begin, p - begin); begin = p; isLastEncodedWord = 0; } + if (!encodedText.IsEmpty()) { + rv = DecodeQOrBase64Str(encodedText.get(), encodedText.Length(), + prevEncoding, prevCharset.get(), aResult); + if (NS_FAILED(rv)) { + aResult.Append(encodedText); + } + } + // put the tail back CopyRawHeader(begin, strlen(begin), aDefaultCharset, aResult);