summaryrefslogtreecommitdiffstats
path: root/mailnews/mime/emitters
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/mime/emitters')
-rw-r--r--mailnews/mime/emitters/moz.build21
-rw-r--r--mailnews/mime/emitters/nsEmitterUtils.cpp67
-rw-r--r--mailnews/mime/emitters/nsEmitterUtils.h14
-rw-r--r--mailnews/mime/emitters/nsMimeBaseEmitter.cpp1092
-rw-r--r--mailnews/mime/emitters/nsMimeBaseEmitter.h147
-rw-r--r--mailnews/mime/emitters/nsMimeEmitterCID.h51
-rw-r--r--mailnews/mime/emitters/nsMimeHtmlEmitter.cpp543
-rw-r--r--mailnews/mime/emitters/nsMimeHtmlEmitter.h64
-rw-r--r--mailnews/mime/emitters/nsMimePlainEmitter.cpp64
-rw-r--r--mailnews/mime/emitters/nsMimePlainEmitter.h31
-rw-r--r--mailnews/mime/emitters/nsMimeRawEmitter.cpp34
-rw-r--r--mailnews/mime/emitters/nsMimeRawEmitter.h29
-rw-r--r--mailnews/mime/emitters/nsMimeRebuffer.cpp50
-rw-r--r--mailnews/mime/emitters/nsMimeRebuffer.h29
-rw-r--r--mailnews/mime/emitters/nsMimeXmlEmitter.cpp184
-rw-r--r--mailnews/mime/emitters/nsMimeXmlEmitter.h47
16 files changed, 2467 insertions, 0 deletions
diff --git a/mailnews/mime/emitters/moz.build b/mailnews/mime/emitters/moz.build
new file mode 100644
index 000000000..b1e3390bd
--- /dev/null
+++ b/mailnews/mime/emitters/moz.build
@@ -0,0 +1,21 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ 'nsMimeEmitterCID.h',
+]
+
+SOURCES += [
+ 'nsEmitterUtils.cpp',
+ 'nsMimeBaseEmitter.cpp',
+ 'nsMimeHtmlEmitter.cpp',
+ 'nsMimePlainEmitter.cpp',
+ 'nsMimeRawEmitter.cpp',
+ 'nsMimeRebuffer.cpp',
+ 'nsMimeXmlEmitter.cpp',
+]
+
+FINAL_LIBRARY = 'mail'
+
diff --git a/mailnews/mime/emitters/nsEmitterUtils.cpp b/mailnews/mime/emitters/nsEmitterUtils.cpp
new file mode 100644
index 000000000..551bf5b31
--- /dev/null
+++ b/mailnews/mime/emitters/nsEmitterUtils.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsCOMPtr.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "nsMailHeaders.h"
+#include "nsIMimeEmitter.h"
+#include "nsIStringBundle.h"
+#include "nsIServiceManager.h"
+#include "nsIIOService.h"
+#include "nsIURI.h"
+#include "prprf.h"
+
+
+extern "C" bool
+EmitThisHeaderForPrefSetting(int32_t dispType, const char *header)
+{
+ if (nsMimeHeaderDisplayTypes::AllHeaders == dispType)
+ return true;
+
+ if ((!header) || (!*header))
+ return false;
+
+ if (nsMimeHeaderDisplayTypes::MicroHeaders == dispType)
+ {
+ if (
+ (!strcmp(header, HEADER_SUBJECT)) ||
+ (!strcmp(header, HEADER_FROM)) ||
+ (!strcmp(header, HEADER_DATE))
+ )
+ return true;
+ else
+ return false;
+ }
+
+ if (nsMimeHeaderDisplayTypes::NormalHeaders == dispType)
+ {
+ if (
+ (!strcmp(header, HEADER_DATE)) ||
+ (!strcmp(header, HEADER_TO)) ||
+ (!strcmp(header, HEADER_SUBJECT)) ||
+ (!strcmp(header, HEADER_SENDER)) ||
+ (!strcmp(header, HEADER_RESENT_TO)) ||
+ (!strcmp(header, HEADER_RESENT_SENDER)) ||
+ (!strcmp(header, HEADER_RESENT_FROM)) ||
+ (!strcmp(header, HEADER_RESENT_CC)) ||
+ (!strcmp(header, HEADER_REPLY_TO)) ||
+ (!strcmp(header, HEADER_REFERENCES)) ||
+ (!strcmp(header, HEADER_NEWSGROUPS)) ||
+ (!strcmp(header, HEADER_MESSAGE_ID)) ||
+ (!strcmp(header, HEADER_FROM)) ||
+ (!strcmp(header, HEADER_FOLLOWUP_TO)) ||
+ (!strcmp(header, HEADER_CC)) ||
+ (!strcmp(header, HEADER_ORGANIZATION)) ||
+ (!strcmp(header, HEADER_REPLY_TO)) ||
+ (!strcmp(header, HEADER_BCC))
+ )
+ return true;
+ else
+ return false;
+ }
+
+ return true;
+}
+
diff --git a/mailnews/mime/emitters/nsEmitterUtils.h b/mailnews/mime/emitters/nsEmitterUtils.h
new file mode 100644
index 000000000..965c1e33c
--- /dev/null
+++ b/mailnews/mime/emitters/nsEmitterUtils.h
@@ -0,0 +1,14 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _nsEmitterUtils_h_
+#define _nsEmitterUtils_h_
+
+#include "prmem.h"
+#include "plstr.h"
+
+extern "C" bool EmitThisHeaderForPrefSetting(int32_t dispType, const char *header);
+
+#endif // _nsEmitterUtils_h_
+
diff --git a/mailnews/mime/emitters/nsMimeBaseEmitter.cpp b/mailnews/mime/emitters/nsMimeBaseEmitter.cpp
new file mode 100644
index 000000000..223eef433
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimeBaseEmitter.cpp
@@ -0,0 +1,1092 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCOMPtr.h"
+#include <stdio.h>
+#include "nsMimeBaseEmitter.h"
+#include "nsMailHeaders.h"
+#include "nscore.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIServiceManager.h"
+#include "prmem.h"
+#include "nsEmitterUtils.h"
+#include "nsMimeStringResources.h"
+#include "msgCore.h"
+#include "nsIComponentManager.h"
+#include "nsEmitterUtils.h"
+#include "nsIMimeStreamConverter.h"
+#include "nsIMimeConverter.h"
+#include "nsMsgMimeCID.h"
+#include "mozilla/Logging.h"
+#include "prprf.h"
+#include "nsIMimeHeaders.h"
+#include "nsIMsgWindow.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsDateTimeFormatCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMsgUtils.h"
+#include "nsTextFormatter.h"
+#include "mozilla/Services.h"
+#include <algorithm>
+
+static PRLogModuleInfo * gMimeEmitterLogModule = nullptr;
+
+#define MIME_HEADER_URL "chrome://messenger/locale/mimeheader.properties"
+#define MIME_URL "chrome://messenger/locale/mime.properties"
+
+NS_IMPL_ISUPPORTS(nsMimeBaseEmitter, nsIMimeEmitter, nsIInterfaceRequestor)
+
+nsMimeBaseEmitter::nsMimeBaseEmitter()
+{
+ // Initialize data output vars...
+ mFirstHeaders = true;
+
+ mBufferMgr = nullptr;
+ mTotalWritten = 0;
+ mTotalRead = 0;
+ mInputStream = nullptr;
+ mOutStream = nullptr;
+ mOutListener = nullptr;
+
+ // Display output control vars...
+ mDocHeader = false;
+ m_stringBundle = nullptr;
+ mURL = nullptr;
+ mHeaderDisplayType = nsMimeHeaderDisplayTypes::NormalHeaders;
+
+ // Setup array for attachments
+ mAttachCount = 0;
+ mAttachArray = new nsTArray<attachmentInfoType*>();
+ mCurrentAttachment = nullptr;
+
+ // Header cache...
+ mHeaderArray = new nsTArray<headerInfoType*>();
+
+ // Embedded Header Cache...
+ mEmbeddedHeaderArray = nullptr;
+
+ // HTML Header Data...
+// mHTMLHeaders = "";
+// mCharset = "";
+
+ // Init the body...
+ mBodyStarted = false;
+// mBody = "";
+
+ // This is needed for conversion of I18N Strings...
+ mUnicodeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID);
+
+ if (!gMimeEmitterLogModule)
+ gMimeEmitterLogModule = PR_NewLogModule("MIME");
+
+ // Do prefs last since we can live without this if it fails...
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (pPrefBranch)
+ pPrefBranch->GetIntPref("mail.show_headers", &mHeaderDisplayType);
+}
+
+nsMimeBaseEmitter::~nsMimeBaseEmitter(void)
+{
+ // Delete the buffer manager...
+ if (mBufferMgr)
+ {
+ delete mBufferMgr;
+ mBufferMgr = nullptr;
+ }
+
+ // Clean up the attachment array structures...
+ if (mAttachArray)
+ {
+ for (size_t i = 0; i < mAttachArray->Length(); i++)
+ {
+ attachmentInfoType *attachInfo = mAttachArray->ElementAt(i);
+ if (!attachInfo)
+ continue;
+
+ PR_FREEIF(attachInfo->contentType);
+ if (attachInfo->displayName)
+ NS_Free(attachInfo->displayName);
+ PR_FREEIF(attachInfo->urlSpec);
+ PR_FREEIF(attachInfo);
+ }
+ delete mAttachArray;
+ }
+
+ // Cleanup allocated header arrays...
+ CleanupHeaderArray(mHeaderArray);
+ mHeaderArray = nullptr;
+
+ CleanupHeaderArray(mEmbeddedHeaderArray);
+ mEmbeddedHeaderArray = nullptr;
+}
+
+NS_IMETHODIMP nsMimeBaseEmitter::GetInterface(const nsIID & aIID, void * *aInstancePtr)
+{
+ NS_ENSURE_ARG_POINTER(aInstancePtr);
+ return QueryInterface(aIID, aInstancePtr);
+}
+
+void
+nsMimeBaseEmitter::CleanupHeaderArray(nsTArray<headerInfoType*> *aArray)
+{
+ if (!aArray)
+ return;
+
+ for (size_t i = 0; i < aArray->Length(); i++)
+ {
+ headerInfoType *headerInfo = aArray->ElementAt(i);
+ if (!headerInfo)
+ continue;
+
+ PR_FREEIF(headerInfo->name);
+ PR_FREEIF(headerInfo->value);
+ PR_FREEIF(headerInfo);
+ }
+
+ delete aArray;
+}
+
+static int32_t MapHeaderNameToID(const char *header)
+{
+ // emitter passes UPPERCASE for header names
+ if (!strcmp(header, "DATE"))
+ return MIME_MHTML_DATE;
+ else if (!strcmp(header, "FROM"))
+ return MIME_MHTML_FROM;
+ else if (!strcmp(header, "SUBJECT"))
+ return MIME_MHTML_SUBJECT;
+ else if (!strcmp(header, "TO"))
+ return MIME_MHTML_TO;
+ else if (!strcmp(header, "SENDER"))
+ return MIME_MHTML_SENDER;
+ else if (!strcmp(header, "RESENT-TO"))
+ return MIME_MHTML_RESENT_TO;
+ else if (!strcmp(header, "RESENT-SENDER"))
+ return MIME_MHTML_RESENT_SENDER;
+ else if (!strcmp(header, "RESENT-FROM"))
+ return MIME_MHTML_RESENT_FROM;
+ else if (!strcmp(header, "RESENT-CC"))
+ return MIME_MHTML_RESENT_CC;
+ else if (!strcmp(header, "REPLY-TO"))
+ return MIME_MHTML_REPLY_TO;
+ else if (!strcmp(header, "REFERENCES"))
+ return MIME_MHTML_REFERENCES;
+ else if (!strcmp(header, "NEWSGROUPS"))
+ return MIME_MHTML_NEWSGROUPS;
+ else if (!strcmp(header, "MESSAGE-ID"))
+ return MIME_MHTML_MESSAGE_ID;
+ else if (!strcmp(header, "FOLLOWUP-TO"))
+ return MIME_MHTML_FOLLOWUP_TO;
+ else if (!strcmp(header, "CC"))
+ return MIME_MHTML_CC;
+ else if (!strcmp(header, "ORGANIZATION"))
+ return MIME_MHTML_ORGANIZATION;
+ else if (!strcmp(header, "BCC"))
+ return MIME_MHTML_BCC;
+
+ return 0;
+}
+
+char *
+nsMimeBaseEmitter::MimeGetStringByName(const char *aHeaderName)
+{
+ nsresult res = NS_OK;
+
+ if (!m_headerStringBundle)
+ {
+ static const char propertyURL[] = MIME_HEADER_URL;
+
+ nsCOMPtr<nsIStringBundleService> sBundleService =
+ mozilla::services::GetStringBundleService();
+ if (sBundleService)
+ {
+ res = sBundleService->CreateBundle(propertyURL, getter_AddRefs(m_headerStringBundle));
+ }
+ }
+
+ if (m_headerStringBundle)
+ {
+ nsString val;
+
+ res = m_headerStringBundle->GetStringFromName(NS_ConvertASCIItoUTF16(aHeaderName).get(),
+ getter_Copies(val));
+
+ if (NS_FAILED(res))
+ return nullptr;
+
+ // Here we need to return a new copy of the string
+ // This returns a UTF-8 string so the caller needs to perform a conversion
+ // if this is used as UCS-2 (e.g. cannot do nsString(utfStr);
+ //
+ return ToNewUTF8String(val);
+ }
+ else
+ {
+ return nullptr;
+ }
+}
+
+char *
+nsMimeBaseEmitter::MimeGetStringByID(int32_t aID)
+{
+ nsresult res = NS_OK;
+
+ if (!m_stringBundle)
+ {
+ static const char propertyURL[] = MIME_URL;
+
+ nsCOMPtr<nsIStringBundleService> sBundleService =
+ mozilla::services::GetStringBundleService();
+ if (sBundleService)
+ res = sBundleService->CreateBundle(propertyURL, getter_AddRefs(m_stringBundle));
+ }
+
+ if (m_stringBundle)
+ {
+ nsString val;
+
+ res = m_stringBundle->GetStringFromID(aID, getter_Copies(val));
+
+ if (NS_FAILED(res))
+ return nullptr;
+
+ return ToNewUTF8String(val);
+ }
+ else
+ return nullptr;
+}
+
+//
+// This will search a string bundle (eventually) to find a descriptive header
+// name to match what was found in the mail message. aHeaderName is passed in
+// in all caps and a dropback default name is provided. The caller needs to free
+// the memory returned by this function.
+//
+char *
+nsMimeBaseEmitter::LocalizeHeaderName(const char *aHeaderName, const char *aDefaultName)
+{
+ char *retVal = nullptr;
+
+ // prefer to use translated strings if not for quoting
+ if (mFormat != nsMimeOutput::nsMimeMessageQuoting &&
+ mFormat != nsMimeOutput::nsMimeMessageBodyQuoting)
+ {
+ // map name to id and get the translated string
+ int32_t id = MapHeaderNameToID(aHeaderName);
+ if (id > 0)
+ retVal = MimeGetStringByID(id);
+ }
+
+ // get the string from the other bundle (usually not translated)
+ if (!retVal)
+ retVal = MimeGetStringByName(aHeaderName);
+
+ if (retVal)
+ return retVal;
+ else
+ return strdup(aDefaultName);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// nsMimeBaseEmitter Interface
+///////////////////////////////////////////////////////////////////////////
+NS_IMETHODIMP
+nsMimeBaseEmitter::SetPipe(nsIInputStream * aInputStream, nsIOutputStream *outStream)
+{
+ mInputStream = aInputStream;
+ mOutStream = outStream;
+ return NS_OK;
+}
+
+// Note - these is setup only...you should not write
+// anything to the stream since these may be image data
+// output streams, etc...
+NS_IMETHODIMP
+nsMimeBaseEmitter::Initialize(nsIURI *url, nsIChannel * aChannel, int32_t aFormat)
+{
+ // set the url
+ mURL = url;
+ mChannel = aChannel;
+
+ // Create rebuffering object
+ delete mBufferMgr;
+ mBufferMgr = new MimeRebuffer();
+
+ // Counters for output stream
+ mTotalWritten = 0;
+ mTotalRead = 0;
+ mFormat = aFormat;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeBaseEmitter::SetOutputListener(nsIStreamListener *listener)
+{
+ mOutListener = listener;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeBaseEmitter::GetOutputListener(nsIStreamListener **listener)
+{
+ NS_ENSURE_ARG_POINTER(listener);
+
+ NS_IF_ADDREF(*listener = mOutListener);
+ return NS_OK;
+}
+
+
+// Attachment handling routines
+nsresult
+nsMimeBaseEmitter::StartAttachment(const nsACString &name,
+ const char *contentType,
+ const char *url,
+ bool aIsExternalAttachment)
+{
+ // Ok, now we will setup the attachment info
+ mCurrentAttachment = (attachmentInfoType *) PR_NEWZAP(attachmentInfoType);
+ if ( (mCurrentAttachment) && mAttachArray)
+ {
+ ++mAttachCount;
+
+ mCurrentAttachment->displayName = ToNewCString(name);
+ mCurrentAttachment->urlSpec = strdup(url);
+ mCurrentAttachment->contentType = strdup(contentType);
+ mCurrentAttachment->isExternalAttachment = aIsExternalAttachment;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsMimeBaseEmitter::EndAttachment()
+{
+ // Ok, add the attachment info to the attachment array...
+ if ( (mCurrentAttachment) && (mAttachArray) )
+ {
+ mAttachArray->AppendElement(mCurrentAttachment);
+ mCurrentAttachment = nullptr;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsMimeBaseEmitter::EndAllAttachments()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeBaseEmitter::AddAttachmentField(const char *field, const char *value)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeBaseEmitter::UtilityWrite(const char *buf)
+{
+ NS_ENSURE_ARG_POINTER(buf);
+
+ uint32_t written;
+ Write(nsDependentCString(buf), &written);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeBaseEmitter::UtilityWrite(const nsACString &buf)
+{
+ uint32_t written;
+ Write(buf, &written);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeBaseEmitter::UtilityWriteCRLF(const char *buf)
+{
+ NS_ENSURE_ARG_POINTER(buf);
+
+ uint32_t written;
+ Write(nsDependentCString(buf), &written);
+ Write(NS_LITERAL_CSTRING(CRLF), &written);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeBaseEmitter::Write(const nsACString &buf, uint32_t *amountWritten)
+{
+ unsigned int written = 0;
+ nsresult rv = NS_OK;
+ uint32_t needToWrite;
+
+#ifdef DEBUG_BenB
+ // If you want to see libmime output...
+ printf("%s", buf);
+#endif
+
+ MOZ_LOG(gMimeEmitterLogModule, mozilla::LogLevel::Info, ("%s", PromiseFlatCString(buf).get()));
+ //
+ // Make sure that the buffer we are "pushing" into has enough room
+ // for the write operation. If not, we have to buffer, return, and get
+ // it on the next time through
+ //
+ *amountWritten = 0;
+
+ needToWrite = mBufferMgr->GetSize();
+ // First, handle any old buffer data...
+ if (needToWrite > 0)
+ {
+ rv = WriteHelper(mBufferMgr->GetBuffer(), &written);
+
+ mTotalWritten += written;
+ mBufferMgr->ReduceBuffer(written);
+ *amountWritten = written;
+
+ // if we couldn't write all the old data, buffer the new data
+ // and return
+ if (mBufferMgr->GetSize() > 0)
+ {
+ mBufferMgr->IncreaseBuffer(buf);
+ return rv;
+ }
+ }
+
+
+ // if we get here, we are dealing with new data...try to write
+ // and then do the right thing...
+ rv = WriteHelper(buf, &written);
+ *amountWritten = written;
+ mTotalWritten += written;
+
+ if (written < buf.Length()) {
+ const nsACString &remainder = Substring(buf, written);
+ mBufferMgr->IncreaseBuffer(remainder);
+ }
+
+ return rv;
+}
+
+nsresult
+nsMimeBaseEmitter::WriteHelper(const nsACString &buf, uint32_t *countWritten)
+{
+ NS_ENSURE_TRUE(mOutStream, NS_ERROR_NOT_INITIALIZED);
+
+ nsresult rv = mOutStream->Write(buf.BeginReading(), buf.Length(), countWritten);
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ // pipe is full, push contents of pipe to listener...
+ uint64_t avail;
+ rv = mInputStream->Available(&avail);
+ if (NS_SUCCEEDED(rv) && avail) {
+ mOutListener->OnDataAvailable(mChannel, mURL, mInputStream, 0,
+ std::min(avail, uint64_t(PR_UINT32_MAX)));
+
+ // try writing again...
+ rv = mOutStream->Write(buf.BeginReading(), buf.Length(), countWritten);
+ }
+ }
+ return rv;
+}
+
+//
+// Find a cached header! Note: Do NOT free this value!
+//
+const char *
+nsMimeBaseEmitter::GetHeaderValue(const char *aHeaderName)
+{
+ char *retVal = nullptr;
+ nsTArray<headerInfoType*> *array = mDocHeader? mHeaderArray : mEmbeddedHeaderArray;
+
+ if (!array)
+ return nullptr;
+
+ for (size_t i = 0; i < array->Length(); i++)
+ {
+ headerInfoType *headerInfo = array->ElementAt(i);
+ if ( (!headerInfo) || (!headerInfo->name) || (!(*headerInfo->name)) )
+ continue;
+
+ if (!PL_strcasecmp(aHeaderName, headerInfo->name))
+ {
+ retVal = headerInfo->value;
+ break;
+ }
+ }
+
+ return retVal;
+}
+
+//
+// This is called at the start of the header block for all header information in ANY
+// AND ALL MESSAGES (yes, quoted, attached, etc...)
+//
+// NOTE: This will be called even when headers are will not follow. This is
+// to allow us to be notified of the charset of the original message. This is
+// important for forward and reply operations
+//
+NS_IMETHODIMP
+nsMimeBaseEmitter::StartHeader(bool rootMailHeader, bool headerOnly, const char *msgID,
+ const char *outCharset)
+{
+ NS_ENSURE_ARG_POINTER(outCharset);
+
+ mDocHeader = rootMailHeader;
+
+ // If this is not the mail messages header, then we need to create
+ // the mEmbeddedHeaderArray structure for use with this internal header
+ // structure.
+ if (!mDocHeader)
+ {
+ if (mEmbeddedHeaderArray)
+ CleanupHeaderArray(mEmbeddedHeaderArray);
+
+ mEmbeddedHeaderArray = new nsTArray<headerInfoType*>();
+ NS_ENSURE_TRUE(mEmbeddedHeaderArray, NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ // If the main doc, check on updated character set
+ if (mDocHeader)
+ UpdateCharacterSet(outCharset);
+ CopyASCIItoUTF16(nsDependentCString(outCharset), mCharset);
+ return NS_OK;
+}
+
+// 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)
+ {
+ nsAutoCString contentType;
+
+ if (NS_SUCCEEDED(mChannel->GetContentType(contentType)) && !contentType.IsEmpty())
+ {
+ char *cBegin = contentType.BeginWriting();
+
+ const char *cPtr = PL_strcasestr(cBegin, "charset=");
+
+ if (cPtr)
+ {
+ char *ptr = cBegin;
+ while (*ptr)
+ {
+ if ( (*ptr == ' ') || (*ptr == ';') )
+ {
+ if ((ptr + 1) >= cPtr)
+ {
+ *ptr = '\0';
+ break;
+ }
+ }
+
+ ++ptr;
+ }
+ }
+
+ // have to set content-type since it could have an embedded null byte
+ mChannel->SetContentType(nsDependentCString(cBegin));
+ 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
+// internal body or the outer message.
+//
+NS_IMETHODIMP
+nsMimeBaseEmitter::AddHeaderField(const char *field, const char *value)
+{
+ if ( (!field) || (!value) )
+ return NS_OK;
+
+ nsTArray<headerInfoType*> *tPtr;
+ if (mDocHeader)
+ tPtr = mHeaderArray;
+ else
+ tPtr = mEmbeddedHeaderArray;
+
+ // This is a header so we need to cache and output later.
+ // Ok, now we will setup the header info for the header array!
+ headerInfoType *ptr = (headerInfoType *) PR_NEWZAP(headerInfoType);
+ if ( (ptr) && tPtr)
+ {
+ ptr->name = strdup(field);
+ ptr->value = strdup(value);
+ tPtr->AppendElement(ptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeBaseEmitter::AddAllHeaders(const nsACString &allheaders)
+{
+ if (mDocHeader) //We want to set only the main headers of a message, not the potentially embedded one
+ {
+ nsresult rv;
+ nsCOMPtr<nsIMsgMailNewsUrl> msgurl (do_QueryInterface(mURL));
+ if (msgurl)
+ {
+ nsCOMPtr<nsIMimeHeaders> mimeHeaders = do_CreateInstance(NS_IMIMEHEADERS_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mimeHeaders->Initialize(allheaders);
+ msgurl->SetMimeHeaders(mimeHeaders);
+ }
+ }
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// The following code is responsible for formatting headers in a manner that is
+// identical to the normal XUL output.
+////////////////////////////////////////////////////////////////////////////////
+
+nsresult
+nsMimeBaseEmitter::GenerateDateString(const char * dateString,
+ nsACString &formattedDate,
+ bool showDateForToday)
+{
+ nsresult rv = NS_OK;
+
+ if (!mDateFormatter) {
+ mDateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ /**
+ * See if the user wants to have the date displayed in the senders
+ * timezone (including the timezone offset).
+ * We also evaluate the pref original_date which was introduced
+ * as makeshift in bug 118899.
+ */
+ bool displaySenderTimezone = false;
+ bool displayOriginalDate = false;
+
+ nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrefBranch> dateFormatPrefs;
+ rv = prefs->GetBranch("mailnews.display.", getter_AddRefs(dateFormatPrefs));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ dateFormatPrefs->GetBoolPref("date_senders_timezone", &displaySenderTimezone);
+ dateFormatPrefs->GetBoolPref("original_date", &displayOriginalDate);
+ // migrate old pref to date_senders_timezone
+ if (displayOriginalDate && !displaySenderTimezone)
+ dateFormatPrefs->SetBoolPref("date_senders_timezone", true);
+
+ PRExplodedTime explodedMsgTime;
+
+ // Bogus date string may leave some fields uninitialized, so take precaution.
+ memset(&explodedMsgTime, 0, sizeof (PRExplodedTime));
+
+ if (PR_ParseTimeStringToExplodedTime(dateString, false, &explodedMsgTime) != PR_SUCCESS)
+ return NS_ERROR_FAILURE;
+
+ /**
+ * To determine the date format to use, comparison of current and message
+ * time has to be made. If displaying in local time, both timestamps have
+ * to be in local time. If displaying in senders time zone, leave the compare
+ * time in that time zone.
+ * Otherwise in TZ+0100 on 2009-03-12 a message from 2009-03-11T20:49-0700
+ * would be displayed as "20:49 -0700" though it in fact is not from the
+ * same day.
+ */
+ PRExplodedTime explodedCompTime;
+ if (displaySenderTimezone)
+ explodedCompTime = explodedMsgTime;
+ else
+ PR_ExplodeTime(PR_ImplodeTime(&explodedMsgTime), PR_LocalTimeParameters, &explodedCompTime);
+
+ PRExplodedTime explodedCurrentTime;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &explodedCurrentTime);
+
+ // If we want short dates, check if the message is from today, and if so
+ // only show the time (e.g. 3:15 pm).
+ nsDateFormatSelector dateFormat = kDateFormatShort;
+ if (!showDateForToday &&
+ explodedCurrentTime.tm_year == explodedCompTime.tm_year &&
+ explodedCurrentTime.tm_month == explodedCompTime.tm_month &&
+ explodedCurrentTime.tm_mday == explodedCompTime.tm_mday)
+ {
+ // same day...
+ dateFormat = kDateFormatNone;
+ }
+
+ nsAutoString formattedDateString;
+
+ rv = mDateFormatter->FormatPRExplodedTime(nullptr /* nsILocale* locale */,
+ dateFormat,
+ kTimeFormatNoSeconds,
+ &explodedCompTime,
+ formattedDateString);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ if (displaySenderTimezone)
+ {
+ // offset of local time from UTC in minutes
+ int32_t senderoffset = (explodedMsgTime.tm_params.tp_gmt_offset +
+ explodedMsgTime.tm_params.tp_dst_offset) / 60;
+ // append offset to date string
+ char16_t *tzstring =
+ nsTextFormatter::smprintf(u" %+05d",
+ (senderoffset / 60 * 100) +
+ (senderoffset % 60));
+ formattedDateString.Append(tzstring);
+ nsTextFormatter::smprintf_free(tzstring);
+ }
+
+ CopyUTF16toUTF8(formattedDateString, formattedDate);
+ }
+
+ return rv;
+}
+
+char*
+nsMimeBaseEmitter::GetLocalizedDateString(const char * dateString)
+{
+ char *i18nValue = nullptr;
+
+ bool displayOriginalDate = false;
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+
+ if (prefBranch)
+ prefBranch->GetBoolPref("mailnews.display.original_date",
+ &displayOriginalDate);
+
+ if (!displayOriginalDate)
+ {
+ nsAutoCString convertedDateString;
+ nsresult rv = GenerateDateString(dateString, convertedDateString, true);
+ if (NS_SUCCEEDED(rv))
+ i18nValue = strdup(convertedDateString.get());
+ else
+ i18nValue = strdup(dateString);
+ }
+ else
+ i18nValue = strdup(dateString);
+
+ return i18nValue;
+}
+
+nsresult
+nsMimeBaseEmitter::WriteHeaderFieldHTML(const char *field, const char *value)
+{
+ char *newValue = nullptr;
+ char *i18nValue = nullptr;
+
+ if ( (!field) || (!value) )
+ return NS_OK;
+
+ //
+ // This is a check to see what the pref is for header display. If
+ // We should only output stuff that corresponds with that setting.
+ //
+ if (!EmitThisHeaderForPrefSetting(mHeaderDisplayType, field))
+ return NS_OK;
+
+ //
+ // If we encounter the 'Date' header we try to convert it's value
+ // into localized format.
+ //
+ if ( strcmp(field, "Date") == 0 )
+ i18nValue = GetLocalizedDateString(value);
+ else
+ i18nValue = strdup(value);
+
+ if ( (mUnicodeConverter) && (mFormat != nsMimeOutput::nsMimeMessageSaveAs) )
+ {
+ nsCString tValue;
+
+ // we're going to need a converter to convert
+ nsresult rv = mUnicodeConverter->DecodeMimeHeaderToUTF8(
+ nsDependentCString(i18nValue), nullptr, false, true, tValue);
+ if (NS_SUCCEEDED(rv) && !tValue.IsEmpty())
+ newValue = MsgEscapeHTML(tValue.get());
+ else
+ newValue = MsgEscapeHTML(i18nValue);
+ }
+ else
+ {
+ newValue = MsgEscapeHTML(i18nValue);
+ }
+
+ free(i18nValue);
+
+ if (!newValue)
+ return NS_OK;
+
+ mHTMLHeaders.Append("<tr>");
+ mHTMLHeaders.Append("<td>");
+
+ if (mFormat == nsMimeOutput::nsMimeMessageSaveAs)
+ mHTMLHeaders.Append("<b>");
+ else
+ mHTMLHeaders.Append("<div class=\"headerdisplayname\" style=\"display:inline;\">");
+
+ // Here is where we are going to try to L10N the tagName so we will always
+ // get a field name next to an emitted header value. Note: Default will always
+ // be the name of the header itself.
+ //
+ nsCString newTagName(field);
+ newTagName.StripWhitespace();
+ ToUpperCase(newTagName);
+
+ char *l10nTagName = LocalizeHeaderName(newTagName.get(), field);
+ if ( (!l10nTagName) || (!*l10nTagName) )
+ mHTMLHeaders.Append(field);
+ else
+ {
+ mHTMLHeaders.Append(l10nTagName);
+ PR_FREEIF(l10nTagName);
+ }
+
+ mHTMLHeaders.Append(": ");
+ if (mFormat == nsMimeOutput::nsMimeMessageSaveAs)
+ mHTMLHeaders.Append("</b>");
+ else
+ mHTMLHeaders.Append("</div>");
+
+ // Now write out the actual value itself and move on!
+ //
+ mHTMLHeaders.Append(newValue);
+ mHTMLHeaders.Append("</td>");
+
+ mHTMLHeaders.Append("</tr>");
+
+ PR_FREEIF(newValue);
+ return NS_OK;
+}
+
+nsresult
+nsMimeBaseEmitter::WriteHeaderFieldHTMLPrefix(const nsACString &name)
+{
+ if (
+ ( (mFormat == nsMimeOutput::nsMimeMessageSaveAs) && (mFirstHeaders) ) ||
+ ( (mFormat == nsMimeOutput::nsMimeMessagePrintOutput) && (mFirstHeaders) )
+ )
+ /* DO NOTHING */ ; // rhp: Do nothing...leaving the conditional like this so its
+ // easier to see the logic of what is going on.
+ else {
+ mHTMLHeaders.Append("<br><fieldset class=\"mimeAttachmentHeader\">");
+ if (!name.IsEmpty()) {
+ mHTMLHeaders.Append("<legend class=\"mimeAttachmentHeaderName\">");
+ nsCString escapedName;
+ escapedName.Adopt(MsgEscapeHTML(nsCString(name).get()));
+ mHTMLHeaders.Append(escapedName);
+ mHTMLHeaders.Append("</legend>");
+ }
+ mHTMLHeaders.Append("</fieldset>");
+ }
+
+ mFirstHeaders = false;
+ return NS_OK;
+}
+
+nsresult
+nsMimeBaseEmitter::WriteHeaderFieldHTMLPostfix()
+{
+ mHTMLHeaders.Append("<br>");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeBaseEmitter::WriteHTMLHeaders(const nsACString &name)
+{
+ WriteHeaderFieldHTMLPrefix(name);
+
+ // Start with the subject, from date info!
+ DumpSubjectFromDate();
+
+ // Continue with the to and cc headers
+ DumpToCC();
+
+ // Do the rest of the headers, but these will only be written if
+ // the user has the "show all headers" pref set
+ if (mHeaderDisplayType == nsMimeHeaderDisplayTypes::AllHeaders)
+ DumpRestOfHeaders();
+
+ WriteHeaderFieldHTMLPostfix();
+
+ // Now, we need to either append the headers we built up to the
+ // overall body or output to the stream.
+ UtilityWriteCRLF(mHTMLHeaders.get());
+
+ mHTMLHeaders = "";
+
+ return NS_OK;
+}
+
+nsresult
+nsMimeBaseEmitter::DumpSubjectFromDate()
+{
+ mHTMLHeaders.Append("<table border=0 cellspacing=0 cellpadding=0 width=\"100%\" class=\"header-part1\">");
+
+ // This is the envelope information
+ OutputGenericHeader(HEADER_SUBJECT);
+ OutputGenericHeader(HEADER_FROM);
+ OutputGenericHeader(HEADER_DATE);
+
+ // If we are Quoting a message, then we should dump the To: also
+ if ( ( mFormat == nsMimeOutput::nsMimeMessageQuoting ) ||
+ ( mFormat == nsMimeOutput::nsMimeMessageBodyQuoting ) )
+ OutputGenericHeader(HEADER_TO);
+
+ mHTMLHeaders.Append("</table>");
+
+ return NS_OK;
+}
+
+nsresult
+nsMimeBaseEmitter::DumpToCC()
+{
+ const char * toField = GetHeaderValue(HEADER_TO);
+ const char * ccField = GetHeaderValue(HEADER_CC);
+ const char * bccField = GetHeaderValue(HEADER_BCC);
+ const char * newsgroupField = GetHeaderValue(HEADER_NEWSGROUPS);
+
+ // only dump these fields if we have at least one of them! When displaying news
+ // messages that didn't have a To or Cc field, we'd always get an empty box
+ // which looked weird.
+ if (toField || ccField || bccField || newsgroupField)
+ {
+ mHTMLHeaders.Append("<table border=0 cellspacing=0 cellpadding=0 width=\"100%\" class=\"header-part2\">");
+
+ if (toField)
+ WriteHeaderFieldHTML(HEADER_TO, toField);
+ if (ccField)
+ WriteHeaderFieldHTML(HEADER_CC, ccField);
+ if (bccField)
+ WriteHeaderFieldHTML(HEADER_BCC, bccField);
+ if (newsgroupField)
+ WriteHeaderFieldHTML(HEADER_NEWSGROUPS, newsgroupField);
+
+ mHTMLHeaders.Append("</table>");
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsMimeBaseEmitter::DumpRestOfHeaders()
+{
+ nsTArray<headerInfoType*> *array = mDocHeader? mHeaderArray : mEmbeddedHeaderArray;
+
+ mHTMLHeaders.Append("<table border=0 cellspacing=0 cellpadding=0 width=\"100%\" class=\"header-part3\">");
+
+ for (size_t i = 0; i < array->Length(); i++)
+ {
+ headerInfoType *headerInfo = array->ElementAt(i);
+ if ( (!headerInfo) || (!headerInfo->name) || (!(*headerInfo->name)) ||
+ (!headerInfo->value) || (!(*headerInfo->value)))
+ continue;
+
+ if ( (!PL_strcasecmp(HEADER_SUBJECT, headerInfo->name)) ||
+ (!PL_strcasecmp(HEADER_DATE, headerInfo->name)) ||
+ (!PL_strcasecmp(HEADER_FROM, headerInfo->name)) ||
+ (!PL_strcasecmp(HEADER_TO, headerInfo->name)) ||
+ (!PL_strcasecmp(HEADER_CC, headerInfo->name)) )
+ continue;
+
+ WriteHeaderFieldHTML(headerInfo->name, headerInfo->value);
+ }
+
+ mHTMLHeaders.Append("</table>");
+ return NS_OK;
+}
+
+nsresult
+nsMimeBaseEmitter::OutputGenericHeader(const char *aHeaderVal)
+{
+ const char *val = GetHeaderValue(aHeaderVal);
+
+ if (val)
+ return WriteHeaderFieldHTML(aHeaderVal, val);
+
+ return NS_ERROR_FAILURE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+// These are the methods that should be implemented by the child class!
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+//
+// This should be implemented by the child class if special processing
+// needs to be done when the entire message is read.
+//
+NS_IMETHODIMP
+nsMimeBaseEmitter::Complete()
+{
+ // If we are here and still have data to write, we should try
+ // to flush it...if we try and fail, we should probably return
+ // an error!
+ uint32_t written;
+
+ nsresult rv = NS_OK;
+ while ( NS_SUCCEEDED(rv) && (mBufferMgr) && (mBufferMgr->GetSize() > 0))
+ rv = Write(EmptyCString(), &written);
+
+ if (mOutListener)
+ {
+ uint64_t bytesInStream = 0;
+ mozilla::DebugOnly<nsresult> rv2 = mInputStream->Available(&bytesInStream);
+ NS_ASSERTION(NS_SUCCEEDED(rv2), "Available failed");
+ if (bytesInStream)
+ {
+ nsCOMPtr<nsIRequest> request = do_QueryInterface(mChannel);
+ mOutListener->OnDataAvailable(request, mURL, mInputStream, 0, std::min(bytesInStream, uint64_t(PR_UINT32_MAX)));
+ }
+ }
+
+ return NS_OK;
+}
+
+//
+// This needs to do the right thing with the stored information. It only
+// has to do the output functions, this base class will take care of the
+// memory cleanup
+//
+NS_IMETHODIMP
+nsMimeBaseEmitter::EndHeader(const nsACString &name)
+{
+ return NS_OK;
+}
+
+// body handling routines
+NS_IMETHODIMP
+nsMimeBaseEmitter::StartBody(bool bodyOnly, const char *msgID, const char *outCharset)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeBaseEmitter::WriteBody(const nsACString &buf, uint32_t *amountWritten)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeBaseEmitter::EndBody()
+{
+ return NS_OK;
+}
diff --git a/mailnews/mime/emitters/nsMimeBaseEmitter.h b/mailnews/mime/emitters/nsMimeBaseEmitter.h
new file mode 100644
index 000000000..c33bc2687
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimeBaseEmitter.h
@@ -0,0 +1,147 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _nsMimeBaseEmitter_h_
+#define _nsMimeBaseEmitter_h_
+
+#include "prio.h"
+#include "nsIMimeEmitter.h"
+#include "nsMimeRebuffer.h"
+#include "nsIStreamListener.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIURI.h"
+#include "nsIChannel.h"
+#include "nsIMimeMiscStatus.h"
+#include "nsIPipe.h"
+#include "nsIStringBundle.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsIMimeConverter.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIDateTimeFormat.h"
+
+//
+// The base emitter will serve as the place to do all of the caching,
+// sorting, etc... of mail headers and bodies for this internally developed
+// emitter library. The other emitter classes in this file (nsMimeHTMLEmitter, etc.)
+// will only be concerned with doing output processing ONLY.
+//
+
+//
+// Used for keeping track of the attachment information...
+//
+typedef struct {
+ char *displayName;
+ char *urlSpec;
+ char *contentType;
+ bool isExternalAttachment;
+} attachmentInfoType;
+
+//
+// For header info...
+//
+typedef struct {
+ char *name;
+ char *value;
+} headerInfoType;
+
+class nsMimeBaseEmitter : public nsIMimeEmitter,
+ public nsIInterfaceRequestor
+{
+public:
+ nsMimeBaseEmitter ();
+
+ // nsISupports interface
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ NS_DECL_NSIMIMEEMITTER
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ // Utility output functions...
+ NS_IMETHOD UtilityWrite(const nsACString &buf);
+ NS_IMETHOD UtilityWriteCRLF(const char *buf);
+
+ // For string bundle usage...
+ char *MimeGetStringByName(const char *aHeaderName);
+ char *MimeGetStringByID(int32_t aID);
+ char *LocalizeHeaderName(const char *aHeaderName, const char *aDefaultName);
+
+ // For header processing...
+ const char *GetHeaderValue(const char *aHeaderName);
+
+ // To write out a stored header array as HTML
+ virtual nsresult WriteHeaderFieldHTMLPrefix(const nsACString &name);
+ virtual nsresult WriteHeaderFieldHTML(const char *field, const char *value);
+ virtual nsresult WriteHeaderFieldHTMLPostfix();
+
+protected:
+ virtual ~nsMimeBaseEmitter();
+ // Internal methods...
+ void CleanupHeaderArray(nsTArray<headerInfoType*> *aArray);
+
+ // For header output...
+ nsresult DumpSubjectFromDate();
+ nsresult DumpToCC();
+ nsresult DumpRestOfHeaders();
+ nsresult OutputGenericHeader(const char *aHeaderVal);
+
+ nsresult WriteHelper(const nsACString &buf, uint32_t *countWritten);
+
+ // For string bundle usage...
+ nsCOMPtr<nsIStringBundle> m_stringBundle; // for translated strings
+ nsCOMPtr<nsIStringBundle> m_headerStringBundle; // for non-translated header strings
+
+ // For buffer management on output
+ MimeRebuffer *mBufferMgr;
+
+ // mscott
+ // don't ref count the streams....the emitter is owned by the converter
+ // which owns these streams...
+ //
+ nsIOutputStream *mOutStream;
+ nsIInputStream *mInputStream;
+ nsIStreamListener *mOutListener;
+ nsCOMPtr<nsIChannel> mChannel;
+
+ // For gathering statistics on processing...
+ uint32_t mTotalWritten;
+ uint32_t mTotalRead;
+
+ // Output control and info...
+ bool mDocHeader; // For header determination...
+ nsIURI *mURL; // the url for the data being processed...
+ int32_t mHeaderDisplayType; // The setting for header output...
+ nsCString mHTMLHeaders; // HTML Header Data...
+
+ // For attachment processing...
+ int32_t mAttachCount;
+ nsTArray<attachmentInfoType*> *mAttachArray;
+ attachmentInfoType *mCurrentAttachment;
+
+ // For header caching...
+ nsTArray<headerInfoType*> *mHeaderArray;
+ nsTArray<headerInfoType*> *mEmbeddedHeaderArray;
+
+ // For body caching...
+ bool mBodyStarted;
+ nsCString mBody;
+ bool mFirstHeaders;
+
+ // For the format being used...
+ int32_t mFormat;
+
+ // For I18N Conversion...
+ nsCOMPtr<nsIMimeConverter> mUnicodeConverter;
+ nsString mCharset;
+ nsCOMPtr<nsIDateTimeFormat> mDateFormatter;
+ nsresult GenerateDateString(const char * dateString, nsACString& formattedDate,
+ bool showDateForToday);
+ // The caller is expected to free the result of GetLocalizedDateString
+ char* GetLocalizedDateString(const char * dateString);
+};
+
+#endif /* _nsMimeBaseEmitter_h_ */
diff --git a/mailnews/mime/emitters/nsMimeEmitterCID.h b/mailnews/mime/emitters/nsMimeEmitterCID.h
new file mode 100644
index 000000000..f2e8c6039
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimeEmitterCID.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMimeEmitterCID_h__
+#define nsMimeEmitterCID_h__
+
+#include "nsISupports.h"
+#include "nsIFactory.h"
+#include "nsIComponentManager.h"
+
+#define NS_MIME_EMITTER_CONTRACTID_PREFIX \
+ "@mozilla.org/messenger/mimeemitter;1?type="
+
+#define NS_HTML_MIME_EMITTER_CONTRACTID \
+ NS_MIME_EMITTER_CONTRACTID_PREFIX "text/html"
+// {F0A8AF16-DCCE-11d2-A411-00805F613C79}
+#define NS_HTML_MIME_EMITTER_CID \
+ { 0xf0a8af16, 0xdcce, 0x11d2, \
+ { 0xa4, 0x11, 0x0, 0x80, 0x5f, 0x61, 0x3c, 0x79 } }
+
+#define NS_XML_MIME_EMITTER_CONTRACTID \
+ NS_MIME_EMITTER_CONTRACTID_PREFIX "text/xml"
+// {977E418F-E392-11d2-A2AC-00A024A7D144}
+#define NS_XML_MIME_EMITTER_CID \
+ { 0x977e418f, 0xe392, 0x11d2, \
+ { 0xa2, 0xac, 0x0, 0xa0, 0x24, 0xa7, 0xd1, 0x44 } }
+
+#define NS_RAW_MIME_EMITTER_CONTRACTID \
+ NS_MIME_EMITTER_CONTRACTID_PREFIX "raw"
+// {F0A8AF16-DCFF-11d2-A411-00805F613C79}
+#define NS_RAW_MIME_EMITTER_CID \
+ { 0xf0a8af16, 0xdcff, 0x11d2, \
+ { 0xa4, 0x11, 0x0, 0x80, 0x5f, 0x61, 0x3c, 0x79 } }
+
+#define NS_XUL_MIME_EMITTER_CONTRACTID \
+ NS_MIME_EMITTER_CONTRACTID_PREFIX "application/vnd.mozilla.xul+xml"
+// {FAA8AF16-DCFF-11d2-A411-00805F613C19}
+#define NS_XUL_MIME_EMITTER_CID \
+ { 0xfaa8af16, 0xdcff, 0x11d2, \
+ { 0xa4, 0x11, 0x0, 0x80, 0x5f, 0x61, 0x3c, 0x19 } }
+
+#define NS_PLAIN_MIME_EMITTER_CONTRACTID \
+ NS_MIME_EMITTER_CONTRACTID_PREFIX "text/plain"
+// {E8892265-7653-46c5-A290-307F3404D0F3}
+#define NS_PLAIN_MIME_EMITTER_CID \
+ { 0xe8892265, 0x7653, 0x46c5, \
+ { 0xa2, 0x90, 0x30, 0x7f, 0x34, 0x4, 0xd0, 0xf3 } }
+
+#endif // nsMimeEmitterCID_h__
diff --git a/mailnews/mime/emitters/nsMimeHtmlEmitter.cpp b/mailnews/mime/emitters/nsMimeHtmlEmitter.cpp
new file mode 100644
index 000000000..d68d7f15c
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimeHtmlEmitter.cpp
@@ -0,0 +1,543 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsCOMPtr.h"
+#include <stdio.h>
+#include "nsMimeRebuffer.h"
+#include "nsMimeHtmlEmitter.h"
+#include "plstr.h"
+#include "nsMailHeaders.h"
+#include "nscore.h"
+#include "nsEmitterUtils.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIMimeStreamConverter.h"
+#include "nsIMsgWindow.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsMimeTypes.h"
+#include "prtime.h"
+#include "prprf.h"
+#include "nsIStringEnumerator.h"
+#include "nsServiceManagerUtils.h"
+// hack: include this to fix opening news attachments.
+#include "nsINntpUrl.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIMimeConverter.h"
+#include "nsMsgMimeCID.h"
+#include "nsMsgUtils.h"
+#include "nsAutoPtr.h"
+#include "nsINetUtil.h"
+#include "nsMemory.h"
+#include "mozilla/Services.h"
+
+#define VIEW_ALL_HEADERS 2
+
+/**
+ * A helper class to implement nsIUTF8StringEnumerator
+ */
+
+class nsMimeStringEnumerator final : public nsIUTF8StringEnumerator {
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIUTF8STRINGENUMERATOR
+
+ nsMimeStringEnumerator() : mCurrentIndex(0) {}
+
+ template<class T>
+ nsCString* Append(T value) { return mValues.AppendElement(value); }
+
+protected:
+ ~nsMimeStringEnumerator() {}
+ nsTArray<nsCString> mValues;
+ uint32_t mCurrentIndex; // consumers expect first-in first-out enumeration
+};
+
+NS_IMPL_ISUPPORTS(nsMimeStringEnumerator, nsIUTF8StringEnumerator)
+
+NS_IMETHODIMP
+nsMimeStringEnumerator::HasMore(bool *result)
+{
+ NS_ENSURE_ARG_POINTER(result);
+ *result = mCurrentIndex < mValues.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimeStringEnumerator::GetNext(nsACString& result)
+{
+ if (mCurrentIndex >= mValues.Length())
+ return NS_ERROR_UNEXPECTED;
+
+ result = mValues[mCurrentIndex++];
+ return NS_OK;
+}
+
+/*
+ * nsMimeHtmlEmitter definitions....
+ */
+nsMimeHtmlDisplayEmitter::nsMimeHtmlDisplayEmitter() : nsMimeBaseEmitter()
+{
+ mFirst = true;
+ mSkipAttachment = false;
+}
+
+nsMimeHtmlDisplayEmitter::~nsMimeHtmlDisplayEmitter(void)
+{
+}
+
+nsresult nsMimeHtmlDisplayEmitter::Init()
+{
+ return NS_OK;
+}
+
+bool nsMimeHtmlDisplayEmitter::BroadCastHeadersAndAttachments()
+{
+ // try to get a header sink if there is one....
+ nsCOMPtr<nsIMsgHeaderSink> headerSink;
+ nsresult rv = GetHeaderSink(getter_AddRefs(headerSink));
+ if (NS_SUCCEEDED(rv) && headerSink && mDocHeader)
+ return true;
+ else
+ return false;
+}
+
+nsresult
+nsMimeHtmlDisplayEmitter::WriteHeaderFieldHTMLPrefix(const nsACString &name)
+{
+ if (!BroadCastHeadersAndAttachments() || (mFormat == nsMimeOutput::nsMimeMessagePrintOutput))
+ return nsMimeBaseEmitter::WriteHeaderFieldHTMLPrefix(name);
+ else
+ return NS_OK;
+}
+
+nsresult
+nsMimeHtmlDisplayEmitter::WriteHeaderFieldHTML(const char *field, const char *value)
+{
+ if (!BroadCastHeadersAndAttachments() || (mFormat == nsMimeOutput::nsMimeMessagePrintOutput))
+ return nsMimeBaseEmitter::WriteHeaderFieldHTML(field, value);
+ else
+ return NS_OK;
+}
+
+nsresult
+nsMimeHtmlDisplayEmitter::WriteHeaderFieldHTMLPostfix()
+{
+ if (!BroadCastHeadersAndAttachments() || (mFormat == nsMimeOutput::nsMimeMessagePrintOutput))
+ return nsMimeBaseEmitter::WriteHeaderFieldHTMLPostfix();
+ else
+ return NS_OK;
+}
+
+nsresult
+nsMimeHtmlDisplayEmitter::GetHeaderSink(nsIMsgHeaderSink ** aHeaderSink)
+{
+ nsresult rv = NS_OK;
+ if ( (mChannel) && (!mHeaderSink) )
+ {
+ nsCOMPtr<nsIURI> uri;
+ mChannel->GetURI(getter_AddRefs(uri));
+ if (uri)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> msgurl (do_QueryInterface(uri));
+ if (msgurl)
+ {
+ msgurl->GetMsgHeaderSink(getter_AddRefs(mHeaderSink));
+ if (!mHeaderSink) // if the url is not overriding the header sink, then just get the one from the msg window
+ {
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ msgurl->GetMsgWindow(getter_AddRefs(msgWindow));
+ if (msgWindow)
+ msgWindow->GetMsgHeaderSink(getter_AddRefs(mHeaderSink));
+ }
+ }
+ }
+ }
+
+ *aHeaderSink = mHeaderSink;
+ NS_IF_ADDREF(*aHeaderSink);
+ return rv;
+}
+
+nsresult nsMimeHtmlDisplayEmitter::BroadcastHeaders(nsIMsgHeaderSink * aHeaderSink, int32_t aHeaderMode, bool aFromNewsgroup)
+{
+ // two string enumerators to pass out to the header sink
+ RefPtr<nsMimeStringEnumerator> headerNameEnumerator = new nsMimeStringEnumerator();
+ NS_ENSURE_TRUE(headerNameEnumerator, NS_ERROR_OUT_OF_MEMORY);
+ RefPtr<nsMimeStringEnumerator> headerValueEnumerator = new nsMimeStringEnumerator();
+ NS_ENSURE_TRUE(headerValueEnumerator, NS_ERROR_OUT_OF_MEMORY);
+
+ nsCString extraExpandedHeaders;
+ nsTArray<nsCString> extraExpandedHeadersArray;
+ nsAutoCString convertedDateString;
+
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (pPrefBranch)
+ {
+ pPrefBranch->GetCharPref("mailnews.headers.extraExpandedHeaders", getter_Copies(extraExpandedHeaders));
+ // todo - should make this upper case
+ if (!extraExpandedHeaders.IsEmpty())
+ {
+ ToLowerCase(extraExpandedHeaders);
+ ParseString(extraExpandedHeaders, ' ', extraExpandedHeadersArray);
+ }
+ }
+
+ for (size_t i = 0; i < mHeaderArray->Length(); i++)
+ {
+ headerInfoType * headerInfo = mHeaderArray->ElementAt(i);
+ if ( (!headerInfo) || (!headerInfo->name) || (!(*headerInfo->name)) || (!headerInfo->value) || (!(*headerInfo->value)))
+ continue;
+
+ const char * headerValue = headerInfo->value;
+
+ // optimization: if we aren't in view all header view mode, we only show a small set of the total # of headers.
+ // don't waste time sending those out to the UI since the UI is going to ignore them anyway.
+ if (aHeaderMode != VIEW_ALL_HEADERS && (mFormat != nsMimeOutput::nsMimeMessageFilterSniffer))
+ {
+ nsDependentCString headerStr(headerInfo->name);
+ if (PL_strcasecmp("to", headerInfo->name) && PL_strcasecmp("from", headerInfo->name) &&
+ PL_strcasecmp("cc", headerInfo->name) && PL_strcasecmp("newsgroups", headerInfo->name) &&
+ PL_strcasecmp("bcc", headerInfo->name) && PL_strcasecmp("followup-to", headerInfo->name) &&
+ PL_strcasecmp("reply-to", headerInfo->name) && PL_strcasecmp("subject", headerInfo->name) &&
+ PL_strcasecmp("organization", headerInfo->name) && PL_strcasecmp("user-agent", headerInfo->name) &&
+ PL_strcasecmp("content-base", headerInfo->name) && PL_strcasecmp("sender", headerInfo->name) &&
+ PL_strcasecmp("date", headerInfo->name) && PL_strcasecmp("x-mailer", headerInfo->name) &&
+ PL_strcasecmp("content-type", headerInfo->name) && PL_strcasecmp("message-id", headerInfo->name) &&
+ PL_strcasecmp("x-newsreader", headerInfo->name) && PL_strcasecmp("x-mimeole", headerInfo->name) &&
+ PL_strcasecmp("references", headerInfo->name) && PL_strcasecmp("in-reply-to", headerInfo->name) &&
+ PL_strcasecmp("list-post", headerInfo->name) && PL_strcasecmp("delivered-to", headerInfo->name) &&
+ // make headerStr lower case because IndexOf is case-sensitive
+ (!extraExpandedHeadersArray.Length() || (ToLowerCase(headerStr),
+ !extraExpandedHeadersArray.Contains(headerStr))))
+ continue;
+ }
+
+ headerNameEnumerator->Append(headerInfo->name);
+ headerValueEnumerator->Append(headerValue);
+
+ // Add a localized version of the date header if we encounter it.
+ if (!PL_strcasecmp("Date", headerInfo->name))
+ {
+ headerNameEnumerator->Append("X-Mozilla-LocalizedDate");
+ GenerateDateString(headerValue, convertedDateString, false);
+ headerValueEnumerator->Append(convertedDateString);
+ }
+ }
+
+ aHeaderSink->ProcessHeaders(headerNameEnumerator, headerValueEnumerator, aFromNewsgroup);
+ return rv;
+}
+
+NS_IMETHODIMP nsMimeHtmlDisplayEmitter::WriteHTMLHeaders(const nsACString &name)
+{
+ // if we aren't broadcasting headers OR printing...just do whatever
+ // our base class does...
+ if (mFormat == nsMimeOutput::nsMimeMessagePrintOutput)
+ {
+ return nsMimeBaseEmitter::WriteHTMLHeaders(name);
+ }
+ else if (!BroadCastHeadersAndAttachments() || !mDocHeader)
+ {
+ // This needs to be here to correct the output format if we are
+ // not going to broadcast headers to the XUL document.
+ if (mFormat == nsMimeOutput::nsMimeMessageBodyDisplay)
+ mFormat = nsMimeOutput::nsMimeMessagePrintOutput;
+
+ return nsMimeBaseEmitter::WriteHTMLHeaders(name);
+ }
+ else
+ mFirstHeaders = false;
+
+ bool bFromNewsgroups = false;
+ for (size_t j = 0; j < mHeaderArray->Length(); j++)
+ {
+ headerInfoType *headerInfo = mHeaderArray->ElementAt(j);
+ if (!(headerInfo && headerInfo->name && *headerInfo->name))
+ continue;
+
+ if (!PL_strcasecmp("Newsgroups", headerInfo->name))
+ {
+ bFromNewsgroups = true;
+ break;
+ }
+ }
+
+ // try to get a header sink if there is one....
+ nsCOMPtr<nsIMsgHeaderSink> headerSink;
+ nsresult rv = GetHeaderSink(getter_AddRefs(headerSink));
+
+ if (headerSink)
+ {
+ int32_t viewMode = 0;
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (pPrefBranch)
+ rv = pPrefBranch->GetIntPref("mail.show_headers", &viewMode);
+
+ rv = BroadcastHeaders(headerSink, viewMode, bFromNewsgroups);
+ } // if header Sink
+
+ return NS_OK;
+}
+
+nsresult
+nsMimeHtmlDisplayEmitter::EndHeader(const nsACString &name)
+{
+ if (mDocHeader && (mFormat != nsMimeOutput::nsMimeMessageFilterSniffer))
+ {
+ UtilityWriteCRLF("<html>");
+ UtilityWriteCRLF("<head>");
+
+ const char * val = GetHeaderValue(HEADER_SUBJECT); // do not free this value
+ if (val)
+ {
+ char * subject = MsgEscapeHTML(val);
+ if (subject)
+ {
+ int32_t bufLen = strlen(subject) + 16;
+ char *buf = new char[bufLen];
+ if (!buf)
+ return NS_ERROR_OUT_OF_MEMORY;
+ PR_snprintf(buf, bufLen, "<title>%s</title>", subject);
+ UtilityWriteCRLF(buf);
+ delete [] buf;
+ free(subject);
+ }
+ }
+
+ // Stylesheet info!
+ UtilityWriteCRLF("<link rel=\"important stylesheet\" href=\"chrome://messagebody/skin/messageBody.css\">");
+
+ UtilityWriteCRLF("</head>");
+ UtilityWriteCRLF("<body>");
+ }
+
+ WriteHTMLHeaders(name);
+
+ return NS_OK;
+}
+
+nsresult
+nsMimeHtmlDisplayEmitter::StartAttachment(const nsACString &name,
+ const char *contentType,
+ const char *url,
+ bool aIsExternalAttachment)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMsgHeaderSink> headerSink;
+ rv = GetHeaderSink(getter_AddRefs(headerSink));
+
+ if (NS_SUCCEEDED(rv) && headerSink)
+ {
+ nsCString uriString;
+
+ nsCOMPtr<nsIMsgMessageUrl> msgurl (do_QueryInterface(mURL, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ // HACK: news urls require us to use the originalSpec. Everyone
+ // else uses GetURI to get the RDF resource which describes the message.
+ nsCOMPtr<nsINntpUrl> nntpUrl (do_QueryInterface(mURL, &rv));
+ if (NS_SUCCEEDED(rv) && nntpUrl)
+ rv = msgurl->GetOriginalSpec(getter_Copies(uriString));
+ else
+ rv = msgurl->GetUri(getter_Copies(uriString));
+ }
+
+ // we need to convert the attachment name from UTF-8 to unicode before
+ // we emit it. The attachment name has already been rfc2047 processed
+ // upstream of us. (Namely, mime_decode_filename has been called, deferring
+ // to nsIMimeHeaderParam.decodeParameter.)
+ nsString unicodeHeaderValue;
+ CopyUTF8toUTF16(name, unicodeHeaderValue);
+
+ headerSink->HandleAttachment(contentType, url /* was escapedUrl */,
+ unicodeHeaderValue.get(), uriString.get(),
+ aIsExternalAttachment);
+
+ mSkipAttachment = false;
+ }
+ else if (mFormat == nsMimeOutput::nsMimeMessagePrintOutput)
+ {
+ // then we need to deal with the attachments in the body by inserting
+ // them into a table..
+ rv = StartAttachmentInBody(name, contentType, url);
+ }
+ else
+ {
+ // If we don't need or cannot broadcast attachment info, just ignore it
+ mSkipAttachment = true;
+ rv = NS_OK;
+ }
+
+ return rv;
+}
+
+// Attachment handling routines
+// Ok, we are changing the way we handle these now...It used to be that we output
+// HTML to make a clickable link, etc... but now, this should just be informational
+// and only show up during printing
+// XXX should they also show up during quoting?
+nsresult
+nsMimeHtmlDisplayEmitter::StartAttachmentInBody(const nsACString &name,
+ const char *contentType,
+ const char *url)
+{
+ mSkipAttachment = false;
+ bool p7mExternal = false;
+
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (prefs)
+ prefs->GetBoolPref("mailnews.p7m_external", &p7mExternal);
+
+ if ( (contentType) &&
+ ((!p7mExternal && !strcmp(contentType, APPLICATION_XPKCS7_MIME)) ||
+ (!p7mExternal && !strcmp(contentType, APPLICATION_PKCS7_MIME)) ||
+ (!strcmp(contentType, APPLICATION_XPKCS7_SIGNATURE)) ||
+ (!strcmp(contentType, APPLICATION_PKCS7_SIGNATURE)) ||
+ (!strcmp(contentType, TEXT_VCARD)))
+ )
+ {
+ mSkipAttachment = true;
+ return NS_OK;
+ }
+
+ if (mFirst)
+ {
+ UtilityWrite("<br><fieldset class=\"mimeAttachmentHeader\">");
+ if (!name.IsEmpty())
+ {
+ nsresult rv;
+
+ nsCOMPtr<nsIStringBundleService> bundleSvc =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleSvc->CreateBundle("chrome://messenger/locale/messenger.properties",
+ getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsString attachmentsHeader;
+ bundle->GetStringFromName(u"attachmentsPrintHeader",
+ getter_Copies(attachmentsHeader));
+
+ UtilityWrite("<legend class=\"mimeAttachmentHeaderName\">");
+ nsCString escapedName;
+ escapedName.Adopt(MsgEscapeHTML(NS_ConvertUTF16toUTF8(attachmentsHeader).get()));
+ UtilityWrite(escapedName.get());
+ UtilityWrite("</legend>");
+ }
+ UtilityWrite("</fieldset>");
+ UtilityWrite("<div class=\"mimeAttachmentWrap\">");
+ UtilityWrite("<table class=\"mimeAttachmentTable\">");
+ }
+
+ UtilityWrite("<tr>");
+
+ UtilityWrite("<td class=\"mimeAttachmentFile\">");
+ UtilityWrite(name);
+ UtilityWrite("</td>");
+
+ mFirst = false;
+ return NS_OK;
+}
+
+nsresult
+nsMimeHtmlDisplayEmitter::AddAttachmentField(const char *field, const char *value)
+{
+ if (mSkipAttachment)
+ return NS_OK;
+
+ // Don't let bad things happen
+ if ( !value || !*value )
+ return NS_OK;
+
+ // Don't output this ugly header...
+ if (!strcmp(field, HEADER_X_MOZILLA_PART_URL))
+ return NS_OK;
+
+ nsCOMPtr<nsIMsgHeaderSink> headerSink;
+ nsresult rv = GetHeaderSink(getter_AddRefs(headerSink));
+ if (NS_SUCCEEDED(rv) && headerSink)
+ {
+ headerSink->AddAttachmentField(field, value);
+ }
+ else
+ {
+ // Currently, we only care about the part size.
+ if (strcmp(field, HEADER_X_MOZILLA_PART_SIZE))
+ return NS_OK;
+
+ uint64_t size = atoi(value);
+ nsAutoString sizeString;
+ rv = FormatFileSize(size, false, sizeString);
+ UtilityWrite("<td class=\"mimeAttachmentSize\">");
+ UtilityWrite(NS_ConvertUTF16toUTF8(sizeString).get());
+ UtilityWrite("</td>");
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsMimeHtmlDisplayEmitter::EndAttachment()
+{
+ if (mSkipAttachment)
+ return NS_OK;
+
+ mSkipAttachment = false; // reset it for next attachment round
+
+ if (BroadCastHeadersAndAttachments())
+ return NS_OK;
+
+ if (mFormat == nsMimeOutput::nsMimeMessagePrintOutput)
+ UtilityWrite("</tr>");
+
+ return NS_OK;
+}
+
+nsresult
+nsMimeHtmlDisplayEmitter::EndAllAttachments()
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMsgHeaderSink> headerSink;
+ rv = GetHeaderSink(getter_AddRefs(headerSink));
+ if (headerSink)
+ headerSink->OnEndAllAttachments();
+
+ if (mFormat == nsMimeOutput::nsMimeMessagePrintOutput)
+ {
+ UtilityWrite("</table>");
+ UtilityWrite("</div>");
+ }
+
+ return rv;
+}
+
+nsresult
+nsMimeHtmlDisplayEmitter::WriteBody(const nsACString &buf,
+ uint32_t *amountWritten)
+{
+ Write(buf, amountWritten);
+ return NS_OK;
+}
+
+nsresult
+nsMimeHtmlDisplayEmitter::EndBody()
+{
+ if (mFormat != nsMimeOutput::nsMimeMessageFilterSniffer)
+ {
+ UtilityWriteCRLF("</body>");
+ UtilityWriteCRLF("</html>");
+ }
+ nsCOMPtr<nsIMsgHeaderSink> headerSink;
+ nsresult rv = GetHeaderSink(getter_AddRefs(headerSink));
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl (do_QueryInterface(mURL, &rv));
+ if (headerSink)
+ headerSink->OnEndMsgHeaders(mailnewsUrl);
+
+ return NS_OK;
+}
+
+
diff --git a/mailnews/mime/emitters/nsMimeHtmlEmitter.h b/mailnews/mime/emitters/nsMimeHtmlEmitter.h
new file mode 100644
index 000000000..886687763
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimeHtmlEmitter.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _nsMimeHtmlEmitter_h_
+#define _nsMimeHtmlEmitter_h_
+
+#include "mozilla/Attributes.h"
+#include "prio.h"
+#include "nsMimeBaseEmitter.h"
+#include "nsMimeRebuffer.h"
+#include "nsIStreamListener.h"
+#include "nsIOutputStream.h"
+#include "nsIInputStream.h"
+#include "nsIURI.h"
+#include "nsIChannel.h"
+#include "nsIMimeMiscStatus.h"
+#include "nsIMimeConverter.h"
+
+class nsMimeHtmlDisplayEmitter : public nsMimeBaseEmitter {
+public:
+ nsMimeHtmlDisplayEmitter ();
+ nsresult Init();
+
+ virtual ~nsMimeHtmlDisplayEmitter (void);
+
+ // Header handling routines.
+ NS_IMETHOD EndHeader(const nsACString &name) override;
+
+ // Attachment handling routines
+ NS_IMETHOD StartAttachment(const nsACString &name,
+ const char *contentType, const char *url,
+ bool aIsExternalAttachment) override;
+ NS_IMETHOD AddAttachmentField(const char *field, const char *value) override;
+ NS_IMETHOD EndAttachment() override;
+ NS_IMETHOD EndAllAttachments() override;
+
+ // Body handling routines
+ NS_IMETHOD WriteBody(const nsACString &buf, uint32_t *amountWritten) override;
+ NS_IMETHOD EndBody() override;
+ NS_IMETHOD WriteHTMLHeaders(const nsACString &name) override;
+
+ virtual nsresult WriteHeaderFieldHTMLPrefix(const nsACString &name
+ ) override;
+ virtual nsresult WriteHeaderFieldHTML(const char *field,
+ const char *value) override;
+ virtual nsresult WriteHeaderFieldHTMLPostfix() override;
+
+protected:
+ bool mFirst; // Attachment flag...
+ bool mSkipAttachment; // attachments we shouldn't show...
+
+ nsCOMPtr<nsIMsgHeaderSink> mHeaderSink;
+
+ nsresult GetHeaderSink(nsIMsgHeaderSink ** aHeaderSink);
+ bool BroadCastHeadersAndAttachments();
+ nsresult StartAttachmentInBody(const nsACString &name,
+ const char *contentType, const char *url);
+
+ nsresult BroadcastHeaders(nsIMsgHeaderSink * aHeaderSink, int32_t aHeaderMode, bool aFromNewsgroup);
+};
+
+
+#endif /* _nsMimeHtmlEmitter_h_ */
diff --git a/mailnews/mime/emitters/nsMimePlainEmitter.cpp b/mailnews/mime/emitters/nsMimePlainEmitter.cpp
new file mode 100644
index 000000000..8e6fae742
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimePlainEmitter.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include "nsMimeRebuffer.h"
+#include "nsMimePlainEmitter.h"
+#include "plstr.h"
+#include "nsMailHeaders.h"
+#include "nscore.h"
+#include "prmem.h"
+#include "nsEmitterUtils.h"
+#include "nsCOMPtr.h"
+#include "nsUnicharUtils.h"
+
+/*
+ * nsMimePlainEmitter definitions....
+ */
+nsMimePlainEmitter::nsMimePlainEmitter()
+{
+}
+
+
+nsMimePlainEmitter::~nsMimePlainEmitter(void)
+{
+}
+
+
+// Header handling routines.
+nsresult
+nsMimePlainEmitter::StartHeader(bool rootMailHeader, bool headerOnly, const char *msgID,
+ const char *outCharset)
+{
+ mDocHeader = rootMailHeader;
+ return NS_OK;
+}
+
+nsresult
+nsMimePlainEmitter::AddHeaderField(const char *field, const char *value)
+{
+ if ( (!field) || (!value) )
+ return NS_OK;
+
+ UtilityWrite(field);
+ UtilityWrite(":\t");
+ UtilityWriteCRLF(value);
+ return NS_OK;
+}
+
+nsresult
+nsMimePlainEmitter::EndHeader(const nsACString &name)
+{
+ UtilityWriteCRLF("");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMimePlainEmitter::WriteBody(const nsACString &buf, uint32_t *amountWritten)
+{
+ Write(buf, amountWritten);
+ return NS_OK;
+}
+
diff --git a/mailnews/mime/emitters/nsMimePlainEmitter.h b/mailnews/mime/emitters/nsMimePlainEmitter.h
new file mode 100644
index 000000000..94cc0cc47
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimePlainEmitter.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _nsMimePlainEmitter_h_
+#define _nsMimePlainEmitter_h_
+
+#include "mozilla/Attributes.h"
+#include "prio.h"
+#include "nsMimeBaseEmitter.h"
+#include "nsMimeRebuffer.h"
+#include "nsIStreamListener.h"
+#include "nsIOutputStream.h"
+#include "nsIURI.h"
+#include "nsIChannel.h"
+
+class nsMimePlainEmitter : public nsMimeBaseEmitter {
+public:
+ nsMimePlainEmitter ();
+ virtual ~nsMimePlainEmitter (void);
+
+ // Header handling routines.
+ NS_IMETHOD StartHeader(bool rootMailHeader, bool headerOnly, const char *msgID,
+ const char *outCharset) override;
+ NS_IMETHOD AddHeaderField(const char *field, const char *value) override;
+ NS_IMETHOD EndHeader(const nsACString &buf) override;
+
+ NS_IMETHOD WriteBody(const nsACString &buf, uint32_t *amountWritten) override;
+};
+
+#endif /* _nsMimePlainEmitter_h_ */
diff --git a/mailnews/mime/emitters/nsMimeRawEmitter.cpp b/mailnews/mime/emitters/nsMimeRawEmitter.cpp
new file mode 100644
index 000000000..28e5f53ec
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimeRawEmitter.cpp
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "nsCOMPtr.h"
+#include <stdio.h>
+#include "nsMimeRebuffer.h"
+#include "nsMimeRawEmitter.h"
+#include "plstr.h"
+#include "nsIMimeEmitter.h"
+#include "nsMailHeaders.h"
+#include "nscore.h"
+#include "prmem.h"
+#include "nsEmitterUtils.h"
+
+/*
+ * nsMimeRawEmitter definitions....
+ */
+nsMimeRawEmitter::nsMimeRawEmitter()
+{
+}
+
+
+nsMimeRawEmitter::~nsMimeRawEmitter(void)
+{
+}
+
+NS_IMETHODIMP
+nsMimeRawEmitter::WriteBody(const nsACString &buf, uint32_t *amountWritten)
+{
+ Write(buf, amountWritten);
+ return NS_OK;
+}
+
diff --git a/mailnews/mime/emitters/nsMimeRawEmitter.h b/mailnews/mime/emitters/nsMimeRawEmitter.h
new file mode 100644
index 000000000..07542efb9
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimeRawEmitter.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _nsMimeRawEmitter_h_
+#define _nsMimeRawEmitter_h_
+
+#include "mozilla/Attributes.h"
+#include "prio.h"
+#include "nsMimeBaseEmitter.h"
+#include "nsMimeRebuffer.h"
+#include "nsIStreamListener.h"
+#include "nsIOutputStream.h"
+#include "nsIURI.h"
+#include "nsIChannel.h"
+
+class nsMimeRawEmitter : public nsMimeBaseEmitter {
+public:
+ nsMimeRawEmitter ();
+ virtual ~nsMimeRawEmitter (void);
+
+ NS_IMETHOD WriteBody(const nsACString &buf,
+ uint32_t *amountWritten) override;
+
+protected:
+};
+
+
+#endif /* _nsMimeRawEmitter_h_ */
diff --git a/mailnews/mime/emitters/nsMimeRebuffer.cpp b/mailnews/mime/emitters/nsMimeRebuffer.cpp
new file mode 100644
index 000000000..0e68a586c
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimeRebuffer.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <string.h>
+#include "nsMimeRebuffer.h"
+#include "prmem.h"
+
+MimeRebuffer::MimeRebuffer(void)
+{
+}
+
+MimeRebuffer::~MimeRebuffer(void)
+{
+}
+
+uint32_t
+MimeRebuffer::GetSize()
+{
+ return mBuf.Length();
+}
+
+uint32_t
+MimeRebuffer::IncreaseBuffer(const nsACString &addBuf)
+{
+ mBuf.Append(addBuf);
+ return mBuf.Length();
+}
+
+uint32_t
+MimeRebuffer::ReduceBuffer(uint32_t numBytes)
+{
+ if (numBytes == 0)
+ return mBuf.Length();
+
+ if (numBytes >= mBuf.Length())
+ {
+ mBuf.Truncate();
+ return 0;
+ }
+
+ mBuf.Cut(0, numBytes);
+ return mBuf.Length();
+}
+
+nsACString &
+MimeRebuffer::GetBuffer()
+{
+ return mBuf;
+}
diff --git a/mailnews/mime/emitters/nsMimeRebuffer.h b/mailnews/mime/emitters/nsMimeRebuffer.h
new file mode 100644
index 000000000..568960206
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimeRebuffer.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _rebuffer_h_
+#define _rebuffer_h_
+
+#include <stdint.h>
+#include "nsStringGlue.h"
+
+//////////////////////////////////////////////////////////////
+// A rebuffering class necessary for stream output buffering
+//////////////////////////////////////////////////////////////
+
+class MimeRebuffer {
+public:
+ MimeRebuffer (void);
+ virtual ~MimeRebuffer (void);
+
+ uint32_t GetSize();
+ uint32_t IncreaseBuffer(const nsACString &addBuf);
+ uint32_t ReduceBuffer(uint32_t numBytes);
+ nsACString & GetBuffer();
+
+protected:
+ nsCString mBuf;
+};
+
+#endif /* _rebuffer_h_ */
diff --git a/mailnews/mime/emitters/nsMimeXmlEmitter.cpp b/mailnews/mime/emitters/nsMimeXmlEmitter.cpp
new file mode 100644
index 000000000..f9cd1ece2
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimeXmlEmitter.cpp
@@ -0,0 +1,184 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+#include "nsMimeRebuffer.h"
+#include "nsMimeXmlEmitter.h"
+#include "plstr.h"
+#include "nsMailHeaders.h"
+#include "nscore.h"
+#include "prmem.h"
+#include "nsEmitterUtils.h"
+#include "nsCOMPtr.h"
+#include "nsUnicharUtils.h"
+#include "nsMsgUtils.h"
+
+/*
+ * nsMimeXmlEmitter definitions....
+ */
+nsMimeXmlEmitter::nsMimeXmlEmitter()
+{
+}
+
+
+nsMimeXmlEmitter::~nsMimeXmlEmitter(void)
+{
+}
+
+
+// Note - this is teardown only...you should not write
+// anything to the stream since these may be image data
+// output streams, etc...
+nsresult
+nsMimeXmlEmitter::Complete()
+{
+ char buf[16];
+
+ // Now write out the total count of attachments for this message
+ UtilityWrite("<mailattachcount>");
+ sprintf(buf, "%d", mAttachCount);
+ UtilityWrite(buf);
+ UtilityWrite("</mailattachcount>");
+
+ UtilityWrite("</message>");
+
+ return nsMimeBaseEmitter::Complete();
+
+}
+
+nsresult
+nsMimeXmlEmitter::WriteXMLHeader(const char *msgID)
+{
+ if ( (!msgID) || (!*msgID) )
+ msgID = "none";
+
+ char *newValue = MsgEscapeHTML(msgID);
+ if (!newValue)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ UtilityWrite("<?xml version=\"1.0\"?>");
+
+ UtilityWriteCRLF("<?xml-stylesheet href=\"chrome://messagebody/skin/messageBody.css\" type=\"text/css\"?>");
+
+ UtilityWrite("<message id=\"");
+ UtilityWrite(newValue);
+ UtilityWrite("\">");
+
+ mXMLHeaderStarted = true;
+ PR_FREEIF(newValue);
+ return NS_OK;
+}
+
+nsresult
+nsMimeXmlEmitter::WriteXMLTag(const char *tagName, const char *value)
+{
+ if ( (!value) || (!*value) )
+ return NS_OK;
+
+ char *upCaseTag = NULL;
+ char *newValue = MsgEscapeHTML(value);
+ if (!newValue)
+ return NS_OK;
+
+ nsCString newTagName(tagName);
+ newTagName.StripWhitespace();
+ ToUpperCase(newTagName);
+ upCaseTag = ToNewCString(newTagName);
+
+ UtilityWrite("<header field=\"");
+ UtilityWrite(upCaseTag);
+ UtilityWrite("\">");
+
+ // Here is where we are going to try to L10N the tagName so we will always
+ // get a field name next to an emitted header value. Note: Default will always
+ // be the name of the header itself.
+ //
+ UtilityWrite("<headerdisplayname>");
+ char *l10nTagName = LocalizeHeaderName(upCaseTag, tagName);
+ if ( (!l10nTagName) || (!*l10nTagName) )
+ UtilityWrite(tagName);
+ else
+ {
+ UtilityWrite(l10nTagName);
+ }
+ PR_FREEIF(l10nTagName);
+
+ UtilityWrite(": ");
+ UtilityWrite("</headerdisplayname>");
+
+ // Now write out the actual value itself and move on!
+ //
+ UtilityWrite(newValue);
+ UtilityWrite("</header>");
+
+ NS_Free(upCaseTag);
+ PR_FREEIF(newValue);
+
+ return NS_OK;
+}
+
+// Header handling routines.
+nsresult
+nsMimeXmlEmitter::StartHeader(bool rootMailHeader, bool headerOnly, const char *msgID,
+ const char *outCharset)
+{
+ mDocHeader = rootMailHeader;
+ WriteXMLHeader(msgID);
+ UtilityWrite("<mailheader>");
+
+ return NS_OK;
+}
+
+nsresult
+nsMimeXmlEmitter::AddHeaderField(const char *field, const char *value)
+{
+ if ( (!field) || (!value) )
+ return NS_OK;
+
+ WriteXMLTag(field, value);
+ return NS_OK;
+}
+
+nsresult
+nsMimeXmlEmitter::EndHeader(const nsACString &name)
+{
+ UtilityWrite("</mailheader>");
+ return NS_OK;
+}
+
+
+// Attachment handling routines
+nsresult
+nsMimeXmlEmitter::StartAttachment(const nsACString &name,
+ const char *contentType,
+ const char *url,
+ bool aIsExternalAttachment)
+{
+ char buf[128];
+
+ ++mAttachCount;
+
+ sprintf(buf, "<mailattachment id=\"%d\">", mAttachCount);
+ UtilityWrite(buf);
+
+ AddAttachmentField(HEADER_PARM_FILENAME, PromiseFlatCString(name).get());
+ return NS_OK;
+}
+
+nsresult
+nsMimeXmlEmitter::AddAttachmentField(const char *field, const char *value)
+{
+ WriteXMLTag(field, value);
+ return NS_OK;
+}
+
+nsresult
+nsMimeXmlEmitter::EndAttachment()
+{
+ UtilityWrite("</mailattachment>");
+ return NS_OK;
+}
+
+
diff --git a/mailnews/mime/emitters/nsMimeXmlEmitter.h b/mailnews/mime/emitters/nsMimeXmlEmitter.h
new file mode 100644
index 000000000..f9e83948d
--- /dev/null
+++ b/mailnews/mime/emitters/nsMimeXmlEmitter.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef _nsMimeXmlEmitter_h_
+#define _nsMimeXmlEmitter_h_
+
+#include "mozilla/Attributes.h"
+#include "prio.h"
+#include "nsMimeBaseEmitter.h"
+#include "nsMimeRebuffer.h"
+#include "nsIStreamListener.h"
+#include "nsIOutputStream.h"
+#include "nsIURI.h"
+#include "nsIChannel.h"
+
+class nsMimeXmlEmitter : public nsMimeBaseEmitter {
+public:
+ nsMimeXmlEmitter ();
+ virtual ~nsMimeXmlEmitter (void);
+
+ NS_IMETHOD Complete() override;
+
+ // Header handling routines.
+ NS_IMETHOD StartHeader(bool rootMailHeader, bool headerOnly, const char *msgID,
+ const char *outCharset) override;
+ NS_IMETHOD AddHeaderField(const char *field, const char *value) override;
+ NS_IMETHOD EndHeader(const nsACString &buf) override;
+
+ // Attachment handling routines
+ NS_IMETHOD StartAttachment(const nsACString &name,
+ const char *contentType, const char *url,
+ bool aIsExternalAttachment) override;
+ NS_IMETHOD AddAttachmentField(const char *field, const char *value) override;
+ NS_IMETHOD EndAttachment() override;
+
+ NS_IMETHOD WriteXMLHeader(const char *msgID);
+ NS_IMETHOD WriteXMLTag(const char *tagName, const char *value);
+
+protected:
+
+ // For header determination...
+ bool mXMLHeaderStarted;
+ int32_t mAttachCount;
+};
+
+#endif /* _nsMimeXmlEmitter_h_ */