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