Bug 493544 - Don't decode the encoded text until it ends. r=smontagu
authorZane U. Ji <ZaneUJi@gmail.com>
Sat, 22 Dec 2012 17:54:00 +0900
changeset 125983 45fd2425adc4c4ace488553e73b9ae78e32474ad
parent 125982 f93588a041ece6b130d5e4039b3312822771f22d
child 125984 96898e6b3078560645dfcd0617d7ce464031f270
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmontagu
bugs493544
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 493544 - Don't decode the encoded text until it ends. r=smontagu
netwerk/mime/nsMIMEHeaderParamImpl.cpp
--- a/netwerk/mime/nsMIMEHeaderParamImpl.cpp
+++ b/netwerk/mime/nsMIMEHeaderParamImpl.cpp
@@ -1086,36 +1086,72 @@ void CopyRawHeader(const char *aInput, u
       if (c & 0x80)
         aOutput.Append(REPLACEMENT_CHAR);
       else
         aOutput.Append(char(c));
     }
   }
 }
 
+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<nsIUTF8ConverterService>
+    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
 // Decode RFC2047-encoded words in the input and convert the result to UTF-8.
 // If aOverrideCharset is true, charset in RFC2047-encoded words is 
 // ignored and aDefaultCharset is assumed, instead. aDefaultCharset
 // is also used to convert raw octets (without RFC 2047 encoding) to UTF-8.
 //static
 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;
 
   // To avoid buffer realloc, if possible, set capacity in advance. No 
   // matter what,  more than 3x expansion can never happen for all charsets
   // supported by Mozilla. SCSU/BCSU with the sliding window set to a
   // non-BMP block may be exceptions, but Mozilla does not support them. 
   // Neither any known mail/news program use them. Even if there's, we're
@@ -1150,25 +1186,19 @@ nsresult DecodeRFC2047Str(const char *aH
       if (!charsetEnd && *q == '*') {
         charsetEnd = q; 
       }
     }
     if (!charsetEnd) {
       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] != '?')
       goto badsyntax;
 
     r = q;
     for (r = q + 2; *r != '?'; r++) {
       if (*r < ' ') goto badsyntax;
@@ -1177,65 +1207,98 @@ nsresult DecodeRFC2047Str(const char *aH
         goto badsyntax;
     else if (r == q + 2) {
         // it's empty, skip
         begin = r + 2;
         isLastEncodedWord = 1;
         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;
     }
-
-    if (decodedText == nullptr)
-      goto badsyntax;
+    // 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;
 
-    // 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';
+      begin = r + 2;
+      isLastEncodedWord = 1;
+      continue;
     }
 
-    {
-      nsCOMPtr<nsIUTF8ConverterService> 
-        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);
+    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();
+    }
+    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);
 
   nsAutoCString tempStr(aResult);
   tempStr.ReplaceChar('\t', ' ');
   aResult = tempStr;
 
   return NS_OK;