/* -*- 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 #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" 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(); mCurrentAttachment = nullptr; // Header cache... mHeaderArray = new nsTArray(); // Embedded Header Cache... mEmbeddedHeaderArray = nullptr; // HTML Header Data... // mHTMLHeaders = ""; // mCharset = ""; // Init the body... mBodyStarted = false; // mBody = ""; // This is needed for conversion of I18N Strings... mUnicodeConverter = do_GetService(NS_MIME_CONVERTER_CONTRACTID); if (!gMimeEmitterLogModule) gMimeEmitterLogModule = PR_NewLogModule("MIME"); // Do prefs last since we can live without this if it fails... nsCOMPtr pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (pPrefBranch) pPrefBranch->GetIntPref("mail.show_headers", &mHeaderDisplayType); } nsMimeBaseEmitter::~nsMimeBaseEmitter(void) { // Delete the buffer manager... if (mBufferMgr) { delete mBufferMgr; mBufferMgr = nullptr; } // Clean up the attachment array structures... if (mAttachArray) { for (size_t i = 0; i < mAttachArray->Length(); i++) { attachmentInfoType *attachInfo = mAttachArray->ElementAt(i); if (!attachInfo) continue; PR_FREEIF(attachInfo->contentType); if (attachInfo->displayName) 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 *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 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 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 *array = mDocHeader? mHeaderArray : mEmbeddedHeaderArray; if (!array) return nullptr; for (size_t i = 0; i < array->Length(); i++) { headerInfoType *headerInfo = array->ElementAt(i); if ( (!headerInfo) || (!headerInfo->name) || (!(*headerInfo->name)) ) continue; if (!PL_strcasecmp(aHeaderName, headerInfo->name)) { retVal = headerInfo->value; break; } } return retVal; } // // This is called at the start of the header block for all header information in ANY // AND ALL MESSAGES (yes, quoted, attached, etc...) // // NOTE: This will be called even when headers are will not follow. This is // to allow us to be notified of the charset of the original message. This is // important for forward and reply operations // NS_IMETHODIMP nsMimeBaseEmitter::StartHeader(bool rootMailHeader, bool headerOnly, const char *msgID, const char *outCharset) { NS_ENSURE_ARG_POINTER(outCharset); mDocHeader = rootMailHeader; // If this is not the mail messages header, then we need to create // the mEmbeddedHeaderArray structure for use with this internal header // structure. if (!mDocHeader) { if (mEmbeddedHeaderArray) CleanupHeaderArray(mEmbeddedHeaderArray); mEmbeddedHeaderArray = new nsTArray(); NS_ENSURE_TRUE(mEmbeddedHeaderArray, NS_ERROR_OUT_OF_MEMORY); } // If the main doc, check on updated character set if (mDocHeader) UpdateCharacterSet(outCharset); CopyASCIItoUTF16(nsDependentCString(outCharset), mCharset); return NS_OK; } // Ok, if we are here, and we have a aCharset passed in that is not // UTF-8 or US-ASCII, then we should tag the mChannel member with this // charset. This is because replying to messages with specified charset's // need to be tagged as that charset by default. // NS_IMETHODIMP nsMimeBaseEmitter::UpdateCharacterSet(const char *aCharset) { if (aCharset) { nsAutoCString contentType; if (NS_SUCCEEDED(mChannel->GetContentType(contentType)) && !contentType.IsEmpty()) { char *cBegin = contentType.BeginWriting(); const char *cPtr = PL_strcasestr(cBegin, "charset="); if (cPtr) { char *ptr = cBegin; while (*ptr) { if ( (*ptr == ' ') || (*ptr == ';') ) { if ((ptr + 1) >= cPtr) { *ptr = '\0'; break; } } ++ptr; } } // have to set content-type since it could have an embedded null byte mChannel->SetContentType(nsDependentCString(cBegin)); if (PL_strcasecmp(aCharset, "US-ASCII") == 0) { mChannel->SetContentCharset(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 *tPtr; if (mDocHeader) tPtr = mHeaderArray; else tPtr = mEmbeddedHeaderArray; // This is a header so we need to cache and output later. // Ok, now we will setup the header info for the header array! headerInfoType *ptr = (headerInfoType *) PR_NEWZAP(headerInfoType); if ( (ptr) && tPtr) { ptr->name = strdup(field); ptr->value = strdup(value); tPtr->AppendElement(ptr); } return NS_OK; } NS_IMETHODIMP nsMimeBaseEmitter::AddAllHeaders(const nsACString &allheaders) { if (mDocHeader) //We want to set only the main headers of a message, not the potentially embedded one { nsresult rv; nsCOMPtr msgurl (do_QueryInterface(mURL)); if (msgurl) { nsCOMPtr mimeHeaders = do_CreateInstance(NS_IMIMEHEADERS_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); mimeHeaders->Initialize(allheaders); msgurl->SetMimeHeaders(mimeHeaders); } } return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // The following code is responsible for formatting headers in a manner that is // identical to the normal XUL output. //////////////////////////////////////////////////////////////////////////////// nsresult nsMimeBaseEmitter::GenerateDateString(const char * dateString, nsACString &formattedDate, bool showDateForToday) { nsresult rv = NS_OK; 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 prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr dateFormatPrefs; rv = prefs->GetBranch("mailnews.display.", getter_AddRefs(dateFormatPrefs)); NS_ENSURE_SUCCESS(rv, rv); dateFormatPrefs->GetBoolPref("date_senders_timezone", &displaySenderTimezone); 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 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(""); mHTMLHeaders.Append(""); if (mFormat == nsMimeOutput::nsMimeMessageSaveAs) mHTMLHeaders.Append(""); else mHTMLHeaders.Append("
"); // 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(""); else mHTMLHeaders.Append("
"); // Now write out the actual value itself and move on! // mHTMLHeaders.Append(newValue); mHTMLHeaders.Append(""); mHTMLHeaders.Append(""); 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("
"); if (!name.IsEmpty()) { mHTMLHeaders.Append(""); nsCString escapedName; escapedName.Adopt(MsgEscapeHTML(nsCString(name).get())); mHTMLHeaders.Append(escapedName); mHTMLHeaders.Append(""); } mHTMLHeaders.Append("
"); } mFirstHeaders = false; return NS_OK; } nsresult nsMimeBaseEmitter::WriteHeaderFieldHTMLPostfix() { mHTMLHeaders.Append("
"); 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(""); // 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("
"); 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(""); 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("
"); } return NS_OK; } nsresult nsMimeBaseEmitter::DumpRestOfHeaders() { nsTArray *array = mDocHeader? mHeaderArray : mEmbeddedHeaderArray; mHTMLHeaders.Append(""); 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("
"); return NS_OK; } nsresult nsMimeBaseEmitter::OutputGenericHeader(const char *aHeaderVal) { const char *val = GetHeaderValue(aHeaderVal); if (val) return WriteHeaderFieldHTML(aHeaderVal, val); return NS_ERROR_FAILURE; } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // These are the methods that should be implemented by the child class! ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // // This should be implemented by the child class if special processing // needs to be done when the entire message is read. // NS_IMETHODIMP nsMimeBaseEmitter::Complete() { // If we are here and still have data to write, we should try // to flush it...if we try and fail, we should probably return // an error! uint32_t written; nsresult rv = NS_OK; while ( NS_SUCCEEDED(rv) && (mBufferMgr) && (mBufferMgr->GetSize() > 0)) rv = Write(EmptyCString(), &written); if (mOutListener) { uint64_t bytesInStream = 0; mozilla::DebugOnly rv2 = mInputStream->Available(&bytesInStream); NS_ASSERTION(NS_SUCCEEDED(rv2), "Available failed"); if (bytesInStream) { nsCOMPtr 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; }