Bug 1323377 - Fix charset handling in nsMsgCompose::CreateMessage. Use MIME-supplied charset for replies. r=mkmelin a=jorgk
authorJorg K <jorgk@jorgk.com>
Tue, 27 Dec 2016 19:00:00 +0100
changeset 26887 cd5dbc99ec53eb7abcf34c1e8ff48b892377692d
parent 26886 f53bfb3398e6131726772f78e1afcd212994f9a5
child 26888 9152817cd3676dd5d9ca64bee508c36d67872a6d
push id1834
push userclokep@gmail.com
push dateMon, 23 Jan 2017 21:48:40 +0000
treeherdercomm-beta@293cffe83e59 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmkmelin, jorgk
bugs1323377
Bug 1323377 - Fix charset handling in nsMsgCompose::CreateMessage. Use MIME-supplied charset for replies. r=mkmelin a=jorgk
mail/test/mozmill/composition/test-reply-multipart-charset.js
mailnews/base/util/nsMsgProtocol.cpp
mailnews/base/util/nsMsgProtocol.h
mailnews/compose/src/nsMsgCompose.cpp
mailnews/compose/src/nsMsgCompose.h
mailnews/imap/src/nsImapProtocol.cpp
mailnews/imap/src/nsImapProtocol.h
mailnews/mime/emitters/nsMimeBaseEmitter.cpp
mailnews/news/src/nsNNTPProtocol.cpp
--- a/mail/test/mozmill/composition/test-reply-multipart-charset.js
+++ b/mail/test/mozmill/composition/test-reply-multipart-charset.js
@@ -79,13 +79,14 @@ function subtest_replyEditAsNewForward_c
   press_delete(mc);
 }
 
 function test_replyEditAsNewForward_charset() {
   // Check that the charset is taken from the message body.
   subtest_replyEditAsNewForward_charset(1, "./multipart-charset.eml", "EUC-KR");
   subtest_replyEditAsNewForward_charset(2, "./multipart-charset.eml", "EUC-KR");
   subtest_replyEditAsNewForward_charset(3, "./multipart-charset.eml", "EUC-KR");
-  subtest_replyEditAsNewForward_charset(4, "./multipart-charset.eml", "EUC-KR");
+  // For "forward as attachment" we use the default charset (which is UTF-8).
+  subtest_replyEditAsNewForward_charset(4, "./multipart-charset.eml", "UTF-8");
 
   // Check that a UTF-16 encoded e-mail is forced to UTF-8 when replying.
   subtest_replyEditAsNewForward_charset(1, "./body-utf16.eml", "UTF-8");
 }
