diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mailnews/import/src/nsOutlookCompose.cpp | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/mailnews/import/src/nsOutlookCompose.cpp')
-rw-r--r-- | comm/mailnews/import/src/nsOutlookCompose.cpp | 669 |
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); +} |