summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/mime/emitters
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mailnews/mime/emitters
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/mailnews/mime/emitters')
-rw-r--r--comm/mailnews/mime/emitters/moz.build18
-rw-r--r--comm/mailnews/mime/emitters/nsEmitterUtils.cpp45
-rw-r--r--comm/mailnews/mime/emitters/nsEmitterUtils.h14
-rw-r--r--comm/mailnews/mime/emitters/nsMimeBaseEmitter.cpp978
-rw-r--r--comm/mailnews/mime/emitters/nsMimeBaseEmitter.h140
-rw-r--r--comm/mailnews/mime/emitters/nsMimeHtmlEmitter.cpp492
-rw-r--r--comm/mailnews/mime/emitters/nsMimeHtmlEmitter.h50
-rw-r--r--comm/mailnews/mime/emitters/nsMimePlainEmitter.cpp50
-rw-r--r--comm/mailnews/mime/emitters/nsMimePlainEmitter.h26
-rw-r--r--comm/mailnews/mime/emitters/nsMimeRawEmitter.cpp24
-rw-r--r--comm/mailnews/mime/emitters/nsMimeRawEmitter.h22
-rw-r--r--comm/mailnews/mime/emitters/nsMimeRebuffer.cpp33
-rw-r--r--comm/mailnews/mime/emitters/nsMimeRebuffer.h29
-rw-r--r--comm/mailnews/mime/emitters/nsMimeXmlEmitter.cpp152
-rw-r--r--comm/mailnews/mime/emitters/nsMimeXmlEmitter.h41
15 files changed, 2114 insertions, 0 deletions
diff --git a/comm/mailnews/mime/emitters/moz.build b/comm/mailnews/mime/emitters/moz.build
new file mode 100644
index 0000000000..7e17ac0b9f
--- /dev/null
+++ b/comm/mailnews/mime/emitters/moz.build
@@ -0,0 +1,18 @@
+# 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 += []
+
+SOURCES += [
+ "nsEmitterUtils.cpp",
+ "nsMimeBaseEmitter.cpp",
+ "nsMimeHtmlEmitter.cpp",
+ "nsMimePlainEmitter.cpp",
+ "nsMimeRawEmitter.cpp",
+ "nsMimeRebuffer.cpp",
+ "nsMimeXmlEmitter.cpp",
+]
+
+FINAL_LIBRARY = "mail"
diff --git a/comm/mailnews/mime/emitters/nsEmitterUtils.cpp b/comm/mailnews/mime/emitters/nsEmitterUtils.cpp
new file mode 100644
index 0000000000..d2e29026ba
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsEmitterUtils.cpp
@@ -0,0 +1,45 @@
+/* -*- 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/. */
+
+#include "nsCOMPtr.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "nsMailHeaders.h"
+#include "nsIMimeEmitter.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_BCC))
+ return true;
+ else
+ return false;
+ }
+
+ return true;
+}
diff --git a/comm/mailnews/mime/emitters/nsEmitterUtils.h b/comm/mailnews/mime/emitters/nsEmitterUtils.h
new file mode 100644
index 0000000000..30499cc84f
--- /dev/null
+++ b/comm/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/comm/mailnews/mime/emitters/nsMimeBaseEmitter.cpp b/comm/mailnews/mime/emitters/nsMimeBaseEmitter.cpp
new file mode 100644
index 0000000000..5d60f050d5
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsMimeBaseEmitter.cpp
@@ -0,0 +1,978 @@
+/* -*- 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/. */
+
+#include "nsCOMPtr.h"
+#include <stdio.h>
+#include "nsMimeBaseEmitter.h"
+#include "nsMailHeaders.h"
+#include "nscore.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "prmem.h"
+#include "nsEmitterUtils.h"
+#include "nsMimeStringResources.h"
+#include "msgCore.h"
+#include "nsEmitterUtils.h"
+#include "nsIMimeStreamConverter.h"
+#include "mozilla/Logging.h"
+#include "prprf.h"
+#include "nsIMimeHeaders.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMsgUtils.h"
+#include "nsTextFormatter.h"
+#include "mozilla/Components.h"
+#include "mozilla/intl/AppDateTimeFormat.h"
+
+static mozilla::LazyLogModule gMimeEmitterLogModule("MIME");
+
+#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("@mozilla.org/messenger/mimeconverter;1");
+
+ // 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) 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) {
+ nsCOMPtr<nsIStringBundleService> sBundleService =
+ mozilla::components::StringBundle::Service();
+ if (sBundleService) {
+ res = sBundleService->CreateBundle(MIME_HEADER_URL,
+ getter_AddRefs(m_headerStringBundle));
+ if (NS_FAILED(res)) return nullptr;
+ }
+ }
+
+ if (m_headerStringBundle) {
+ nsString val;
+
+ res = m_headerStringBundle->GetStringFromName(aHeaderName, 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);
+ }
+ 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::components::StringBundle::Service();
+ if (sBundleService)
+ res = sBundleService->CreateBundle(propertyURL,
+ getter_AddRefs(m_stringBundle));
+ }
+
+ if (m_stringBundle) {
+ nsString val;
+ res = m_stringBundle->GetStringFromID(aID, val);
+
+ if (NS_FAILED(res)) return nullptr;
+
+ return ToNewUTF8String(val);
+ }
+ 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(nsLiteralCString(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, 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("ISO-8859-1"_ns);
+ } 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;
+
+ /**
+ * See if the user wants to have the date displayed in the senders
+ * timezone (including the timezone offset).
+ */
+ bool displaySenderTimezone = 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);
+
+ 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).
+ nsDateFormatSelectorComm 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;
+
+ mozilla::intl::DateTimeFormat::StyleBag style;
+ if (dateFormat == kDateFormatShort) {
+ style.date = mozilla::Some(mozilla::intl::DateTimeFormat::Style::Short);
+ }
+ style.time = mozilla::Some(mozilla::intl::DateTimeFormat::Style::Short);
+ rv = mozilla::intl::AppDateTimeFormat::Format(style, &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
+ nsString tzstring;
+ nsTextFormatter::ssprintf(
+ tzstring, u" %+05d", (senderoffset / 60 * 100) + (senderoffset % 60));
+ formattedDateString.Append(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.date_senders_timezone",
+ &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) {
+ nsCString newValue;
+ 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())
+ nsAppendEscapedHTML(tValue, newValue);
+ else
+ nsAppendEscapedHTML(nsDependentCString(i18nValue), newValue);
+ } else {
+ nsAppendEscapedHTML(nsDependentCString(i18nValue), newValue);
+ }
+
+ free(i18nValue);
+
+ if (newValue.IsEmpty()) return NS_OK;
+
+ mHTMLHeaders.AppendLiteral("<tr>");
+ mHTMLHeaders.AppendLiteral("<td>");
+
+ if (mFormat == nsMimeOutput::nsMimeMessageSaveAs)
+ mHTMLHeaders.AppendLiteral("<b>");
+ else
+ mHTMLHeaders.AppendLiteral(
+ "<div class=\"moz-header-display-name\" 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.AppendLiteral(": ");
+ if (mFormat == nsMimeOutput::nsMimeMessageSaveAs)
+ mHTMLHeaders.AppendLiteral("</b>");
+ else
+ mHTMLHeaders.AppendLiteral("</div>");
+
+ // Now write out the actual value itself and move on!
+ //
+ mHTMLHeaders.Append(newValue);
+ mHTMLHeaders.AppendLiteral("</td>");
+
+ mHTMLHeaders.AppendLiteral("</tr>");
+
+ return NS_OK;
+}
+
+nsresult nsMimeBaseEmitter::WriteHeaderFieldHTMLPrefix(const nsACString& name) {
+ if (((mFormat == nsMimeOutput::nsMimeMessageSaveAs) && (mFirstHeaders)) ||
+ ((mFormat == nsMimeOutput::nsMimeMessagePrintOutput) &&
+ (mFirstHeaders)) ||
+ ((mFormat == nsMimeOutput::nsMimeMessageBodyDisplay) && (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.AppendLiteral(
+ "<br><fieldset class=\"moz-mime-attachment-header\">");
+ if (!name.IsEmpty()) {
+ mHTMLHeaders.AppendLiteral(
+ "<legend class=\"moz-mime-attachment-header-name\">");
+ nsAppendEscapedHTML(name, mHTMLHeaders);
+ mHTMLHeaders.AppendLiteral("</legend>");
+ }
+ mHTMLHeaders.AppendLiteral("</fieldset>");
+ }
+
+ mFirstHeaders = false;
+ return NS_OK;
+}
+
+nsresult nsMimeBaseEmitter::WriteHeaderFieldHTMLPostfix() {
+ mHTMLHeaders.AppendLiteral("<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.AppendLiteral(
+ "<table border=0 cellspacing=0 cellpadding=0 width=\"100%\" "
+ "class=\"moz-header-part1");
+ if (mDocHeader) {
+ mHTMLHeaders.AppendLiteral(" moz-main-header");
+ }
+ mHTMLHeaders.AppendLiteral("\">");
+
+ // 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.AppendLiteral("</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.AppendLiteral(
+ "<table border=0 cellspacing=0 cellpadding=0 width=\"100%\" "
+ "class=\"moz-header-part2");
+ if (mDocHeader) {
+ mHTMLHeaders.AppendLiteral(" moz-main-header");
+ }
+ mHTMLHeaders.AppendLiteral("\">");
+
+ 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.AppendLiteral("</table>");
+ }
+
+ return NS_OK;
+}
+
+nsresult nsMimeBaseEmitter::DumpRestOfHeaders() {
+ nsTArray<headerInfoType*>* array =
+ mDocHeader ? mHeaderArray : mEmbeddedHeaderArray;
+
+ mHTMLHeaders.AppendLiteral(
+ "<table border=0 cellspacing=0 cellpadding=0 width=\"100%\" "
+ "class=\"moz-header-part3");
+ if (mDocHeader) {
+ mHTMLHeaders.AppendLiteral(" moz-main-header");
+ }
+ mHTMLHeaders.AppendLiteral("\">");
+
+ 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.AppendLiteral("</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) {
+ mOutListener->OnDataAvailable(
+ mChannel, 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/comm/mailnews/mime/emitters/nsMimeBaseEmitter.h b/comm/mailnews/mime/emitters/nsMimeBaseEmitter.h
new file mode 100644
index 0000000000..96e7fd7b7f
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsMimeBaseEmitter.h
@@ -0,0 +1,140 @@
+/* -*- 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 "nsIURI.h"
+#include "nsIChannel.h"
+#include "nsIStringBundle.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsIMimeConverter.h"
+#include "nsIInterfaceRequestor.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;
+ 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/comm/mailnews/mime/emitters/nsMimeHtmlEmitter.cpp b/comm/mailnews/mime/emitters/nsMimeHtmlEmitter.cpp
new file mode 100644
index 0000000000..81537c6446
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsMimeHtmlEmitter.cpp
@@ -0,0 +1,492 @@
+/* -*- 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/. */
+
+#include "nsCOMPtr.h"
+#include <stdio.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 "nsStringEnumerator.h"
+#include "nsServiceManagerUtils.h"
+// hack: include this to fix opening news attachments.
+#include "nsINntpUrl.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMsgUtils.h"
+#include "nsMemory.h"
+#include "mozilla/Components.h"
+#include "nsIMailChannel.h"
+#include "nsIProgressEventSink.h"
+
+#define VIEW_ALL_HEADERS 2
+
+/**
+ * A helper class to implement nsIUTF8StringEnumerator
+ */
+
+class nsMimeStringEnumerator final : public nsStringEnumeratorBase {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIUTF8STRINGENUMERATOR
+
+ nsMimeStringEnumerator() : mCurrentIndex(0) {}
+
+ template <class T>
+ nsCString* Append(T value) {
+ return mValues.AppendElement(value);
+ }
+
+ using nsStringEnumeratorBase::GetNext;
+
+ protected:
+ ~nsMimeStringEnumerator() {}
+ nsTArray<nsCString> mValues;
+ uint32_t mCurrentIndex; // consumers expect first-in first-out enumeration
+};
+
+NS_IMPL_ISUPPORTS(nsMimeStringEnumerator, nsIUTF8StringEnumerator,
+ nsIStringEnumerator)
+
+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; }
+
+nsresult nsMimeHtmlDisplayEmitter::WriteHeaderFieldHTMLPrefix(
+ const nsACString& name) {
+ if ((mFormat == nsMimeOutput::nsMimeMessageSaveAs) ||
+ (mFormat == nsMimeOutput::nsMimeMessagePrintOutput) ||
+ (mFormat == nsMimeOutput::nsMimeMessageBodyDisplay))
+ return nsMimeBaseEmitter::WriteHeaderFieldHTMLPrefix(name);
+ else
+ return NS_OK;
+}
+
+nsresult nsMimeHtmlDisplayEmitter::WriteHeaderFieldHTML(const char* field,
+ const char* value) {
+ if ((mFormat == nsMimeOutput::nsMimeMessageSaveAs) ||
+ (mFormat == nsMimeOutput::nsMimeMessagePrintOutput) ||
+ (mFormat == nsMimeOutput::nsMimeMessageBodyDisplay))
+ return nsMimeBaseEmitter::WriteHeaderFieldHTML(field, value);
+ else
+ return NS_OK;
+}
+
+nsresult nsMimeHtmlDisplayEmitter::WriteHeaderFieldHTMLPostfix() {
+ if ((mFormat == nsMimeOutput::nsMimeMessageSaveAs) ||
+ (mFormat == nsMimeOutput::nsMimeMessagePrintOutput) ||
+ (mFormat == nsMimeOutput::nsMimeMessageBodyDisplay))
+ return nsMimeBaseEmitter::WriteHeaderFieldHTMLPostfix();
+ else
+ return NS_OK;
+}
+
+nsresult nsMimeHtmlDisplayEmitter::BroadcastHeaders(int32_t aHeaderMode) {
+ nsresult rv;
+ nsCOMPtr<nsIMailChannel> mailChannel = do_QueryInterface(mChannel, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString extraExpandedHeaders;
+ nsTArray<nsCString> extraExpandedHeadersArray;
+ nsCString extraAddonHeaders;
+ nsTArray<nsCString> extraAddonHeadersArray;
+ nsAutoCString convertedDateString;
+ bool pushAllHeaders = false;
+ bool checkExtraHeaders = false;
+ bool checkAddonHeaders = false;
+ nsCString otherHeaders;
+ nsTArray<nsCString> otherHeadersArray;
+ bool checkOtherHeaders = false;
+
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(
+ do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (pPrefBranch) {
+ pPrefBranch->GetCharPref("mailnews.headers.extraExpandedHeaders",
+ extraExpandedHeaders);
+ if (!extraExpandedHeaders.IsEmpty()) {
+ ToLowerCase(extraExpandedHeaders);
+ ParseString(extraExpandedHeaders, ' ', extraExpandedHeadersArray);
+ checkExtraHeaders = true;
+ }
+
+ pPrefBranch->GetCharPref("mailnews.headers.extraAddonHeaders",
+ extraAddonHeaders);
+ if (!extraAddonHeaders.IsEmpty()) {
+ // Push all headers if extraAddonHeaders is "*".
+ if (extraAddonHeaders.EqualsLiteral("*")) {
+ pushAllHeaders = true;
+ } else {
+ ToLowerCase(extraAddonHeaders);
+ ParseString(extraAddonHeaders, ' ', extraAddonHeadersArray);
+ checkAddonHeaders = true;
+ }
+ }
+
+ pPrefBranch->GetCharPref("mail.compose.other.header", otherHeaders);
+ if (!otherHeaders.IsEmpty()) {
+ ToLowerCase(otherHeaders);
+ ParseString(otherHeaders, ',', otherHeadersArray);
+ for (uint32_t i = 0; i < otherHeadersArray.Length(); i++) {
+ otherHeadersArray[i].Trim(" ");
+ }
+
+ checkOtherHeaders = true;
+ }
+ }
+
+ 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;
+
+ // 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)) {
+ bool skip = true;
+ const char* headerName = headerInfo->name;
+ if (pushAllHeaders) {
+ skip = false;
+
+ // Accept the following:
+ } else if (!PL_strcasecmp("to", headerName) ||
+ !PL_strcasecmp("from", headerName) ||
+ !PL_strcasecmp("cc", headerName) ||
+ !PL_strcasecmp("newsgroups", headerName) ||
+ !PL_strcasecmp("bcc", headerName) ||
+ !PL_strcasecmp("followup-to", headerName) ||
+ !PL_strcasecmp("reply-to", headerName) ||
+ !PL_strcasecmp("subject", headerName) ||
+ !PL_strcasecmp("organization", headerName) ||
+ !PL_strcasecmp("user-agent", headerName) ||
+ !PL_strcasecmp("content-base", headerName) ||
+ !PL_strcasecmp("sender", headerName) ||
+ !PL_strcasecmp("date", headerName) ||
+ !PL_strcasecmp("x-mailer", headerName) ||
+ !PL_strcasecmp("content-type", headerName) ||
+ !PL_strcasecmp("message-id", headerName) ||
+ !PL_strcasecmp("x-newsreader", headerName) ||
+ !PL_strcasecmp("x-mimeole", headerName) ||
+ !PL_strcasecmp("references", headerName) ||
+ !PL_strcasecmp("in-reply-to", headerName) ||
+ !PL_strcasecmp("list-post", headerName) ||
+ !PL_strcasecmp("delivered-to", headerName)) {
+ skip = false;
+
+ } else if (checkExtraHeaders || checkAddonHeaders || checkOtherHeaders) {
+ // Make headerStr lowercase because
+ // extraExpandedHeaders/extraAddonHeadersArray was made lowercase above.
+ nsDependentCString headerStr(headerInfo->name);
+ ToLowerCase(headerStr);
+ // Accept if it's an "extra" header.
+ if (checkExtraHeaders && extraExpandedHeadersArray.Contains(headerStr))
+ skip = false;
+ if (checkAddonHeaders && extraAddonHeadersArray.Contains(headerStr))
+ skip = false;
+ if (checkOtherHeaders && otherHeadersArray.Contains(headerStr))
+ skip = false;
+ }
+
+ if (skip) continue;
+ }
+
+ const char* headerValue = headerInfo->value;
+ mailChannel->AddHeaderFromMIME(nsCString(headerInfo->name),
+ nsCString(headerValue));
+
+ // Add a localized version of the date header if we encounter it.
+ if (!PL_strcasecmp("Date", headerInfo->name)) {
+ GenerateDateString(headerValue, convertedDateString, false);
+ mailChannel->AddHeaderFromMIME("X-Mozilla-LocalizedDate"_ns,
+ convertedDateString);
+ }
+ }
+
+ // Notify the front end that the headers are ready on `mailChannel`.
+ nsCOMPtr<nsIMailProgressListener> listener;
+ mailChannel->GetListener(getter_AddRefs(listener));
+ if (listener) {
+ listener->OnHeadersComplete(mailChannel);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMimeHtmlDisplayEmitter::WriteHTMLHeaders(
+ const nsACString& name) {
+ if ((mFormat == nsMimeOutput::nsMimeMessageSaveAs) ||
+ (mFormat == nsMimeOutput::nsMimeMessagePrintOutput) ||
+ (mFormat == nsMimeOutput::nsMimeMessageBodyDisplay)) {
+ nsMimeBaseEmitter::WriteHTMLHeaders(name);
+ }
+
+ if (!mDocHeader) {
+ return NS_OK;
+ }
+
+ nsresult rv;
+ int32_t viewMode = 0;
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(
+ do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && pPrefBranch) {
+ pPrefBranch->GetIntPref("mail.show_headers", &viewMode);
+ }
+
+ return BroadcastHeaders(viewMode);
+}
+
+nsresult nsMimeHtmlDisplayEmitter::EndHeader(const nsACString& name) {
+ if (mDocHeader && (mFormat != nsMimeOutput::nsMimeMessageFilterSniffer)) {
+ // Start with a UTF-8 BOM so this can't be mistaken for another charset.
+ UtilityWriteCRLF("\xEF\xBB\xBF<!DOCTYPE html>");
+ UtilityWriteCRLF("<html>");
+ UtilityWriteCRLF("<head>");
+
+ const char* val = GetHeaderValue(HEADER_SUBJECT); // do not free this value
+ if (val) {
+ nsCString subject("<title>");
+ nsAppendEscapedHTML(nsDependentCString(val), subject);
+ subject.AppendLiteral("</title>");
+ UtilityWriteCRLF(subject.get());
+ }
+
+ // 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;
+
+ 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(uriString);
+ else
+ rv = msgurl->GetUri(uriString);
+ }
+
+ // The attachment name has already been RFC2047 processed
+ // upstream of us. (Namely, mime_decode_filename has been called, deferring
+ // to nsIMimeHeaderParam.decodeParameter.)
+ // But we'l send it through decoding ourselves as well, since we do some
+ // more adjustments, such as removing spoofy chars.
+
+ nsCString decodedName(name);
+ nsCOMPtr<nsIMimeConverter> mimeConverter =
+ do_GetService("@mozilla.org/messenger/mimeconverter;1", &rv);
+
+ if (NS_SUCCEEDED(rv)) {
+ mimeConverter->DecodeMimeHeaderToUTF8(name, nullptr, false, true,
+ decodedName);
+ }
+
+ nsCOMPtr<nsIMailChannel> mailChannel = do_QueryInterface(mChannel);
+ if (mailChannel) {
+ mailChannel->HandleAttachmentFromMIME(nsDependentCString(contentType),
+ nsDependentCString(url), decodedName,
+ uriString, aIsExternalAttachment);
+ }
+
+ // List the attachments for printing.
+ rv = StartAttachmentInBody(decodedName, contentType, url);
+
+ return rv;
+}
+
+// Attachment handling routines
+
+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)))) {
+ mSkipAttachment = true;
+ return NS_OK;
+ }
+
+ // Add the list of attachments. This is only visible when printing.
+
+ if (mFirst) {
+ UtilityWrite(
+ "<fieldset class=\"moz-mime-attachment-header moz-print-only\">");
+ if (!name.IsEmpty()) {
+ nsresult rv;
+
+ nsCOMPtr<nsIStringBundleService> bundleSvc =
+ mozilla::components::StringBundle::Service();
+ 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("attachmentsPrintHeader", attachmentsHeader);
+
+ UtilityWrite(
+ "<legend class=\"moz-mime-attachment-headerName moz-print-only\">");
+ nsCString escapedName;
+ nsAppendEscapedHTML(NS_ConvertUTF16toUTF8(attachmentsHeader),
+ escapedName);
+ UtilityWrite(escapedName.get());
+ UtilityWrite("</legend>");
+ }
+ UtilityWrite("</fieldset>");
+ UtilityWrite("<div class=\"moz-mime-attachment-wrap moz-print-only\">");
+ UtilityWrite("<table class=\"moz-mime-attachment-table\">");
+ }
+
+ UtilityWrite("<tr>");
+
+ UtilityWrite("<td class=\"moz-mime-attachment-file\">");
+ nsCString escapedName;
+ nsAppendEscapedHTML(name, escapedName);
+ UtilityWrite(escapedName.get());
+ 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<nsIMailChannel> mailChannel = do_QueryInterface(mChannel);
+ if (mailChannel) {
+ mailChannel->AddAttachmentFieldFromMIME(nsDependentCString(field),
+ nsDependentCString(value));
+ }
+
+ // 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;
+ FormatFileSize(size, false, sizeString);
+ UtilityWrite("<td class=\"moz-mime-attachment-size\">");
+ UtilityWrite(NS_ConvertUTF16toUTF8(sizeString).get());
+ UtilityWrite("</td>");
+
+ return NS_OK;
+}
+
+nsresult nsMimeHtmlDisplayEmitter::EndAttachment() {
+ if (!mSkipAttachment) {
+ UtilityWrite("</tr>");
+ }
+
+ mSkipAttachment = false; // reset it for next attachment round
+ return NS_OK;
+}
+
+nsresult nsMimeHtmlDisplayEmitter::EndAllAttachments() {
+ UtilityWrite("</table>");
+ UtilityWrite("</div>");
+
+ // Notify the front end that we've finished reading the body.
+ nsresult rv;
+ nsCOMPtr<nsIMailChannel> mailChannel = do_QueryInterface(mChannel, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMailProgressListener> listener;
+ mailChannel->GetListener(getter_AddRefs(listener));
+ if (listener) {
+ listener->OnAttachmentsComplete(mailChannel);
+ }
+
+ return NS_OK;
+}
+
+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>");
+ }
+
+ // Notify the front end that we've finished reading the body.
+ nsresult rv;
+ nsCOMPtr<nsIMailChannel> mailChannel = do_QueryInterface(mChannel, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMailProgressListener> listener;
+ mailChannel->GetListener(getter_AddRefs(listener));
+ if (listener) {
+ listener->OnBodyComplete(mailChannel);
+ }
+
+ return NS_OK;
+}
diff --git a/comm/mailnews/mime/emitters/nsMimeHtmlEmitter.h b/comm/mailnews/mime/emitters/nsMimeHtmlEmitter.h
new file mode 100644
index 0000000000..818a5179dd
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsMimeHtmlEmitter.h
@@ -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/. */
+#ifndef _nsMimeHtmlEmitter_h_
+#define _nsMimeHtmlEmitter_h_
+
+#include "mozilla/Attributes.h"
+#include "prio.h"
+#include "nsMimeBaseEmitter.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...
+
+ nsresult StartAttachmentInBody(const nsACString& name,
+ const char* contentType, const char* url);
+
+ nsresult BroadcastHeaders(int32_t aHeaderMode);
+};
+
+#endif /* _nsMimeHtmlEmitter_h_ */
diff --git a/comm/mailnews/mime/emitters/nsMimePlainEmitter.cpp b/comm/mailnews/mime/emitters/nsMimePlainEmitter.cpp
new file mode 100644
index 0000000000..1a74e10417
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsMimePlainEmitter.cpp
@@ -0,0 +1,50 @@
+/* -*- 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/. */
+
+#include <stdio.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/comm/mailnews/mime/emitters/nsMimePlainEmitter.h b/comm/mailnews/mime/emitters/nsMimePlainEmitter.h
new file mode 100644
index 0000000000..e3447f29ba
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsMimePlainEmitter.h
@@ -0,0 +1,26 @@
+/* -*- 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"
+
+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/comm/mailnews/mime/emitters/nsMimeRawEmitter.cpp b/comm/mailnews/mime/emitters/nsMimeRawEmitter.cpp
new file mode 100644
index 0000000000..7f7115017d
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsMimeRawEmitter.cpp
@@ -0,0 +1,24 @@
+/* -*- 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/. */
+
+#include "nsCOMPtr.h"
+#include <stdio.h>
+#include "nsMimeRawEmitter.h"
+#include "plstr.h"
+#include "nscore.h"
+#include "prmem.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/comm/mailnews/mime/emitters/nsMimeRawEmitter.h b/comm/mailnews/mime/emitters/nsMimeRawEmitter.h
new file mode 100644
index 0000000000..3b6fdf2276
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsMimeRawEmitter.h
@@ -0,0 +1,22 @@
+/* -*- 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"
+
+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/comm/mailnews/mime/emitters/nsMimeRebuffer.cpp b/comm/mailnews/mime/emitters/nsMimeRebuffer.cpp
new file mode 100644
index 0000000000..136ad7553d
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsMimeRebuffer.cpp
@@ -0,0 +1,33 @@
+/* -*- 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/. */
+
+#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/comm/mailnews/mime/emitters/nsMimeRebuffer.h b/comm/mailnews/mime/emitters/nsMimeRebuffer.h
new file mode 100644
index 0000000000..8f22c35961
--- /dev/null
+++ b/comm/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 "nsString.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/comm/mailnews/mime/emitters/nsMimeXmlEmitter.cpp b/comm/mailnews/mime/emitters/nsMimeXmlEmitter.cpp
new file mode 100644
index 0000000000..30c2b5eac3
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsMimeXmlEmitter.cpp
@@ -0,0 +1,152 @@
+/* -*- 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/. */
+
+#include <stdio.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";
+
+ nsCString newValue;
+ nsAppendEscapedHTML(nsDependentCString(msgID), newValue);
+
+ UtilityWrite("<?xml version=\"1.0\"?>");
+
+ UtilityWriteCRLF(
+ "<?xml-stylesheet href=\"chrome://messagebody/skin/messageBody.css\" "
+ "type=\"text/css\"?>");
+
+ UtilityWrite("<message id=\"");
+ UtilityWrite(newValue.get());
+ UtilityWrite("\">");
+
+ mXMLHeaderStarted = true;
+ return NS_OK;
+}
+
+nsresult nsMimeXmlEmitter::WriteXMLTag(const char* tagName, const char* value) {
+ if ((!value) || (!*value)) return NS_OK;
+
+ char* upCaseTag = NULL;
+ nsCString newValue;
+ nsAppendEscapedHTML(nsDependentCString(value), newValue);
+
+ 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.get());
+ UtilityWrite("</header>");
+
+ free(upCaseTag);
+
+ 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/comm/mailnews/mime/emitters/nsMimeXmlEmitter.h b/comm/mailnews/mime/emitters/nsMimeXmlEmitter.h
new file mode 100644
index 0000000000..8c6f2d29ba
--- /dev/null
+++ b/comm/mailnews/mime/emitters/nsMimeXmlEmitter.h
@@ -0,0 +1,41 @@
+/* -*- 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"
+
+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_ */