Bug 1528496 - Back out bug 1023285 (rev 1ecabc4781e3) and fix handling of raw UTF-8 in headers properly. r+a=jorgk
authoralta88 <alta88@fixall.com>
Tue, 05 Mar 2019 23:26:49 +0100
changeset 34366 071ea9f37f83dc9bac2e603cff7003044c4ea84d
parent 34365 5a9179d16346a51c3015db486579cb705a21efc4
child 34367 8eed720334644dd96ce6dd0dd788a0b2176b08c4
push id389
push userclokep@gmail.com
push dateMon, 18 Mar 2019 19:01:53 +0000
bugs1528496, 1023285
Bug 1528496 - Back out bug 1023285 (rev 1ecabc4781e3) and fix handling of raw UTF-8 in headers properly. r+a=jorgk
mail/base/content/msgHdrView.js
mailnews/base/test/unit/test_nsMsgDBView_headerValues.js
mailnews/mime/public/nsIMsgHeaderParser.idl
mailnews/mime/src/mimeJSComponents.js
--- a/mail/base/content/msgHdrView.js
+++ b/mail/base/content/msgHdrView.js
@@ -1182,51 +1182,44 @@ function OutputMessageIds(headerEntry, h
  * @param headerEntry     parent div
  * @param emailAddresses  comma separated list of the addresses for this
  *                        header field
  */
 function OutputEmailAddresses(headerEntry, emailAddresses) {
   if (!emailAddresses)
     return;
 
-  var addresses = {};
-  var fullNames = {};
-  var names = {};
-  var numAddresses =  0;
+  // The email addresses are still RFC2047 encoded but libmime has already converted from
+  // "raw UTF-8" to "wide" (UTF-16) characters.
+  var addresses = MailServices.headerParser.parseEncodedHeaderW(emailAddresses);
 
-  numAddresses = MailServices.headerParser
-                             .parseHeadersWithArray(emailAddresses, addresses,
-                                                    names, fullNames);
-  var index = 0;
   if (headerEntry.useToggle)
     headerEntry.enclosingBox.resetAddressView(); // make sure we start clean
-  if (numAddresses == 0 && emailAddresses.includes(":")) {
+  if (addresses.length == 0 && emailAddresses.includes(":")) {
     // No addresses and a colon, so an empty group like "undisclosed-recipients: ;".
     // Add group name so at least something displays.
     let address = { displayName: emailAddresses };
     if (headerEntry.useToggle)
       headerEntry.enclosingBox.addAddressView(address);
     else
       updateEmailAddressNode(headerEntry.enclosingBox.emailAddressNode, address);
   }
-  while (index < numAddresses) {
+  for (let addr of addresses) {
     // If we want to include short/long toggle views and we have a long view,
     // always add it. If we aren't including a short/long view OR if we are and
     // we haven't parsed enough addresses to reach the cutoff valve yet then add
     // it to the default (short) div.
     let address = {};
-    address.emailAddress = addresses.value[index];
-    address.fullAddress = fullNames.value[index];
-    address.displayName = names.value[index];
+    address.emailAddress = addr.email;
+    address.fullAddress = addr.toString();
+    address.displayName = addr.name;
     if (headerEntry.useToggle)
       headerEntry.enclosingBox.addAddressView(address);
     else
       updateEmailAddressNode(headerEntry.enclosingBox.emailAddressNode, address);
-
-    index++;
   }
 
   if (headerEntry.useToggle)
     headerEntry.enclosingBox.buildViews();
 }
 
 function updateEmailAddressNode(emailAddressNode, address) {
   emailAddressNode.setAttribute("emailAddress", address.emailAddress || "");
--- a/mailnews/base/test/unit/test_nsMsgDBView_headerValues.js
+++ b/mailnews/base/test/unit/test_nsMsgDBView_headerValues.js
@@ -20,16 +20,22 @@ var tests = [
   [{from: "John Doe <db@tinderbox.invalid>"}, {senderCol: "John Doe"}],
   [{from: "\"Doe, John\" <db@tinderbox.invalid>"}, {senderCol: "Doe, John"}],
   [{from: "John Doe <db@tinderbox.invalid>, Sally Ann <db@null.invalid>"},
     {senderCol: "John Doe"}],
   [{from: "=?UTF-8?Q?David_H=C3=A5s=C3=A4ther?= <db@null.invalid>"},
     {senderCol: "David Håsäther"}],
   [{from: "=?UTF-8?Q?H=C3=A5s=C3=A4ther=2C_David?= <db@null.invalid>"},
     {senderCol: "Håsäther, David"}],
+  [{from: "\"Håsäther, David\" <db@null.invalid>"},
+    {senderCol: "Håsäther, David"}],
+  [{from: "David Håsäther <db@null.invalid>"},
+    {senderCol: "David Håsäther"}],
+  [{from: "\xC2\xAB\xCE\xA0\xCE\x9F\xCE\x9B\xCE\x99\xCE\xA4\xCE\x97\xCE\xA3\xC2\xBB"},
+    {senderCol: "«ΠΟΛΙΤΗΣ»"}],
   [{from: "John Doe \xF5  <db@null.invalid>",
      clobberHeaders: { "Content-type" : "text/plain; charset=ISO-8859-1" }},
     {senderCol: "John Doe õ"}],
   [{from: "John Doe \xF5 <db@null.invalid>",
      clobberHeaders: { "Content-type" : "text/plain; charset=ISO-8859-2" }},
     {senderCol: "John Doe ő"}],
   [{from: "=?UTF-8?Q?H=C3=A5s=C3=A4ther=2C_David?= <db@null.invalid>",
      clobberHeaders: { "Content-type" : "text/plain; charset=ISO-8859-2" }},
--- a/mailnews/mime/public/nsIMsgHeaderParser.idl
+++ b/mailnews/mime/public/nsIMsgHeaderParser.idl
@@ -92,16 +92,28 @@ interface nsIMsgHeaderParser : nsISuppor
   void parseEncodedHeader(in ACString aEncodedHeader,
                           in string aHeaderCharset,
                           [optional] in bool aPreserveGroups,
                           [optional] out unsigned long length,
                           [retval, array, size_is(length)]
                           out msgIAddressObject addresses);
 
   /**
+   * Parse an address-based header that has not yet been 2047-decoded and does not
+   * contain raw octets but instead wide (UTF-16) characters.
+   *
+   * @param aEncodedHeader  The RFC 2047-encoded header to parse.
+   * @return                An array corresponding to the header description.
+   */
+  void parseEncodedHeaderW(in AString aEncodedHeader,
+                           [optional] out unsigned long length,
+                           [retval, array, size_is(length)]
+                           out msgIAddressObject addresses);
+
+/**
    * Parse an address-based header that has been 2047-decoded.
    *
    * The result of this method is an array of objects described in the above
    * comment. Note that the header is a binary string that will be decoded as if
    * passed into nsIMimeConverter.
    *
    * @param aDecodedHeader  The non-RFC 2047-encoded header to parse.
    * @param aPreserveGroups If false (the default), the result is a flat array
@@ -173,19 +185,16 @@ interface nsIMsgHeaderParser : nsISuppor
    * Return an array of structured mailbox objects for the given display name
    * string.
    *
    * The string is expected to be a comma-separated sequence of strings that
    * would be produced by msgIAddressObject::toString(). For example, the string
    * "Bond, James <agent007@mi5.invalid>" would produce one address object,
    * while the string "webmaster@nowhere.invalid, child@nowhere.invalid" would
    * produce two address objects.
-   *
-   * Note that the input string is RFC 2231 and RFC 2047 decoded but no UTF-8
-   * decoding takes place.
    */
   void makeFromDisplayAddress(in AString aDisplayAddresses,
                               [optional] out unsigned long count,
                               [retval, array, size_is(count)] out msgIAddressObject addresses);
 
   /* @deprecated */ void parseHeadersWithArray(in wstring aLine,
                              [array, size_is(count)] out wstring aEmailAddresses,
                              [array, size_is(count)] out wstring aNames,
--- a/mailnews/mime/src/mimeJSComponents.js
+++ b/mailnews/mime/src/mimeJSComponents.js
@@ -263,16 +263,25 @@ MimeAddressParser.prototype = {
   QueryInterface: ChromeUtils.generateQI([Ci.nsIMsgHeaderParser]),
 
   parseEncodedHeader: function (aHeader, aCharset, aPreserveGroups, count) {
     aHeader = aHeader || "";
     let value = MimeParser.parseHeaderField(aHeader,
       MimeParser.HEADER_ADDRESS | MimeParser.HEADER_OPTION_ALL_I18N, aCharset);
     return fixArray(value, aPreserveGroups, count);
   },
+  parseEncodedHeaderW: function (aHeader, count) {
+    aHeader = aHeader || "";
+    let value = MimeParser.parseHeaderField(aHeader,
+      MimeParser.HEADER_ADDRESS |
+      MimeParser.HEADER_OPTION_DECODE_2231 |
+      MimeParser.HEADER_OPTION_DECODE_2047,
+      undefined);
+    return fixArray(value, false, count);
+  },
   parseDecodedHeader: function (aHeader, aPreserveGroups, count) {
     aHeader = aHeader || "";
     let value = MimeParser.parseHeaderField(aHeader, MimeParser.HEADER_ADDRESS);
     return fixArray(value, aPreserveGroups, count);
   },
 
   makeMimeHeader: function (addresses, length) {
     addresses = fixXpconnectAddresses(addresses);
@@ -384,23 +393,17 @@ MimeAddressParser.prototype = {
       return this.makeMailboxObject('', aDisplayName);
     }
   },
 
   // What follows is the deprecated API that will be removed shortly.
 
   parseHeadersWithArray: function (aHeader, aAddrs, aNames, aFullNames) {
     let addrs = [], names = [], fullNames = [];
-    // Parse header, but without HEADER_OPTION_ALLOW_RAW.
-    let value = MimeParser.parseHeaderField(aHeader || "",
-                                            MimeParser.HEADER_ADDRESS |
-                                            MimeParser.HEADER_OPTION_DECODE_2231 |
-                                            MimeParser.HEADER_OPTION_DECODE_2047,
-                                            undefined);
-    let allAddresses = fixArray(value, false);
+    let allAddresses = this.parseEncodedHeader(aHeader, undefined, false);
 
     // Don't index the dummy empty address.
     if (aHeader.trim() == "")
       allAddresses = [];
     for (let address of allAddresses) {
       addrs.push(address.email);
       names.push(address.name || null);
       fullNames.push(address.toString());