--- a/mailnews/base/util/nsMsgProtocol.cpp
+++ b/mailnews/base/util/nsMsgProtocol.cpp
@@ -566,42 +566,42 @@ NS_IMETHODIMP nsMsgProtocol::SetLoadFlag
 
 NS_IMETHODIMP nsMsgProtocol::GetContentType(nsACString &aContentType)
 {
   // as url dispatching matures, we'll be intelligent and actually start
   // opening the url before specifying the content type. This will allow
   // us to optimize the case where the message url actual refers to
   // a part in the message that has a content type that is not message/rfc822
 
-  if (m_ContentType.IsEmpty())
+  if (mContentType.IsEmpty())
     aContentType.AssignLiteral("message/rfc822");
   else
-    aContentType = m_ContentType;
+    aContentType = mContentType;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsMsgProtocol::SetContentType(const nsACString &aContentType)
 {
   nsAutoCString charset;
-  nsresult rv = NS_ParseResponseContentType(aContentType, m_ContentType, charset);
-  if (NS_FAILED(rv) || m_ContentType.IsEmpty())
-    m_ContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
+  nsresult rv = NS_ParseResponseContentType(aContentType, mContentType, charset);
+  if (NS_FAILED(rv) || mContentType.IsEmpty())
+    mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
   return rv;
 }
 
 NS_IMETHODIMP nsMsgProtocol::GetContentCharset(nsACString &aContentCharset)
 {
-  aContentCharset.Truncate();
+  aContentCharset.Assign(mCharset);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsMsgProtocol::SetContentCharset(const nsACString &aContentCharset)
 {
-  NS_WARNING("nsMsgProtocol::SetContentCharset() not implemented");
-  return NS_ERROR_NOT_IMPLEMENTED;
+  mCharset.Assign(aContentCharset);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMsgProtocol::GetContentDisposition(uint32_t *aContentDisposition)
 {
   return NS_ERROR_NOT_AVAILABLE;
 }
 
--- a/mailnews/base/util/nsMsgProtocol.h
+++ b/mailnews/base/util/nsMsgProtocol.h
@@ -143,17 +143,18 @@ protected:
   nsCOMPtr<nsIURI>            m_url;          // the running url
   nsCOMPtr<nsIStreamListener> m_channelListener;
   nsCOMPtr<nsISupports>        m_channelContext;
   nsCOMPtr<nsILoadGroup>      m_loadGroup;
   nsLoadFlags                 mLoadFlags;
   nsCOMPtr<nsIProgressEventSink> mProgressEventSink;
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   nsCOMPtr<nsISupports>       mOwner;
-  nsCString                   m_ContentType;
+  nsCString                   mContentType;
+  nsCString                   mCharset;
   int64_t                     mContentLength;
   nsCOMPtr<nsILoadInfo>       m_loadInfo;
 
   nsCString m_lastPasswordSent; // used to prefill the password prompt
 
   // private helper routine used by subclasses to quickly get a reference to the correct prompt dialog
   // for a mailnews url. 
   nsresult GetPromptDialogFromUrl(nsIMsgMailNewsUrl * aMsgUrl, nsIPrompt ** aPromptDialog);
--- a/mailnews/compose/src/nsMsgCompose.cpp
+++ b/mailnews/compose/src/nsMsgCompose.cpp
@@ -175,16 +175,17 @@ nsMsgCompose::nsMsgCompose()
 
   mQuotingToFollow = false;
   mInsertingQuotedContent = false;
   mWhatHolder = 1;
   m_window = nullptr;
   m_editor = nullptr;
   mQuoteStreamListener=nullptr;
   mCharsetOverride = false;
+  mAnswerDefaultCharset = false;
   mDeleteDraft = false;
   m_compFields = nullptr;    //m_compFields will be set during nsMsgCompose::Initialize
   mType = nsIMsgCompType::New;
 
   // For TagConvertible
   // Read and cache pref
   mConvertStructs = false;
   nsCOMPtr<nsIPrefBranch> prefBranch (do_GetService(NS_PREFSERVICE_CONTRACTID));
@@ -1587,45 +1588,58 @@ NS_IMETHODIMP nsMsgCompose::GetEditor(ns
 }
 
 NS_IMETHODIMP nsMsgCompose::SetEditor(nsIEditor *aEditor)
 {
   m_editor = aEditor;
   return NS_OK;
 }
 
+static nsresult fixCharset(nsCString &aCharset)
+{
+  // No matter what, we should block x-windows-949 (our internal name)
+  // from being used for outgoing emails (bug 234958).
+  if (aCharset.Equals("x-windows-949", nsCaseInsensitiveCStringComparator()))
+    aCharset = "EUC-KR";
+
+  // Convert to a canonical charset name.
+  // Bug 1297118 will revisit this call site.
+  nsresult rv;
+  nsCOMPtr<nsICharsetConverterManager> ccm =
+    do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCString charset(aCharset);
+  rv = ccm->GetCharsetAlias(charset.get(), aCharset);
+
+  // Don't accept UTF-16 ever. UTF-16 should never be selected as an
+  // outgoing encoding for e-mail. MIME can't handle those messages
+  // encoded in ASCII-incompatible encodings.
+  if (NS_FAILED(rv) ||
+      StringBeginsWith(aCharset, NS_LITERAL_CSTRING("UTF-16"))) {
+    aCharset.AssignLiteral("UTF-8");
+  }
+  return NS_OK;
+}
+
 // This used to be called BEFORE editor was created
 //  (it did the loadUrl that triggered editor creation)
 // It is called from JS after editor creation
 //  (loadUrl is done in JS)
 NS_IMETHODIMP nsMsgCompose::InitEditor(nsIEditor* aEditor, mozIDOMWindowProxy* aContentWindow)
 {
   NS_ENSURE_ARG_POINTER(aEditor);
   NS_ENSURE_ARG_POINTER(aContentWindow);
   nsresult rv;
 
   m_editor = aEditor;
 
-  // Convert to a canonical charset name.
-  // Bug 1297118 will revisit this call site.
-  nsCOMPtr<nsICharsetConverterManager> ccm =
-    do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+  nsAutoCString msgCharSet(m_compFields->GetCharacterSet());
+  rv = fixCharset(msgCharSet);
   NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoCString msgCharSet;
-  rv = ccm->GetCharsetAlias(m_compFields->GetCharacterSet(), msgCharSet);
-
-  // Don't accept UTF-16 ever. UTF-16 should never be selected as an
-  // outgoing encoding for e-mail. MIME can't handle those messages
-  // encoded in ASCII-incompatible encodings.
-  if (NS_FAILED(rv) ||
-      StringBeginsWith(msgCharSet, NS_LITERAL_CSTRING("UTF-16"))) {
-    NS_WARNING("Suppressing UTF-16");
-    msgCharSet.AssignLiteral("UTF-8");
-  }
   m_compFields->SetCharacterSet(msgCharSet.get());
   m_editor->SetDocumentCharacterSet(msgCharSet);
 
   nsCOMPtr<nsPIDOMWindowOuter> window = nsPIDOMWindowOuter::From(aContentWindow);
 
   nsIDocShell *docShell = window->GetDocShell();
   NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
 
@@ -1872,35 +1886,38 @@ nsresult nsMsgCompose::CreateMessage(con
   // store the original message URI so we can extract it after we send the message to properly
   // mark any disposition flags like replied or forwarded on the message.
   if (mOriginalMsgURI.IsEmpty())
     mOriginalMsgURI = originalMsgURI;
 
   nsCOMPtr<nsIPrefBranch> prefs (do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // If we are forwarding inline or replying with template, mime did already
-  // setup the compose fields therefore we should stop now.
+  // "Forward inline" and "Reply with template" processing.
+  // Note the early return at the end of the block.
   if (type == nsIMsgCompType::ForwardInline ||
       type == nsIMsgCompType::ReplyWithTemplate)
   {
+    // Use charset set up in the compose fields by MIME unless we should
+    // use the default charset.
     bool replyInDefault = false;
     prefs->GetBoolPref("mailnews.reply_in_default_charset",
                         &replyInDefault);
     // Use send_default_charset if reply_in_default_charset is on.
     if (replyInDefault)
     {
       nsString str;
       nsCString charset;
       NS_GetLocalizedUnicharPreferenceWithDefault(prefs, "mailnews.send_default_charset",
                                                   EmptyString(), str);
       if (!str.IsEmpty())
       {
         LossyCopyUTF16toASCII(str, charset);
         m_compFields->SetCharacterSet(charset.get());
+        mAnswerDefaultCharset = true;
       }
     }
 
     // We want to treat this message as a reference too
     nsCOMPtr<nsIMsgDBHdr> msgHdr;
     rv = GetMsgDBHdrFromURI(originalMsgURI, getter_AddRefs(msgHdr));
     if (NS_SUCCEEDED(rv))
     {
@@ -1929,38 +1946,53 @@ nsresult nsMsgCompose::CreateMessage(con
         reference.Trim(" ", false, true);
       }
       msgHdr->GetMessageId(getter_Copies(messageId));
       reference.AppendLiteral("<");
       reference.Append(messageId);
       reference.AppendLiteral(">");
       m_compFields->SetReferences(reference.get());
     }
+
+    // Early return for "Forward inline" and "Reply with template" processing.
     return NS_OK;
   }
 
+  // All other processing.
   char *uriList = PL_strdup(originalMsgURI);
   if (!uriList)
     return NS_ERROR_OUT_OF_MEMORY;
 
+  // Resulting charset for this message.
   nsCString charset;
-  // use a charset of the original message
-  nsCString mailCharset;
-  bool charsetOverride = false;
-  GetTopmostMsgWindowCharacterSet(mailCharset, &mCharsetOverride);
-  if (!mailCharset.IsEmpty())
-  {
-    charset = mailCharset;
-    charsetOverride = mCharsetOverride;
+
+  // Check for the charset of the last displayed message, it
+  // will be used for quoting and as override.
+  nsCString windowCharset;
+  mCharsetOverride = false;
+  mAnswerDefaultCharset = false;
+  GetTopmostMsgWindowCharacterSet(windowCharset, &mCharsetOverride);
+  if (!windowCharset.IsEmpty()) {
+    // Although the charset in which to send the message might change,
+    // the original message will be parsed for quoting using the charset it is
+    // now displayed with.
+    mQuoteCharset = windowCharset;
+
+    if (mCharsetOverride) {
+      // Use override charset.
+      charset = windowCharset;
+    }
   }
 
-  // although the charset in which to _send_ the message might change,
-  // the original message will be parsed for quoting using the charset it is
-  // now displayed with
-  mQuoteCharset = charset;
+  // Note the following:
+  // LoadDraftOrTemplate() is run in nsMsgComposeService::OpenComposeWindow()
+  // for five compose types: ForwardInline, ReplyWithTemplate (both covered
+  // in the code block above) and Draft, Template and Redirect. For these
+  // compose types, the charset is already correct (incl. MIME-applied override)
+  // unless the default charset should be used.
 
   bool isFirstPass = true;
   char *uri = uriList;
   char *nextUri;
   do
   {
     nextUri = strstr(uri, "://");
     if (nextUri)
@@ -1985,49 +2017,34 @@ nsresult nsMsgCompose::CreateMessage(con
     {
       rv = GetMsgDBHdrFromURI(uri, getter_AddRefs(msgHdr));
       NS_ENSURE_SUCCESS(rv,rv);
     }
     if (msgHdr)
     {
       nsCString decodedCString;
 
-      if (!charsetOverride && charset.IsEmpty())
-      {
-        rv = msgHdr->GetCharset(getter_Copies(charset));
-        if (NS_FAILED(rv)) return rv;
-      }
-
       bool replyInDefault = false;
       prefs->GetBoolPref("mailnews.reply_in_default_charset",
                           &replyInDefault);
       // Use send_default_charset if reply_in_default_charset is on.
       if (replyInDefault)
       {
         nsString str;
         NS_GetLocalizedUnicharPreferenceWithDefault(prefs, "mailnews.send_default_charset",
                                                     EmptyString(), str);
-        if (!str.IsEmpty())
+        if (!str.IsEmpty()) {
           LossyCopyUTF16toASCII(str, charset);
+          mAnswerDefaultCharset = true;
+        }
       }
 
-      // ReplyWithTemplate needs to always use the charset of the template,
-      // nothing else. That is passed in through the compFields.
-      if (type == nsIMsgCompType::ReplyWithTemplate) {
-         rv = compFields->GetCharacterSet(getter_Copies(charset));
-         NS_ENSURE_SUCCESS(rv,rv);
-      }
-
-      // No matter what, we should block x-windows-949 (our internal name)
-      // from being used for outgoing emails (bug 234958)
-      if (charset.Equals("x-windows-949",
-            nsCaseInsensitiveCStringComparator()))
-        charset = "EUC-KR";
-
-      // get an original charset, used for a label, UTF-8 is used for the internal processing
+      // Set the charset we determined, if any, in the comp fields.
+      // For replies, the charset will be set after processing the message
+      // through MIME in QuotingOutputStreamListener::OnStopRequest().
       if (isFirstPass && !charset.IsEmpty())
         m_compFields->SetCharacterSet(charset.get());
 
       nsString subject;
       rv = msgHdr->GetMime2DecodedSubject(subject);
       if (NS_FAILED(rv)) return rv;
 
       // Check if (was: is present in the subject
@@ -2278,30 +2295,32 @@ QuotingOutputStreamListener::~QuotingOut
     free(mUnicodeConversionBuffer);
 }
 
 QuotingOutputStreamListener::QuotingOutputStreamListener(const char * originalMsgURI,
                                                          nsIMsgDBHdr *originalMsgHdr,
                                                          bool quoteHeaders,
                                                          bool headersOnly,
                                                          nsIMsgIdentity *identity,
-                                                         const char *charset,
-                                                         bool charsetOverride,
+                                                         nsIMsgQuote* msgQuote,
+                                                         bool charsetFixed,
                                                          bool quoteOriginal,
                                                          const nsACString& htmlToQuote)
 {
   nsresult rv;
   mQuoteHeaders = quoteHeaders;
   mHeadersOnly = headersOnly;
   mIdentity = identity;
   mOrigMsgHdr = originalMsgHdr;
   mUnicodeBufferCharacterLength = 0;
   mUnicodeConversionBuffer = nullptr;
   mQuoteOriginal = quoteOriginal;
   mHtmlToQuote = htmlToQuote;
+  mQuote = msgQuote;
+  mCharsetFixed = charsetFixed;
 
   if (!mHeadersOnly || !mHtmlToQuote.IsEmpty())
   {
     // Get header type, locale and strings from pref.
     int32_t replyHeaderType;
     nsAutoString replyHeaderLocale;
     nsString replyHeaderAuthorWrote;
     nsString replyHeaderOnDateAuthorWrote;
@@ -2460,17 +2479,16 @@ QuotingOutputStreamListener::ConvertToPl
 NS_IMETHODIMP QuotingOutputStreamListener::OnStartRequest(nsIRequest *request, nsISupports * /* ctxt */)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP QuotingOutputStreamListener::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
 {
   nsresult rv = NS_OK;
-  nsAutoString aCharset;
 
   if (!mHtmlToQuote.IsEmpty())
   {
     // If we had a selection in the original message to quote, we can add
     // it now that we are done ignoring the original body of the message
     mHeadersOnly = false;
     rv = AppendToMsgBody(mHtmlToQuote);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -2493,17 +2511,16 @@ NS_IMETHODIMP QuotingOutputStreamListene
                    type == nsIMsgCompType::ReplyToGroup ||
                    type == nsIMsgCompType::ReplyToSenderAndGroup) &&
       mQuoteOriginal)
   {
     nsCOMPtr<nsIMsgCompFields> compFields;
     compose->GetCompFields(getter_AddRefs(compFields));
     if (compFields)
     {
-      aCharset.AssignLiteral("UTF-8");
       nsAutoString from;
       nsAutoString to;
       nsAutoString cc;
       nsAutoString bcc;
       nsAutoString replyTo;
       nsAutoString mailReplyTo;
       nsAutoString mailFollowupTo;
       nsAutoString newgroups;
@@ -2518,16 +2535,32 @@ NS_IMETHODIMP QuotingOutputStreamListene
       if (!mMimeConverter)
       {
         mMimeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID, &rv);
         NS_ENSURE_SUCCESS(rv, rv);
       }
       nsCString charset;
       compFields->GetCharacterSet(getter_Copies(charset));
 
+      if (!mCharsetFixed) {
+        // Get the charset from the channel where MIME left it.
+        if (mQuote) {
+          nsCOMPtr<nsIChannel> quoteChannel;
+          mQuote->GetQuoteChannel(getter_AddRefs(quoteChannel));
+          if (quoteChannel) {
+            quoteChannel->GetContentCharset(charset);
+            if (!charset.IsEmpty()) {
+              rv = fixCharset(charset);
+              NS_ENSURE_SUCCESS(rv, rv);
+              compFields->SetCharacterSet(charset.get());
+            }
+          }
+        }
+      }
+
       mHeaders->ExtractHeader(HEADER_FROM, true, outCString);
       ConvertRawBytesToUTF16(outCString, charset.get(), from);
 
       mHeaders->ExtractHeader(HEADER_TO, true, outCString);
       ConvertRawBytesToUTF16(outCString, charset.get(), to);
 
       mHeaders->ExtractHeader(HEADER_CC, true, outCString);
       ConvertRawBytesToUTF16(outCString, charset.get(), cc);
@@ -3286,18 +3319,24 @@ nsMsgCompose::QuoteMessage(const char *m
   mQuote = do_CreateInstance(NS_MSGQUOTE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr <nsIMsgDBHdr> msgHdr;
   rv = GetMsgDBHdrFromURI(msgURI, getter_AddRefs(msgHdr));
 
   // Create the consumer output stream.. this will receive all the HTML from libmime
   mQuoteStreamListener =
-    new QuotingOutputStreamListener(msgURI, msgHdr, false, !mHtmlToQuote.IsEmpty(), m_identity,
-                                    m_compFields->GetCharacterSet(), mCharsetOverride, false,
+    new QuotingOutputStreamListener(msgURI,
+                                    msgHdr,
+                                    false,
+                                    !mHtmlToQuote.IsEmpty(),
+                                    m_identity,
+                                    mQuote,
+                                    mCharsetOverride || mAnswerDefaultCharset,
+                                    false,
                                     mHtmlToQuote);
 
   if (!mQuoteStreamListener)
     return NS_ERROR_FAILURE;
   NS_ADDREF(mQuoteStreamListener);
 
   mQuoteStreamListener->SetComposeObj(this);
 
@@ -3333,19 +3372,25 @@ nsMsgCompose::QuoteOriginalMessage() // 
   if (fileUrl)
   {
     mOriginalMsgURI.Replace(0, 5, NS_LITERAL_CSTRING("mailbox:"));
     mOriginalMsgURI.AppendLiteral("?number=0");
   }
 
   // Create the consumer output stream.. this will receive all the HTML from libmime
   mQuoteStreamListener =
-    new QuotingOutputStreamListener(mOriginalMsgURI.get(), originalMsgHdr, mWhatHolder != 1,
-                                    !bAutoQuote || !mHtmlToQuote.IsEmpty(), m_identity,
-                                    mQuoteCharset.get(), mCharsetOverride, true, mHtmlToQuote);
+    new QuotingOutputStreamListener(mOriginalMsgURI.get(),
+                                    originalMsgHdr,
+                                    mWhatHolder != 1,
+                                    !bAutoQuote || !mHtmlToQuote.IsEmpty(),
+                                    m_identity,
+                                    mQuote,
+                                    mCharsetOverride || mAnswerDefaultCharset,
+                                    true,
+                                    mHtmlToQuote);
 
   if (!mQuoteStreamListener)
     return NS_ERROR_FAILURE;
   NS_ADDREF(mQuoteStreamListener);
 
   mQuoteStreamListener->SetComposeObj(this);
 
   rv = mQuote->QuoteMessage(mOriginalMsgURI.get(), mWhatHolder != 1, mQuoteStreamListener,
--- a/mailnews/compose/src/nsMsgCompose.h
+++ b/mailnews/compose/src/nsMsgCompose.h
@@ -123,16 +123,17 @@ protected:
   nsCOMPtr<nsIMsgProgress>                  mProgress;          // use by the back end to report progress to the front end
 
   // Deal with quoting issues...
   nsString                                  mCiteReference;
   nsCOMPtr<nsIMsgQuote>                     mQuote;
   bool                                      mQuotingToFollow;   // Quoting indicator
   MSG_ComposeType                           mType;              // Message type
   bool                                      mCharsetOverride;
+  bool                                      mAnswerDefaultCharset;
   bool                                      mDeleteDraft;
   nsMsgDispositionState                     mDraftDisposition;
   nsCOMPtr <nsIMsgDBHdr>                    mOrigMsgHdr;
 
   nsCString                                 mSmtpPassword;
   nsCString                                 mHtmlToQuote;
 
   nsTObserverArray<nsCOMPtr<nsIMsgComposeStateListener> > mStateListeners;
@@ -152,18 +153,18 @@ protected:
 class QuotingOutputStreamListener : public nsIMsgQuotingOutputStreamListener
 {
 public:
     QuotingOutputStreamListener(const char *originalMsgURI,
                                 nsIMsgDBHdr *origMsgHdr,
                                 bool quoteHeaders,
                                 bool headersOnly,
                                 nsIMsgIdentity *identity,
-                                const char *charset,
-                                bool charetOverride, 
+                                nsIMsgQuote* msgQuote,
+                                bool charsetFixed,
                                 bool quoteOriginal,
                                 const nsACString& htmlToQuote);
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSIMSGQUOTINGOUTPUTSTREAMLISTENER
 
@@ -178,16 +179,18 @@ public:
 private:
     virtual ~QuotingOutputStreamListener();
     nsWeakPtr                   mWeakComposeObj;
     nsString                    mMsgBody;
     nsString                    mCitePrefix;
     nsString                    mSignature;
     bool                        mQuoteHeaders;
     bool                        mHeadersOnly;
+    bool                        mCharsetFixed;
+    nsCOMPtr<nsIMsgQuote>       mQuote;
     nsCOMPtr<nsIMimeHeaders>    mHeaders;
     nsCOMPtr<nsIMsgIdentity>    mIdentity;
     nsCOMPtr<nsIMsgDBHdr>       mOrigMsgHdr;
     nsString                    mCiteReference;
     nsCOMPtr<nsIMimeConverter>  mMimeConverter;
     nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
     int32_t                     mUnicodeBufferCharacterLength;
     char16_t*                   mUnicodeConversionBuffer;
--- a/mailnews/imap/src/nsImapProtocol.cpp
+++ b/mailnews/imap/src/nsImapProtocol.cpp
@@ -8768,17 +8768,17 @@ nsImapMockChannel::~nsImapMockChannel()
     Close();
 }
 
 nsresult nsImapMockChannel::NotifyStartEndReadFromCache(bool start)
 {
   nsresult rv = NS_OK;
   mReadingFromCache = start;
   nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(m_url, &rv);
-  nsCOMPtr<nsIImapProtocol> imapProtocol = do_QueryReferent(m_protocol);
+  nsCOMPtr<nsIImapProtocol> imapProtocol = do_QueryReferent(mProtocol);
   if (imapUrl)
   {
     nsCOMPtr<nsIImapMailFolderSink> folderSink;
     rv = imapUrl->GetImapMailFolderSink(getter_AddRefs(folderSink));
     if (folderSink)
     {
       nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(m_url);
       rv = folderSink->SetUrlState(nullptr /* we don't know the protocol */,
@@ -9576,56 +9576,56 @@ NS_IMETHODIMP nsImapMockChannel::GetLoad
 NS_IMETHODIMP nsImapMockChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
 {
   mLoadFlags = aLoadFlags;
   return NS_OK;       // don't fail when trying to set this
 }
 
 NS_IMETHODIMP nsImapMockChannel::GetContentType(nsACString &aContentType)
 {
-  if (m_ContentType.IsEmpty())
+  if (mContentType.IsEmpty())
   {
     nsImapAction imapAction = 0;
     if (m_url)
     {
       nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(m_url);
       if (imapUrl)
       {
         imapUrl->GetImapAction(&imapAction);
       }
     }
     if (imapAction == nsIImapUrl::nsImapSelectFolder)
       aContentType.AssignLiteral("x-application-imapfolder");
     else
       aContentType.AssignLiteral("message/rfc822");
   }
   else
-    aContentType = m_ContentType;
+    aContentType = mContentType;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsImapMockChannel::SetContentType(const nsACString &aContentType)
 {
   nsAutoCString charset;
-  nsresult rv = NS_ParseResponseContentType(aContentType, m_ContentType, charset);
-  if (NS_FAILED(rv) || m_ContentType.IsEmpty())
-    m_ContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
+  nsresult rv = NS_ParseResponseContentType(aContentType, mContentType, charset);
+  if (NS_FAILED(rv) || mContentType.IsEmpty())
+    mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
   return rv;
 }
 
 NS_IMETHODIMP nsImapMockChannel::GetContentCharset(nsACString &aContentCharset)
 {
-  aContentCharset.Truncate();
+  aContentCharset.Assign(mCharset);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsImapMockChannel::SetContentCharset(const nsACString &aContentCharset)
 {
-  NS_WARNING("nsImapMockChannel::SetContentCharset() not implemented");
-  return NS_ERROR_NOT_IMPLEMENTED;
+  mCharset.Assign(aContentCharset);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsImapMockChannel::GetContentDisposition(uint32_t *aContentDisposition)
 {
   return NS_ERROR_NOT_AVAILABLE;
 }
 
@@ -9712,26 +9712,26 @@ NS_IMETHODIMP nsImapMockChannel::IsPendi
 NS_IMETHODIMP nsImapMockChannel::GetStatus(nsresult *status)
 {
     *status = m_cancelStatus;
     return NS_OK;
 }
 
 NS_IMETHODIMP nsImapMockChannel::SetImapProtocol(nsIImapProtocol *aProtocol)
 {
-  m_protocol = do_GetWeakReference(aProtocol);
+  mProtocol = do_GetWeakReference(aProtocol);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsImapMockChannel::Cancel(nsresult status)
 {
   NS_WARNING_ASSERTION(NS_IsMainThread(),
                        "nsImapMockChannel::Cancel should only be called from UI thread");
   m_cancelStatus = status;
-  nsCOMPtr<nsIImapProtocol> imapProtocol = do_QueryReferent(m_protocol);
+  nsCOMPtr<nsIImapProtocol> imapProtocol = do_QueryReferent(mProtocol);
 
   // if we aren't reading from the cache and we get canceled...doom our cache entry...
   if (m_url)
   {
     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url);
     DoomCacheEntry(mailnewsUrl);
   }
 
--- a/mailnews/imap/src/nsImapProtocol.h
+++ b/mailnews/imap/src/nsImapProtocol.h
@@ -713,18 +713,19 @@ protected:
   nsISupports * m_channelContext; 
   nsresult m_cancelStatus;
   nsLoadFlags mLoadFlags;
   nsCOMPtr<nsIProgressEventSink> mProgressEventSink;
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   nsCOMPtr<nsISupports> mOwner;
   nsCOMPtr<nsISupports> mSecurityInfo;
   nsCOMPtr<nsIRequest> mCacheRequest; // the request associated with a read from the cache
-  nsCString m_ContentType;
-  nsWeakPtr   m_protocol;
+  nsCString mContentType;
+  nsCString mCharset;
+  nsWeakPtr mProtocol;
 
   bool mChannelClosed;
   bool mReadingFromCache;
   bool mTryingToReadPart;
   int64_t mContentLength;
 
   // cache related helper methods
   nsresult OpenCacheEntry(); // makes a request to the cache service for a cache entry for a url
--- a/mailnews/mime/emitters/nsMimeBaseEmitter.cpp
+++ b/mailnews/mime/emitters/nsMimeBaseEmitter.cpp
@@ -558,19 +558,17 @@ nsMimeBaseEmitter::StartHeader(bool root
 // Ok, if we are here, and we have a aCharset passed in that is not
 // UTF-8 or US-ASCII, then we should tag the mChannel member with this
 // charset. This is because replying to messages with specified charset's
 // need to be tagged as that charset by default.
 //
 NS_IMETHODIMP
 nsMimeBaseEmitter::UpdateCharacterSet(const char *aCharset)
 {
-  if ( (aCharset) && (PL_strcasecmp(aCharset, "US-ASCII")) &&
-        (PL_strcasecmp(aCharset, "ISO-8859-1")) &&
-        (PL_strcasecmp(aCharset, "UTF-8")) )
+  if (aCharset)
   {
     nsAutoCString contentType;
 
     if (NS_SUCCEEDED(mChannel->GetContentType(contentType)) && !contentType.IsEmpty())
     {
       char *cBegin = contentType.BeginWriting();
 
       const char *cPtr = PL_strcasestr(cBegin, "charset=");
@@ -590,17 +588,21 @@ nsMimeBaseEmitter::UpdateCharacterSet(co
           }
 
           ++ptr;
         }
       }
 
       // have to set content-type since it could have an embedded null byte
       mChannel->SetContentType(nsDependentCString(cBegin));
-      mChannel->SetContentCharset(nsDependentCString(aCharset));
+      if (PL_strcasecmp(aCharset, "US-ASCII") == 0) {
+        mChannel->SetContentCharset(NS_LITERAL_CSTRING("ISO-8859-1"));
+      } else {
+        mChannel->SetContentCharset(nsDependentCString(aCharset));
+      }
     }
   }
 
   return NS_OK;
 }
 
 //
 // This will be called for every header field regardless if it is in an
--- a/mailnews/news/src/nsNNTPProtocol.cpp
+++ b/mailnews/news/src/nsNNTPProtocol.cpp
@@ -713,17 +713,17 @@ nsresult nsNNTPProtocol::ReadFromMemCach
     NS_ADDREF(cacheListener);
 
     SetLoadGroup(m_loadGroup);
     m_typeWanted = ARTICLE_WANTED;
 
     nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningURL);
     cacheListener->Init(m_channelListener, static_cast<nsIChannel *>(this), mailnewsUrl);
 
-    m_ContentType = ""; // reset the content type for the upcoming read....
+    mContentType = ""; // reset the content type for the upcoming read....
 
     rv = pump->AsyncRead(cacheListener, m_channelContext);
     NS_RELEASE(cacheListener);
 
     if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return
     {
       // we're not calling nsMsgProtocol::AsyncRead(), which calls nsNNTPProtocol::LoadUrl, so we need to take care of some stuff it does.
       m_channelListener = nullptr;
@@ -791,17 +791,17 @@ bool nsNNTPProtocol::ReadFromLocalCache(
                                    fileStream, offset, (int64_t) size);
         if (NS_SUCCEEDED(rv))
           rv = pump->AsyncRead(cacheListener, m_channelContext);
 
         NS_RELEASE(cacheListener);
 
         if (NS_SUCCEEDED(rv)) // ONLY if we succeeded in actually starting the read should we return
         {
-          m_ContentType.Truncate();
+          mContentType.Truncate();
           m_channelListener = nullptr;
           NNTP_LOG_NOTE("Loading message from offline storage");
           return true;
         }
       }
       else
         mailnewsUrl->SetMsgIsInLocalCache(false);
     }
@@ -945,17 +945,17 @@ NS_IMETHODIMP nsNNTPProtocol::AsyncOpen2
     return AsyncOpen(listener, nullptr);
 }
 
 nsresult nsNNTPProtocol::LoadUrl(nsIURI * aURL, nsISupports * aConsumer)
 {
   NS_ENSURE_ARG_POINTER(aURL);
 
   nsCString group;
-  m_ContentType.Truncate();
+  mContentType.Truncate();
   nsresult rv = NS_OK;
 
   m_runningURL = do_QueryInterface(aURL, &rv);
   if (NS_FAILED(rv)) return rv;
   m_runningURL->GetNewsAction(&m_newsAction);
 
   SetIsBusy(true);
 
@@ -4727,19 +4727,19 @@ nsNNTPProtocol::SetProgressStatus(const 
   return rv;
 }
 
 NS_IMETHODIMP nsNNTPProtocol::GetContentType(nsACString &aContentType)
 {
 
   // if we've been set with a content type, then return it....
   // this happens when we go through libmime now as it sets our new content type
-  if (!m_ContentType.IsEmpty())
+  if (!mContentType.IsEmpty())
   {
-    aContentType = m_ContentType;
+    aContentType = mContentType;
     return NS_OK;
   }
 
   // otherwise do what we did before...
 
   if (m_typeWanted == GROUP_WANTED)
     aContentType.AssignLiteral("x-application-newsgroup");
   else if (m_typeWanted == IDS_WANTED)