From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- comm/mailnews/mime/emitters/moz.build | 18 + comm/mailnews/mime/emitters/nsEmitterUtils.cpp | 45 + comm/mailnews/mime/emitters/nsEmitterUtils.h | 14 + comm/mailnews/mime/emitters/nsMimeBaseEmitter.cpp | 978 +++++++++++++++++++++ comm/mailnews/mime/emitters/nsMimeBaseEmitter.h | 140 +++ comm/mailnews/mime/emitters/nsMimeHtmlEmitter.cpp | 492 +++++++++++ comm/mailnews/mime/emitters/nsMimeHtmlEmitter.h | 50 ++ comm/mailnews/mime/emitters/nsMimePlainEmitter.cpp | 50 ++ comm/mailnews/mime/emitters/nsMimePlainEmitter.h | 26 + comm/mailnews/mime/emitters/nsMimeRawEmitter.cpp | 24 + comm/mailnews/mime/emitters/nsMimeRawEmitter.h | 22 + comm/mailnews/mime/emitters/nsMimeRebuffer.cpp | 33 + comm/mailnews/mime/emitters/nsMimeRebuffer.h | 29 + comm/mailnews/mime/emitters/nsMimeXmlEmitter.cpp | 152 ++++ comm/mailnews/mime/emitters/nsMimeXmlEmitter.h | 41 + 15 files changed, 2114 insertions(+) create mode 100644 comm/mailnews/mime/emitters/moz.build create mode 100644 comm/mailnews/mime/emitters/nsEmitterUtils.cpp create mode 100644 comm/mailnews/mime/emitters/nsEmitterUtils.h create mode 100644 comm/mailnews/mime/emitters/nsMimeBaseEmitter.cpp create mode 100644 comm/mailnews/mime/emitters/nsMimeBaseEmitter.h create mode 100644 comm/mailnews/mime/emitters/nsMimeHtmlEmitter.cpp create mode 100644 comm/mailnews/mime/emitters/nsMimeHtmlEmitter.h create mode 100644 comm/mailnews/mime/emitters/nsMimePlainEmitter.cpp create mode 100644 comm/mailnews/mime/emitters/nsMimePlainEmitter.h create mode 100644 comm/mailnews/mime/emitters/nsMimeRawEmitter.cpp create mode 100644 comm/mailnews/mime/emitters/nsMimeRawEmitter.h create mode 100644 comm/mailnews/mime/emitters/nsMimeRebuffer.cpp create mode 100644 comm/mailnews/mime/emitters/nsMimeRebuffer.h create mode 100644 comm/mailnews/mime/emitters/nsMimeXmlEmitter.cpp create mode 100644 comm/mailnews/mime/emitters/nsMimeXmlEmitter.h (limited to 'comm/mailnews/mime/emitters') 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 +#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(); + mCurrentAttachment = nullptr; + + // Header cache... + mHeaderArray = new nsTArray(); + + // 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 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* 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 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 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* 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(); + 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* 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 msgurl(do_QueryInterface(mURL)); + if (msgurl) { + nsCOMPtr 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 prefs = + do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr 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 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(""); + mHTMLHeaders.AppendLiteral(""); + + if (mFormat == nsMimeOutput::nsMimeMessageSaveAs) + mHTMLHeaders.AppendLiteral(""); + else + mHTMLHeaders.AppendLiteral( + "
"); + + // 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(""); + else + mHTMLHeaders.AppendLiteral("
"); + + // Now write out the actual value itself and move on! + // + mHTMLHeaders.Append(newValue); + mHTMLHeaders.AppendLiteral(""); + + mHTMLHeaders.AppendLiteral(""); + + 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( + "
"); + if (!name.IsEmpty()) { + mHTMLHeaders.AppendLiteral( + ""); + nsAppendEscapedHTML(name, mHTMLHeaders); + mHTMLHeaders.AppendLiteral(""); + } + mHTMLHeaders.AppendLiteral("
"); + } + + mFirstHeaders = false; + return NS_OK; +} + +nsresult nsMimeBaseEmitter::WriteHeaderFieldHTMLPostfix() { + mHTMLHeaders.AppendLiteral("
"); + 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( + ""); + + // 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("
"); + + 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( + ""); + + 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("
"); + } + + return NS_OK; +} + +nsresult nsMimeBaseEmitter::DumpRestOfHeaders() { + nsTArray* array = + mDocHeader ? mHeaderArray : mEmbeddedHeaderArray; + + 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("
"); + 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 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* 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 m_stringBundle; // for translated strings + nsCOMPtr + 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 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* mAttachArray; + attachmentInfoType* mCurrentAttachment; + + // For header caching... + nsTArray* mHeaderArray; + nsTArray* mEmbeddedHeaderArray; + + // For body caching... + bool mBodyStarted; + nsCString mBody; + bool mFirstHeaders; + + // For the format being used... + int32_t mFormat; + + // For I18N Conversion... + nsCOMPtr 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 +#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 + nsCString* Append(T value) { + return mValues.AppendElement(value); + } + + using nsStringEnumeratorBase::GetNext; + + protected: + ~nsMimeStringEnumerator() {} + nsTArray 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 mailChannel = do_QueryInterface(mChannel, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString extraExpandedHeaders; + nsTArray extraExpandedHeadersArray; + nsCString extraAddonHeaders; + nsTArray extraAddonHeadersArray; + nsAutoCString convertedDateString; + bool pushAllHeaders = false; + bool checkExtraHeaders = false; + bool checkAddonHeaders = false; + nsCString otherHeaders; + nsTArray otherHeadersArray; + bool checkOtherHeaders = false; + + nsCOMPtr 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 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 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"); + UtilityWriteCRLF(""); + UtilityWriteCRLF(""); + + const char* val = GetHeaderValue(HEADER_SUBJECT); // do not free this value + if (val) { + nsCString subject(""); + nsAppendEscapedHTML(nsDependentCString(val), subject); + subject.AppendLiteral(""); + UtilityWriteCRLF(subject.get()); + } + + // Stylesheet info! + UtilityWriteCRLF( + ""); + + UtilityWriteCRLF(""); + UtilityWriteCRLF(""); + } + + 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 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 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 mimeConverter = + do_GetService("@mozilla.org/messenger/mimeconverter;1", &rv); + + if (NS_SUCCEEDED(rv)) { + mimeConverter->DecodeMimeHeaderToUTF8(name, nullptr, false, true, + decodedName); + } + + nsCOMPtr 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 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( + "
"); + if (!name.IsEmpty()) { + nsresult rv; + + nsCOMPtr bundleSvc = + mozilla::components::StringBundle::Service(); + NS_ENSURE_TRUE(bundleSvc, NS_ERROR_UNEXPECTED); + + nsCOMPtr bundle; + rv = bundleSvc->CreateBundle( + "chrome://messenger/locale/messenger.properties", + getter_AddRefs(bundle)); + NS_ENSURE_SUCCESS(rv, rv); + + nsString attachmentsHeader; + bundle->GetStringFromName("attachmentsPrintHeader", attachmentsHeader); + + UtilityWrite( + ""); + nsCString escapedName; + nsAppendEscapedHTML(NS_ConvertUTF16toUTF8(attachmentsHeader), + escapedName); + UtilityWrite(escapedName.get()); + UtilityWrite(""); + } + UtilityWrite("
"); + UtilityWrite("
"); + UtilityWrite(""); + } + + UtilityWrite(""); + + UtilityWrite(""); + + 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 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(""); + + return NS_OK; +} + +nsresult nsMimeHtmlDisplayEmitter::EndAttachment() { + if (!mSkipAttachment) { + UtilityWrite(""); + } + + mSkipAttachment = false; // reset it for next attachment round + return NS_OK; +} + +nsresult nsMimeHtmlDisplayEmitter::EndAllAttachments() { + UtilityWrite("
"); + nsCString escapedName; + nsAppendEscapedHTML(name, escapedName); + UtilityWrite(escapedName.get()); + UtilityWrite(""); + UtilityWrite(NS_ConvertUTF16toUTF8(sizeString).get()); + UtilityWrite("
"); + UtilityWrite("
"); + + // Notify the front end that we've finished reading the body. + nsresult rv; + nsCOMPtr mailChannel = do_QueryInterface(mChannel, &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr 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(""); + UtilityWriteCRLF(""); + } + + // Notify the front end that we've finished reading the body. + nsresult rv; + nsCOMPtr mailChannel = do_QueryInterface(mChannel, &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr 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 +#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 +#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 +#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 +#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 +#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(""); + sprintf(buf, "%d", mAttachCount); + UtilityWrite(buf); + UtilityWrite(""); + + UtilityWrite(""); + + return nsMimeBaseEmitter::Complete(); +} + +nsresult nsMimeXmlEmitter::WriteXMLHeader(const char* msgID) { + if ((!msgID) || (!*msgID)) msgID = "none"; + + nsCString newValue; + nsAppendEscapedHTML(nsDependentCString(msgID), newValue); + + UtilityWrite(""); + + UtilityWriteCRLF( + ""); + + 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("
"); + + // 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(""); + char* l10nTagName = LocalizeHeaderName(upCaseTag, tagName); + if ((!l10nTagName) || (!*l10nTagName)) + UtilityWrite(tagName); + else { + UtilityWrite(l10nTagName); + } + PR_FREEIF(l10nTagName); + + UtilityWrite(": "); + UtilityWrite(""); + + // Now write out the actual value itself and move on! + // + UtilityWrite(newValue.get()); + UtilityWrite("
"); + + 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(""); + + 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(""); + 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, "", 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(""); + 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_ */ -- cgit v1.2.3