summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/import/src/nsOutlookCompose.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/import/src/nsOutlookCompose.cpp')
-rw-r--r--comm/mailnews/import/src/nsOutlookCompose.cpp669
1 files changed, 669 insertions, 0 deletions
diff --git a/comm/mailnews/import/src/nsOutlookCompose.cpp b/comm/mailnews/import/src/nsOutlookCompose.cpp
new file mode 100644
index 0000000000..1098dcbab7
--- /dev/null
+++ b/comm/mailnews/import/src/nsOutlookCompose.cpp
@@ -0,0 +1,669 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nscore.h"
+#include "prthread.h"
+#include "nsString.h"
+#include "nsMsgUtils.h"
+#include "nsUnicharUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "nsIURI.h"
+#include "nsMsgI18N.h"
+#include "nsIOutputStream.h"
+#include "nsIInputStream.h"
+#include "nsMsgAttachmentData.h"
+#include "nsIMsgCompFields.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgSend.h"
+#include "nsImportEmbeddedImageData.h"
+#include "nsNetCID.h"
+#include "nsCRT.h"
+#include "nsOutlookCompose.h"
+#include "nsTArray.h"
+
+#include "ImportDebug.h"
+
+#include "nsMimeTypes.h"
+#include "nsMsgUtils.h"
+
+#include "nsIThread.h"
+#include "nsThreadUtils.h"
+
+#include "nsMsgMessageFlags.h"
+#include "nsMsgLocalFolderHdrs.h"
+
+#define NS_MSGCOMPFIELDS_CID \
+ { /* e64b0f51-0d7b-4e2f-8c60-3862ee8c174f */ \
+ 0xe64b0f51, 0x0d7b, 0x4e2f, { \
+ 0x8c, 0x60, 0x38, 0x62, 0xee, 0x8c, 0x17, 0x4f \
+ } \
+ }
+static NS_DEFINE_CID(kMsgCompFieldsCID, NS_MSGCOMPFIELDS_CID);
+
+#ifdef IMPORT_DEBUG
+static const char* p_test_headers =
+ "Received: from netppl.invalid (IDENT:monitor@get.freebsd.because.microsoftsucks.invalid [209.3.31.115])\n\
+ by mail4.sirius.invalid (8.9.1/8.9.1) with SMTP id PAA27232;\n\
+ Mon, 17 May 1999 15:27:43 -0700 (PDT)\n\
+Message-ID: <ikGD3jRTsKklU.Ggm2HmE2A1Jsqd0p@netppl.invalid>\n\
+From: \"adsales@qualityservice.invalid\" <adsales@qualityservice.invalid>\n\
+Subject: Re: Your College Diploma (36822)\n\
+Date: Mon, 17 May 1999 15:09:29 -0400 (EDT)\n\
+MIME-Version: 1.0\n\
+Content-Type: TEXT/PLAIN; charset=\"US-ASCII\"\n\
+Content-Transfer-Encoding: 7bit\n\
+X-UIDL: 19990517.152941\n\
+Status: RO";
+
+static const char* p_test_body =
+ "Hello world?\n\
+";
+#else
+# define p_test_headers nullptr
+# define p_test_body nullptr
+#endif
+
+#define kWhitespace "\b\t\r\n "
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+// A replacement for SimpleBufferTonyRCopiedTwice round-robin buffer and
+// ReadFileState classes
+class CCompositionFile {
+ public:
+ // fifoBuffer is used for memory allocation optimization
+ // convertCRs controls if we want to convert standalone CRs to CRLFs
+ CCompositionFile(nsIFile* aFile, void* fifoBuffer, uint32_t fifoBufferSize,
+ bool convertCRs = false);
+
+ explicit operator bool() const { return m_fileSize && m_pInputStream; }
+
+ // Reads up to and including the term sequence, or entire file if term isn't
+ // found termSize may be used to include NULLs in the terminator sequences.
+ // termSize value of -1 means "zero-terminated string" -> size is calculated
+ // with strlen
+ nsresult ToString(nsCString& dest, const char* term = 0, int termSize = -1);
+ nsresult ToStream(nsIOutputStream* dest, const char* term = 0,
+ int termSize = -1);
+ char LastChar() { return m_lastChar; }
+
+ private:
+ nsCOMPtr<nsIFile> m_pFile;
+ nsCOMPtr<nsIInputStream> m_pInputStream;
+ int64_t m_fileSize;
+ int64_t m_fileReadPos;
+ char* m_fifoBuffer;
+ uint32_t m_fifoBufferSize;
+ char* m_fifoBufferReadPos; // next character to read
+ char* m_fifoBufferWrittenPos; // if we have read less than buffer size then
+ // this will show it
+ bool m_convertCRs;
+ char m_lastChar;
+
+ nsresult EnsureHasDataInBuffer();
+ template <class _OutFn>
+ nsresult ToDest(_OutFn dest, const char* term, int termSize);
+};
+
+//////////////////////////////////////////////////////////////////////////////////////////////////
+
+// First off, a listener
+class OutlookSendListener : public nsIMsgSendListener {
+ public:
+ OutlookSendListener() { m_done = false; }
+
+ // nsISupports interface
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ /* void OnStartSending (in string aMsgID, in uint32_t aMsgSize); */
+ NS_IMETHOD OnStartSending(const char* aMsgID, uint32_t aMsgSize) {
+ return NS_OK;
+ }
+
+ /* void OnProgress (in string aMsgID, in uint32_t aProgress, in uint32_t
+ * aProgressMax); */
+ NS_IMETHOD OnProgress(const char* aMsgID, uint32_t aProgress,
+ uint32_t aProgressMax) {
+ return NS_OK;
+ }
+
+ /* void OnStatus (in string aMsgID, in wstring aMsg); */
+ NS_IMETHOD OnStatus(const char* aMsgID, const char16_t* aMsg) {
+ return NS_OK;
+ }
+
+ /* void OnStopSending (in string aMsgID, in nsresult aStatus, in wstring aMsg,
+ * in nsIFile returnFile); */
+ NS_IMETHOD OnStopSending(const char* aMsgID, nsresult aStatus,
+ const char16_t* aMsg, nsIFile* returnFile) {
+ m_done = true;
+ m_location = returnFile;
+ return NS_OK;
+ }
+
+ /* void OnTransportSecurityError( in string msgID, in nsresult status, in
+ * nsITransportSecurityInfo secInfo, in ACString location); */
+ NS_IMETHOD OnTransportSecurityError(const char* msgID, nsresult status,
+ nsITransportSecurityInfo* secInfo,
+ nsACString const& location) {
+ return NS_OK;
+ }
+
+ /* void OnSendNotPerformed */
+ NS_IMETHOD OnSendNotPerformed(const char* aMsgID, nsresult aStatus) {
+ return NS_OK;
+ }
+
+ /* void OnGetDraftFolderURI (); */
+ NS_IMETHOD OnGetDraftFolderURI(const char* aMsgID,
+ const nsACString& aFolderURI) {
+ return NS_OK;
+ }
+
+ static nsresult CreateSendListener(nsIMsgSendListener** ppListener);
+ void Reset() {
+ m_done = false;
+ m_location = nullptr;
+ }
+
+ public:
+ virtual ~OutlookSendListener() {}
+
+ bool m_done;
+ nsCOMPtr<nsIFile> m_location;
+};
+
+NS_IMPL_ISUPPORTS(OutlookSendListener, nsIMsgSendListener)
+
+nsresult OutlookSendListener::CreateSendListener(
+ nsIMsgSendListener** ppListener) {
+ NS_ENSURE_ARG_POINTER(ppListener);
+ NS_ADDREF(*ppListener = new OutlookSendListener());
+ return NS_OK;
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////
+
+nsOutlookCompose::nsOutlookCompose() {
+ m_optimizationBuffer = new char[FILE_IO_BUFFER_SIZE];
+}
+
+nsOutlookCompose::~nsOutlookCompose() {
+ if (m_pIdentity) {
+ nsresult rv = m_pIdentity->ClearAllValues();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to clear values");
+ if (NS_FAILED(rv)) return;
+ }
+ delete[] m_optimizationBuffer;
+}
+
+nsCOMPtr<nsIMsgIdentity> nsOutlookCompose::m_pIdentity = nullptr;
+
+nsresult nsOutlookCompose::CreateIdentity(void) {
+ if (m_pIdentity) return NS_OK;
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accMgr =
+ do_GetService("@mozilla.org/messenger/account-manager;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = accMgr->CreateIdentity(getter_AddRefs(m_pIdentity));
+ nsString name;
+ name.AssignLiteral("Import Identity");
+ if (m_pIdentity) {
+ m_pIdentity->SetFullName(name);
+ m_pIdentity->SetEmail("import@service.invalid"_ns);
+ }
+ return rv;
+}
+
+void nsOutlookCompose::ReleaseIdentity() { m_pIdentity = nullptr; }
+
+nsresult nsOutlookCompose::CreateComponents(void) {
+ nsresult rv = NS_OK;
+
+ m_pMsgFields = nullptr;
+ if (!m_pListener)
+ rv = OutlookSendListener::CreateSendListener(getter_AddRefs(m_pListener));
+
+ if (NS_SUCCEEDED(rv)) {
+ m_pMsgFields = do_CreateInstance(kMsgCompFieldsCID, &rv);
+ if (NS_SUCCEEDED(rv) && m_pMsgFields) {
+ // IMPORT_LOG0("nsOutlookCompose - CreateComponents succeeded\n");
+ m_pMsgFields->SetForcePlainText(false);
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult nsOutlookCompose::ComposeTheMessage(nsMsgDeliverMode mode,
+ CMapiMessage& msg,
+ nsIFile** pMsg) {
+ nsresult rv = CreateComponents();
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = CreateIdentity();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // IMPORT_LOG0("Outlook Compose created necessary components\n");
+
+ CMapiMessageHeaders* headers = msg.GetHeaders();
+
+ nsString unival;
+ headers->UnfoldValue(CMapiMessageHeaders::hdrFrom, unival,
+ msg.GetBodyCharset());
+ m_pMsgFields->SetFrom(unival);
+ headers->UnfoldValue(CMapiMessageHeaders::hdrTo, unival,
+ msg.GetBodyCharset());
+ m_pMsgFields->SetTo(unival);
+ headers->UnfoldValue(CMapiMessageHeaders::hdrSubject, unival,
+ msg.GetBodyCharset());
+ m_pMsgFields->SetSubject(unival);
+ headers->UnfoldValue(CMapiMessageHeaders::hdrCc, unival,
+ msg.GetBodyCharset());
+ m_pMsgFields->SetCc(unival);
+ headers->UnfoldValue(CMapiMessageHeaders::hdrReplyTo, unival,
+ msg.GetBodyCharset());
+ m_pMsgFields->SetReplyTo(unival);
+ m_pMsgFields->SetMessageId(headers->Value(CMapiMessageHeaders::hdrMessageID));
+
+ // We only use those headers that may need to be processed by Thunderbird
+ // to create a good rfc822 document, or need to be encoded (like To and Cc).
+ // These will replace the originals on import. All the other headers
+ // will be copied to the destination unaltered in CopyComposedMessage().
+
+ nsTArray<RefPtr<nsIMsgAttachedFile>> attachments;
+ msg.GetAttachments(attachments);
+
+ nsString bodyW;
+ bodyW = msg.GetBody();
+
+ nsTArray<RefPtr<nsIMsgEmbeddedImageData>> embeddedObjects;
+
+ if (msg.BodyIsHtml()) {
+ for (unsigned int i = 0; i < msg.EmbeddedAttachmentsCount(); i++) {
+ nsIURI* uri;
+ const char* cid;
+ const char* name;
+ if (msg.GetEmbeddedAttachmentInfo(i, &uri, &cid, &name)) {
+ nsCOMPtr<nsIMsgEmbeddedImageData> imageData =
+ new nsImportEmbeddedImageData(uri, nsDependentCString(cid),
+ nsDependentCString(name));
+ embeddedObjects.AppendElement(imageData);
+ }
+ }
+ }
+
+ nsCString bodyA;
+ const char* charset = msg.GetBodyCharset();
+ nsMsgI18NConvertFromUnicode(
+ charset ? nsDependentCString(charset) : EmptyCString(), bodyW, bodyA);
+
+ nsCOMPtr<nsIImportService> impService(
+ do_GetService(NS_IMPORTSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // nsIImportService::CreateRFC822Message creates a runnable and dispatches to
+ // the main thread.
+ rv = impService->CreateRFC822Message(
+ m_pIdentity, // dummy identity
+ m_pMsgFields, // message fields
+ msg.BodyIsHtml() ? "text/html" : "text/plain",
+ bodyA, // body pointer
+ mode == nsIMsgSend::nsMsgSaveAsDraft,
+ attachments, // local attachments
+ embeddedObjects,
+ m_pListener); // listener
+
+ OutlookSendListener* pListen =
+ static_cast<OutlookSendListener*>(m_pListener.get());
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG1("*** Error, CreateAndSendMessage FAILED: 0x%x\n", rv);
+ } else {
+ // Wait for the listener to get done.
+ nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
+ while (!pListen->m_done) {
+ NS_ProcessNextEvent(thread, true);
+ }
+ }
+
+ if (pListen->m_location) {
+ pListen->m_location->Clone(pMsg);
+ rv = NS_OK;
+ } else {
+ rv = NS_ERROR_FAILURE;
+ IMPORT_LOG0("*** Error, Outlook compose unsuccessful\n");
+ }
+
+ pListen->Reset();
+ return rv;
+}
+
+nsresult nsOutlookCompose::CopyComposedMessage(nsIFile* pSrc,
+ nsIOutputStream* pDst,
+ CMapiMessage& origMsg) {
+ // I'm unsure if we really need the convertCRs feature here.
+ // The headers in the file are generated by TB, the body was generated by rtf
+ // reader that always used CRLF, and the attachments were processed by TB
+ // either... However, I let it stay as it was in the original code.
+ CCompositionFile f(pSrc, m_optimizationBuffer, FILE_IO_BUFFER_SIZE, true);
+ if (!f) {
+ IMPORT_LOG0("*** Error, unexpected zero file size for composed message\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ // The "From ..." separates the messages. Without it, TB cannot see the
+ // messages in the mailbox file. Thus, the lines that look like "From ..." in
+ // the message must be escaped (see EscapeFromSpaceLine())
+ int fromLineLen;
+ const char* fromLine = origMsg.GetFromLine(fromLineLen);
+ uint32_t written;
+ nsresult rv = pDst->Write(fromLine, fromLineLen, &written);
+
+ // Bug 219269
+ // Write out the x-mozilla-status headers.
+ char statusLine[50];
+ uint32_t msgFlags = 0;
+ if (origMsg.IsRead()) msgFlags |= nsMsgMessageFlags::Read;
+ if (!origMsg.FullMessageDownloaded()) msgFlags |= nsMsgMessageFlags::Partial;
+ if (origMsg.IsForvarded()) msgFlags |= nsMsgMessageFlags::Forwarded;
+ if (origMsg.IsReplied()) msgFlags |= nsMsgMessageFlags::Replied;
+ if (origMsg.HasAttach()) msgFlags |= nsMsgMessageFlags::Attachment;
+ _snprintf(statusLine, sizeof(statusLine),
+ X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF);
+ rv = pDst->Write(statusLine, strlen(statusLine), &written);
+ _snprintf(statusLine, sizeof(statusLine),
+ X_MOZILLA_STATUS2_FORMAT MSG_LINEBREAK, msgFlags & 0xFFFF0000);
+ rv = pDst->Write(statusLine, strlen(statusLine), &written);
+ // End Bug 219269
+
+ // well, isn't this a hoot!
+ // Read the headers from the new message, get the ones we like
+ // and write out only the headers we want from the new message,
+ // along with all of the other headers from the "old" message!
+
+ nsCString newHeadersStr;
+ rv = f.ToString(newHeadersStr,
+ MSG_LINEBREAK MSG_LINEBREAK); // Read all the headers
+ NS_ENSURE_SUCCESS(rv, rv);
+ UpdateHeaders(*origMsg.GetHeaders(),
+ CMapiMessageHeaders(newHeadersStr.get()));
+ rv = origMsg.GetHeaders()->ToStream(pDst);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // I use the terminating sequence here to avoid a possible situation when a
+ // "From " line gets split over two sequential reads and thus will not be
+ // escaped. This is done by reading up to CRLF (one line every time), though
+ // it may be slower
+
+ // Here I revert the changes that were made when the multipart/related message
+ // was composed in nsMsgSend::ProcessMultipartRelated() - the Content-Ids of
+ // attachments were replaced with new ones.
+ nsCString line;
+ while (NS_SUCCEEDED(f.ToString(line, MSG_LINEBREAK))) {
+ EscapeFromSpaceLine(pDst, const_cast<char*>(line.get()),
+ line.get() + line.Length());
+ }
+
+ if (f.LastChar() != nsCRT::LF) {
+ rv = pDst->Write(MSG_LINEBREAK, 2, &written);
+ if (written != 2) rv = NS_ERROR_FAILURE;
+ }
+
+ return rv;
+}
+
+nsresult nsOutlookCompose::ProcessMessage(nsMsgDeliverMode mode,
+ CMapiMessage& msg,
+ nsIOutputStream* pDst) {
+ nsCOMPtr<nsIFile> compositionFile;
+ nsresult rv = ComposeTheMessage(mode, msg, getter_AddRefs(compositionFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = CopyComposedMessage(compositionFile, pDst, msg);
+ compositionFile->Remove(false);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error copying composed message to destination mailbox\n");
+ }
+ return rv;
+}
+
+void nsOutlookCompose::UpdateHeader(CMapiMessageHeaders& oldHeaders,
+ const CMapiMessageHeaders& newHeaders,
+ CMapiMessageHeaders::SpecialHeader header,
+ bool addIfAbsent) {
+ const char* oldVal = oldHeaders.Value(header);
+ if (!addIfAbsent && !oldVal) return;
+ const char* newVal = newHeaders.Value(header);
+ if (!newVal) return;
+ // Bug 145150 - Turn "Content-Type: application/ms-tnef" into "Content-Type:
+ // text/plain"
+ // so the body text can be displayed normally (instead of in an
+ // attachment).
+ if (header == CMapiMessageHeaders::hdrContentType)
+ if (stricmp(newVal, "application/ms-tnef") == 0) newVal = "text/plain";
+ // End Bug 145150
+ if (oldVal) {
+ if (strcmp(oldVal, newVal) == 0) return;
+ // Backup the old header value
+ nsCString backupHdrName("X-MozillaBackup-");
+ backupHdrName += CMapiMessageHeaders::SpecialName(header);
+ oldHeaders.SetValue(backupHdrName.get(), oldVal, false);
+ }
+ // Now replace it with new value
+ oldHeaders.SetValue(header, newVal);
+}
+
+void nsOutlookCompose::UpdateHeaders(CMapiMessageHeaders& oldHeaders,
+ const CMapiMessageHeaders& newHeaders) {
+ // Well, ain't this a peach?
+ // This is rather disgusting but there really isn't much to be done about
+ // it....
+
+ // 1. For each "old" header, replace it with the new one if we want,
+ // then right it out.
+ // 2. Then if we haven't written the "important" new headers, write them out
+ // 3. Terminate the headers with an extra eol.
+
+ // Important headers:
+ // "Content-type",
+ // "MIME-Version",
+ // "Content-transfer-encoding"
+ // consider "X-Accept-Language"?
+
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrContentType);
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrMimeVersion);
+ UpdateHeader(oldHeaders, newHeaders,
+ CMapiMessageHeaders::hdrContentTransferEncoding);
+
+ // Other replaced headers (only if they exist):
+ // "From",
+ // "To",
+ // "Subject",
+ // "Reply-to",
+ // "Cc"
+
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrFrom, false);
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrTo, false);
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrSubject, false);
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrReplyTo, false);
+ UpdateHeader(oldHeaders, newHeaders, CMapiMessageHeaders::hdrCc, false);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+CCompositionFile::CCompositionFile(nsIFile* aFile, void* fifoBuffer,
+ uint32_t fifoBufferSize, bool convertCRs)
+ : m_pFile(aFile),
+ m_fileSize(0),
+ m_fileReadPos(0),
+ m_fifoBuffer(static_cast<char*>(fifoBuffer)),
+ m_fifoBufferSize(fifoBufferSize),
+ m_fifoBufferReadPos(static_cast<char*>(fifoBuffer)),
+ m_fifoBufferWrittenPos(static_cast<char*>(fifoBuffer)),
+ m_convertCRs(convertCRs),
+ m_lastChar(0) {
+ m_pFile->GetFileSize(&m_fileSize);
+ if (!m_fileSize) {
+ IMPORT_LOG0("*** Error, unexpected zero file size for composed message\n");
+ return;
+ }
+
+ nsresult rv =
+ NS_NewLocalFileInputStream(getter_AddRefs(m_pInputStream), m_pFile);
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Error, unable to open composed message file\n");
+ return;
+ }
+}
+
+nsresult CCompositionFile::EnsureHasDataInBuffer() {
+ if (m_fifoBufferReadPos < m_fifoBufferWrittenPos) return NS_OK;
+ // Populate the buffer with new data!
+ uint32_t count = m_fifoBufferSize;
+ if ((m_fileReadPos + count) > m_fileSize) count = m_fileSize - m_fileReadPos;
+ if (!count) return NS_ERROR_FAILURE; // Isn't there a "No more data" error?
+
+ uint32_t bytesRead = 0;
+ nsresult rv = m_pInputStream->Read(m_fifoBuffer, count, &bytesRead);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!bytesRead || (bytesRead > count)) return NS_ERROR_FAILURE;
+ m_fifoBufferWrittenPos = m_fifoBuffer + bytesRead;
+ m_fifoBufferReadPos = m_fifoBuffer;
+ m_fileReadPos += bytesRead;
+
+ return NS_OK;
+}
+
+class CTermGuard {
+ public:
+ CTermGuard(const char* term, int termSize)
+ : m_term(term),
+ m_termSize(term ? ((termSize != -1) ? termSize : strlen(term)) : 0),
+ m_matchPos(0) {}
+
+ // if the guard triggered
+ inline bool IsTriggered() const {
+ return m_termSize && (m_matchPos == m_termSize);
+ }
+ // indicates if the guard has something to check
+ inline bool IsChecking() const { return m_termSize; }
+
+ bool Check(char c) // returns true only if the whole sequence passed
+ {
+ if (!m_termSize) // no guard
+ return false;
+ if (m_matchPos >= m_termSize) // check past success!
+ m_matchPos = 0;
+ if (m_term[m_matchPos] != c) // Reset sequence
+ m_matchPos = 0;
+ if (m_term[m_matchPos] == c) { // Sequence continues
+ return ++m_matchPos == m_termSize; // If equal then sequence complete!
+ }
+ // Sequence broken
+ return false;
+ }
+
+ private:
+ const char* m_term;
+ int m_termSize;
+ int m_matchPos;
+};
+
+template <class _OutFn>
+nsresult CCompositionFile::ToDest(_OutFn dest, const char* term, int termSize) {
+ CTermGuard guard(term, termSize);
+
+ // We already know the required string size, so reduce future reallocations
+ if (!guard.IsChecking() && !m_convertCRs)
+ dest.SetCapacity(m_fileSize - m_fileReadPos);
+
+ bool wasCR = false;
+ char c = 0;
+ nsresult rv;
+ while (NS_SUCCEEDED(rv = EnsureHasDataInBuffer())) {
+ if (!guard.IsChecking() && !m_convertCRs) { // Use efficient algorithm
+ dest.Append(m_fifoBufferReadPos,
+ m_fifoBufferWrittenPos - m_fifoBufferReadPos);
+ } else { // Check character by character to convert CRs and find
+ // terminating sequence
+ while (m_fifoBufferReadPos < m_fifoBufferWrittenPos) {
+ c = *m_fifoBufferReadPos;
+ if (m_convertCRs && wasCR) {
+ wasCR = false;
+ if (c != nsCRT::LF) {
+ const char kTmpLF = nsCRT::LF;
+ dest.Append(&kTmpLF, 1);
+ if (guard.Check(nsCRT::LF)) {
+ c = nsCRT::LF; // save last char
+ break;
+ }
+ }
+ }
+ dest.Append(&c, 1);
+ m_fifoBufferReadPos++;
+
+ if (guard.Check(c)) break;
+
+ if (m_convertCRs && (c == nsCRT::CR)) wasCR = true;
+ }
+ if (guard.IsTriggered()) break;
+ }
+ }
+
+ // check for trailing CR (only if caller didn't specify the terminating
+ // sequence that ends with CR - in this case he knows what he does!)
+ if (m_convertCRs && !guard.IsTriggered() && (c == nsCRT::CR)) {
+ c = nsCRT::LF;
+ dest.Append(&c, 1);
+ }
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ m_lastChar = c;
+ return NS_OK;
+}
+
+class dest_nsCString {
+ public:
+ explicit dest_nsCString(nsCString& str) : m_str(str) { m_str.Truncate(); }
+ void SetCapacity(int32_t sz) { m_str.SetCapacity(sz); }
+ nsresult Append(const char* buf, uint32_t count) {
+ m_str.Append(buf, count);
+ return NS_OK;
+ }
+
+ private:
+ nsCString& m_str;
+};
+
+class dest_Stream {
+ public:
+ explicit dest_Stream(nsIOutputStream* dest) : m_stream(dest) {}
+ void SetCapacity(int32_t) { /*do nothing*/
+ }
+ // const_cast here is due to the poor design of the EscapeFromSpaceLine()
+ // that requires a non-constant pointer while doesn't modify its data
+ nsresult Append(const char* buf, uint32_t count) {
+ return EscapeFromSpaceLine(m_stream, const_cast<char*>(buf), buf + count);
+ }
+
+ private:
+ nsIOutputStream* m_stream;
+};
+
+nsresult CCompositionFile::ToString(nsCString& dest, const char* term,
+ int termSize) {
+ return ToDest(dest_nsCString(dest), term, termSize);
+}
+
+nsresult CCompositionFile::ToStream(nsIOutputStream* dest, const char* term,
+ int termSize) {
+ return ToDest(dest_Stream(dest), term, termSize);
+}