diff options
Diffstat (limited to 'mailnews/mime/emitters/nsMimeBaseEmitter.cpp')
-rw-r--r-- | mailnews/mime/emitters/nsMimeBaseEmitter.cpp | 1092 |
1 files changed, 1092 insertions, 0 deletions
diff --git a/mailnews/mime/emitters/nsMimeBaseEmitter.cpp b/mailnews/mime/emitters/nsMimeBaseEmitter.cpp new file mode 100644 index 0000000000..223eef4335 --- /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; +} |