summaryrefslogtreecommitdiffstats
path: root/mailnews/local/src/nsPop3Sink.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/local/src/nsPop3Sink.cpp')
-rw-r--r--mailnews/local/src/nsPop3Sink.cpp1026
1 files changed, 1026 insertions, 0 deletions
diff --git a/mailnews/local/src/nsPop3Sink.cpp b/mailnews/local/src/nsPop3Sink.cpp
new file mode 100644
index 000000000..af68ea2be
--- /dev/null
+++ b/mailnews/local/src/nsPop3Sink.cpp
@@ -0,0 +1,1026 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "msgCore.h" // precompiled header...
+#include "nsPop3Sink.h"
+#include "prprf.h"
+#include "prlog.h"
+#include "nscore.h"
+#include <stdio.h>
+#include <time.h>
+#include "nsParseMailbox.h"
+#include "nsIMsgLocalMailFolder.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsLocalUtils.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsIMsgFolder.h" // TO include biffState enum. Change to bool later...
+#include "nsMailHeaders.h"
+#include "nsIMsgAccountManager.h"
+#include "nsILineInputStream.h"
+#include "nsIPop3Protocol.h"
+#include "nsLocalMailFolder.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIPrompt.h"
+#include "nsIPromptService.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIDocShell.h"
+#include "mozIDOMWindow.h"
+#include "nsEmbedCID.h"
+#include "nsMsgUtils.h"
+#include "nsMsgBaseCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIPop3Service.h"
+#include "nsMsgLocalCID.h"
+#include "mozilla/Services.h"
+#include "mozilla/Logging.h"
+
+/* for logging to Error Console */
+#include "nsIScriptError.h"
+#include "nsIConsoleService.h"
+
+extern PRLogModuleInfo *POP3LOGMODULE; // defined in nsPop3Protocol.cpp
+#define POP3LOG(str) "%s sink: [this=%p] " str, POP3LOGMODULE->name, this
+
+NS_IMPL_ISUPPORTS(nsPop3Sink, nsIPop3Sink)
+
+nsPop3Sink::nsPop3Sink()
+{
+ m_authed = false;
+ m_downloadingToTempFile = false;
+ m_biffState = 0;
+ m_numNewMessages = 0;
+ m_numNewMessagesInFolder = 0;
+ m_numMsgsDownloaded = 0;
+ m_senderAuthed = false;
+ m_outFileStream = nullptr;
+ m_uidlDownload = false;
+ m_buildMessageUri = false;
+ if (!POP3LOGMODULE)
+ POP3LOGMODULE = PR_NewLogModule("POP3");
+}
+
+nsPop3Sink::~nsPop3Sink()
+{
+ MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
+ (POP3LOG("Calling ReleaseFolderLock from ~nsPop3Sink")));
+ ReleaseFolderLock();
+}
+
+nsresult
+nsPop3Sink::SetUserAuthenticated(bool authed)
+{
+ m_authed = authed;
+ m_popServer->SetAuthenticated(authed);
+ return NS_OK;
+}
+
+nsresult
+nsPop3Sink::GetUserAuthenticated(bool* authed)
+{
+ return m_popServer->GetAuthenticated(authed);
+}
+
+nsresult
+nsPop3Sink::SetSenderAuthedFlag(void* closure, bool authed)
+{
+ m_authed = authed;
+ return NS_OK;
+}
+
+nsresult
+nsPop3Sink::SetMailAccountURL(const nsACString &urlString)
+{
+ m_accountUrl.Assign(urlString);
+ return NS_OK;
+}
+
+nsresult
+nsPop3Sink::GetMailAccountURL(nsACString &urlString)
+{
+ urlString.Assign(m_accountUrl);
+ return NS_OK;
+}
+
+partialRecord::partialRecord() :
+ m_msgDBHdr(nullptr)
+{
+}
+
+partialRecord::~partialRecord()
+{
+}
+
+// Walk through all the messages in this folder and look for any
+// PARTIAL messages. For each of those, dig thru the mailbox and
+// find the Account that the message belongs to. If that Account
+// matches the current Account, then look for the Uidl and save
+// this message for later processing.
+nsresult
+nsPop3Sink::FindPartialMessages()
+{
+ nsCOMPtr<nsISimpleEnumerator> messages;
+ bool hasMore = false;
+ bool isOpen = false;
+ nsLocalFolderScanState folderScanState;
+ nsCOMPtr<nsIMsgDatabase> db;
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
+ m_folder->GetMsgDatabase(getter_AddRefs(db));
+ if (!localFolder || !db)
+ return NS_ERROR_FAILURE; // we need it to grub thru the folder
+
+ nsresult rv = db->EnumerateMessages(getter_AddRefs(messages));
+ if (messages)
+ messages->HasMoreElements(&hasMore);
+ while(hasMore && NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsISupports> aSupport;
+ uint32_t flags = 0;
+ rv = messages->GetNext(getter_AddRefs(aSupport));
+ nsCOMPtr<nsIMsgDBHdr> msgDBHdr(do_QueryInterface(aSupport, &rv));
+ msgDBHdr->GetFlags(&flags);
+ if (flags & nsMsgMessageFlags::Partial)
+ {
+ // Open the various streams we need to seek and read from the mailbox
+ if (!isOpen)
+ {
+ rv = localFolder->GetFolderScanState(&folderScanState);
+ if (NS_SUCCEEDED(rv))
+ isOpen = true;
+ else
+ break;
+ }
+ rv = localFolder->GetUidlFromFolder(&folderScanState, msgDBHdr);
+ if (!NS_SUCCEEDED(rv))
+ break;
+
+ // If we got the uidl, see if this partial message belongs to this
+ // account. Add it to the array if so...
+ if (folderScanState.m_uidl &&
+ m_accountKey.Equals(folderScanState.m_accountKey, nsCaseInsensitiveCStringComparator()))
+ {
+ partialRecord *partialMsg = new partialRecord();
+ if (partialMsg)
+ {
+ partialMsg->m_uidl = folderScanState.m_uidl;
+ partialMsg->m_msgDBHdr = msgDBHdr;
+ m_partialMsgsArray.AppendElement(partialMsg);
+ }
+ }
+ }
+ messages->HasMoreElements(&hasMore);
+ }
+ if (isOpen && folderScanState.m_inputStream)
+ folderScanState.m_inputStream->Close();
+ return rv;
+}
+
+// For all the partial messages saved by FindPartialMessages,
+// ask the protocol handler if they still exist on the server.
+// Any messages that don't exist any more are deleted from the
+// msgDB.
+void
+nsPop3Sink::CheckPartialMessages(nsIPop3Protocol *protocol)
+{
+ uint32_t count = m_partialMsgsArray.Length();
+ bool deleted = false;
+
+ for (uint32_t i = 0; i < count; i++)
+ {
+ partialRecord *partialMsg;
+ bool found = true;
+ partialMsg = m_partialMsgsArray.ElementAt(i);
+ protocol->CheckMessage(partialMsg->m_uidl.get(), &found);
+ if (!found && partialMsg->m_msgDBHdr)
+ {
+ if (m_newMailParser)
+ m_newMailParser->m_mailDB->DeleteHeader(partialMsg->m_msgDBHdr, nullptr, false, true);
+ deleted = true;
+ }
+ delete partialMsg;
+ }
+ m_partialMsgsArray.Clear();
+ if (deleted)
+ {
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
+ if (localFolder)
+ localFolder->NotifyDelete();
+ }
+}
+
+nsresult
+nsPop3Sink::BeginMailDelivery(bool uidlDownload, nsIMsgWindow *aMsgWindow, bool* aBool)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
+ if (!server)
+ return NS_ERROR_UNEXPECTED;
+
+ m_window = aMsgWindow;
+
+ nsCOMPtr <nsIMsgAccountManager> acctMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ nsCOMPtr <nsIMsgAccount> account;
+ NS_ENSURE_SUCCESS(rv, rv);
+ acctMgr->FindAccountForServer(server, getter_AddRefs(account));
+ if (account)
+ account->GetKey(m_accountKey);
+
+ bool isLocked;
+ nsCOMPtr <nsISupports> supports = do_QueryInterface(static_cast<nsIPop3Sink*>(this));
+ m_folder->GetLocked(&isLocked);
+ if(!isLocked)
+ {
+ MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
+ (POP3LOG("BeginMailDelivery acquiring semaphore")));
+ m_folder->AcquireSemaphore(supports);
+ }
+ else
+ {
+ MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
+ (POP3LOG("BeginMailDelivery folder locked")));
+ return NS_MSG_FOLDER_BUSY;
+ }
+ m_uidlDownload = uidlDownload;
+ if (!uidlDownload)
+ FindPartialMessages();
+
+ m_folder->GetNumNewMessages(false, &m_numNewMessagesInFolder);
+
+#ifdef DEBUG
+ printf("Begin mail message delivery.\n");
+#endif
+ nsCOMPtr<nsIPop3Service> pop3Service(do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ pop3Service->NotifyDownloadStarted(m_folder);
+ if (aBool)
+ *aBool = true;
+ return NS_OK;
+}
+
+nsresult
+nsPop3Sink::EndMailDelivery(nsIPop3Protocol *protocol)
+{
+ CheckPartialMessages(protocol);
+
+ if (m_newMailParser)
+ {
+ if (m_outFileStream)
+ m_outFileStream->Flush(); // try this.
+ m_newMailParser->OnStopRequest(nullptr, nullptr, NS_OK);
+ m_newMailParser->EndMsgDownload();
+ }
+ if (m_outFileStream)
+ {
+ m_outFileStream->Close();
+ m_outFileStream = nullptr;
+ }
+
+ if (m_downloadingToTempFile)
+ m_tmpDownloadFile->Remove(false);
+
+ // tell the parser to mark the db valid *after* closing the mailbox.
+ if (m_newMailParser)
+ m_newMailParser->UpdateDBFolderInfo();
+
+ MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
+ (POP3LOG("Calling ReleaseFolderLock from EndMailDelivery")));
+ nsresult rv = ReleaseFolderLock();
+ NS_ASSERTION(NS_SUCCEEDED(rv),"folder lock not released successfully");
+
+ bool filtersRun;
+ m_folder->CallFilterPlugins(nullptr, &filtersRun); // ??? do we need msgWindow?
+ int32_t numNewMessagesInFolder;
+ // if filters have marked msgs read or deleted, the num new messages count
+ // will go negative by the number of messages marked read or deleted,
+ // so if we add that number to the number of msgs downloaded, that will give
+ // us the number of actual new messages.
+ m_folder->GetNumNewMessages(false, &numNewMessagesInFolder);
+ m_numNewMessages -= (m_numNewMessagesInFolder - numNewMessagesInFolder);
+ m_folder->SetNumNewMessages(m_numNewMessages); // we'll adjust this for spam later
+ if (!filtersRun && m_numNewMessages > 0)
+ {
+ nsCOMPtr <nsIMsgIncomingServer> server;
+ m_folder->GetServer(getter_AddRefs(server));
+ if (server)
+ {
+ server->SetPerformingBiff(true);
+ m_folder->SetBiffState(m_biffState);
+ server->SetPerformingBiff(false);
+ }
+ }
+ // note that size on disk has possibly changed.
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
+ if (localFolder)
+ (void) localFolder->RefreshSizeOnDisk();
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
+ if (server)
+ {
+ nsCOMPtr <nsIMsgFilterList> filterList;
+ rv = server->GetFilterList(nullptr, getter_AddRefs(filterList));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (filterList)
+ (void) filterList->FlushLogIfNecessary();
+ }
+
+ // fix for bug #161999
+ // we should update the summary totals for the folder (inbox)
+ // in case it's not the open folder
+ m_folder->UpdateSummaryTotals(true);
+
+ // check if the folder open in this window is not the current folder, and if it has new
+ // message, in which case we need to try to run the filter plugin.
+ if (m_newMailParser)
+ {
+ nsCOMPtr <nsIMsgWindow> msgWindow;
+ m_newMailParser->GetMsgWindow(getter_AddRefs(msgWindow));
+ // this breaks down if it's biff downloading new mail because
+ // there's no msgWindow...
+ if (msgWindow)
+ {
+ nsCOMPtr <nsIMsgFolder> openFolder;
+ (void) msgWindow->GetOpenFolder(getter_AddRefs(openFolder));
+ if (openFolder && openFolder != m_folder)
+ {
+ // only call filter plugins if folder is a local folder, because only
+ // local folders get messages filtered into them synchronously by pop3.
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(openFolder);
+ if (localFolder)
+ {
+ bool hasNew, isLocked;
+ (void) openFolder->GetHasNewMessages(&hasNew);
+ if (hasNew)
+ {
+ // if the open folder is locked, we shouldn't run the spam filters
+ // on it because someone is using the folder. see 218433.
+ // Ideally, the filter plugin code would try to grab the folder lock
+ // and hold onto it until done, but that's more difficult and I think
+ // this will actually fix the problem.
+ openFolder->GetLocked(&isLocked);
+ if(!isLocked)
+ openFolder->CallFilterPlugins(nullptr, &filtersRun);
+ }
+ }
+ }
+ }
+ }
+#ifdef DEBUG
+ printf("End mail message delivery.\n");
+#endif
+ nsCOMPtr<nsIPop3Service> pop3Service(do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ pop3Service->NotifyDownloadCompleted(m_folder, m_numNewMessages);
+ return NS_OK;
+}
+
+nsresult
+nsPop3Sink::ReleaseFolderLock()
+{
+ nsresult result = NS_OK;
+ if (!m_folder)
+ return result;
+ bool haveSemaphore;
+ nsCOMPtr <nsISupports> supports = do_QueryInterface(static_cast<nsIPop3Sink*>(this));
+ result = m_folder->TestSemaphore(supports, &haveSemaphore);
+ MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
+ (POP3LOG("ReleaseFolderLock haveSemaphore = %s"), haveSemaphore ? "TRUE" : "FALSE"));
+
+ if(NS_SUCCEEDED(result) && haveSemaphore)
+ result = m_folder->ReleaseSemaphore(supports);
+ return result;
+}
+
+nsresult
+nsPop3Sink::AbortMailDelivery(nsIPop3Protocol *protocol)
+{
+ CheckPartialMessages(protocol);
+
+ // ### PS TODO - discard any new message?
+
+ if (m_outFileStream)
+ {
+ m_outFileStream->Close();
+ m_outFileStream = nullptr;
+ }
+
+ if (m_downloadingToTempFile && m_tmpDownloadFile)
+ m_tmpDownloadFile->Remove(false);
+
+ /* tell the parser to mark the db valid *after* closing the mailbox.
+ we have truncated the inbox, so berkeley mailbox and msf file are in sync*/
+ if (m_newMailParser)
+ m_newMailParser->UpdateDBFolderInfo();
+ MOZ_LOG(POP3LOGMODULE, mozilla::LogLevel::Debug,
+ (POP3LOG("Calling ReleaseFolderLock from AbortMailDelivery")));
+
+ nsresult rv = ReleaseFolderLock();
+ NS_ASSERTION(NS_SUCCEEDED(rv),"folder lock not released successfully");
+
+#ifdef DEBUG
+ printf("Abort mail message delivery.\n");
+#endif
+ nsCOMPtr<nsIPop3Service> pop3Service(do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ pop3Service->NotifyDownloadCompleted(m_folder, 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::IncorporateBegin(const char* uidlString,
+ nsIURI* aURL,
+ uint32_t flags,
+ void** closure)
+{
+#ifdef DEBUG
+ printf("Incorporate message begin:\n");
+ if (uidlString)
+ printf("uidl string: %s\n", uidlString);
+#endif
+ nsCOMPtr<nsIFile> path;
+
+ m_folder->GetFilePath(getter_AddRefs(path));
+
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ if (pPrefBranch)
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ m_folder->GetServer(getter_AddRefs(server));
+ nsCString plugStoreContract;
+ server->GetCharValue("storeContractID", plugStoreContract);
+ // Maildir doesn't care about quaranting, but other stores besides berkeley
+ // mailbox might. We should probably make this an attribute on the pluggable
+ // store, though.
+ if (plugStoreContract.Equals(
+ NS_LITERAL_CSTRING("@mozilla.org/msgstore/berkeleystore;1")))
+ pPrefBranch->GetBoolPref("mailnews.downloadToTempFile", &m_downloadingToTempFile);
+ }
+
+ nsCOMPtr<nsIMsgDBHdr> newHdr;
+
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_popServer);
+ if (!server)
+ return NS_ERROR_UNEXPECTED;
+
+ if (m_downloadingToTempFile)
+ {
+ // need to create an nsIOFileStream from a temp file...
+ nsCOMPtr<nsIFile> tmpDownloadFile;
+ rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
+ "newmsg",
+ getter_AddRefs(tmpDownloadFile));
+
+ NS_ASSERTION(NS_SUCCEEDED(rv),
+ "writing tmp pop3 download file: failed to append filename");
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!m_tmpDownloadFile)
+ {
+ //need a unique tmp file to prevent dataloss in multiuser environment
+ rv = tmpDownloadFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ m_tmpDownloadFile = do_QueryInterface(tmpDownloadFile, &rv);
+ }
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = MsgGetFileStream(m_tmpDownloadFile, getter_AddRefs(m_outFileStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ else
+ {
+ rv = server->GetMsgStore(getter_AddRefs(m_msgStore));
+ bool reusable;
+ NS_ENSURE_SUCCESS(rv, rv);
+ m_msgStore->GetNewMsgOutputStream(m_folder, getter_AddRefs(newHdr),
+ &reusable, getter_AddRefs(m_outFileStream));
+ }
+ // The following (!m_outFileStream etc) was added to make sure that we don't
+ // write somewhere where for some reason or another we can't write to and
+ // lose the messages. See bug 62480
+ if (!m_outFileStream)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr<nsISeekableStream> seekableOutStream = do_QueryInterface(m_outFileStream);
+
+ // create a new mail parser
+ if (!m_newMailParser)
+ m_newMailParser = new nsParseNewMailState;
+ NS_ENSURE_TRUE(m_newMailParser, NS_ERROR_OUT_OF_MEMORY);
+ if (m_uidlDownload)
+ m_newMailParser->DisableFilters();
+
+ nsCOMPtr <nsIMsgFolder> serverFolder;
+ rv = GetServerFolder(getter_AddRefs(serverFolder));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = m_newMailParser->Init(serverFolder, m_folder,
+ m_window, newHdr, m_outFileStream);
+ // If we failed to initialize the parser, then just don't use it!!!
+ // We can still continue without one.
+
+ if (NS_FAILED(rv))
+ {
+ m_newMailParser = nullptr;
+ rv = NS_OK;
+ }
+
+ if (closure)
+ *closure = (void*) this;
+
+#ifdef DEBUG
+ // Debugging, see bug 1116055.
+ int64_t first_pre_seek_pos;
+ nsresult rv3 = seekableOutStream->Tell(&first_pre_seek_pos);
+#endif
+
+ // XXX Handle error such as network error for remote file system.
+ seekableOutStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
+
+#ifdef DEBUG
+ // Debugging, see bug 1116055.
+ int64_t first_post_seek_pos;
+ nsresult rv4 = seekableOutStream->Tell(&first_post_seek_pos);
+ if (NS_SUCCEEDED(rv3) && NS_SUCCEEDED(rv4)) {
+ if (first_pre_seek_pos != first_post_seek_pos) {
+ nsCOMPtr<nsIMsgFolder> localFolder = do_QueryInterface(m_folder);
+ nsString folderName;
+ if (localFolder)
+ localFolder->GetPrettiestName(folderName);
+ if (!folderName.IsEmpty()) {
+ fprintf(stderr,"(seekdebug) Seek was necessary in IncorporateBegin() for folder %s.\n",
+ NS_ConvertUTF16toUTF8(folderName).get());
+ } else {
+ fprintf(stderr,"(seekdebug) Seek was necessary in IncorporateBegin().\n");
+ }
+ fprintf(stderr,"(seekdebug) first_pre_seek_pos = 0x%016llx, first_post_seek_pos=0x%016llx\n",
+ (unsigned long long) first_pre_seek_pos, (unsigned long long) first_post_seek_pos);
+ }
+ }
+#endif
+
+ nsCString outputString(GetDummyEnvelope());
+ rv = WriteLineToMailbox(outputString);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Write out account-key before UIDL so the code that looks for
+ // UIDL will find the account first and know it can stop looking
+ // once it finds the UIDL line.
+ if (!m_accountKey.IsEmpty())
+ {
+ outputString.AssignLiteral(HEADER_X_MOZILLA_ACCOUNT_KEY ": ");
+ outputString.Append(m_accountKey);
+ outputString.AppendLiteral(MSG_LINEBREAK);
+ rv = WriteLineToMailbox(outputString);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ if (uidlString)
+ {
+ outputString.AssignLiteral("X-UIDL: ");
+ outputString.Append(uidlString);
+ outputString.AppendLiteral(MSG_LINEBREAK);
+ rv = WriteLineToMailbox(outputString);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // WriteLineToMailbox("X-Mozilla-Status: 8000" MSG_LINEBREAK);
+ char *statusLine = PR_smprintf(X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK, flags);
+ outputString.Assign(statusLine);
+ rv = WriteLineToMailbox(outputString);
+ PR_smprintf_free(statusLine);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = WriteLineToMailbox(NS_LITERAL_CSTRING("X-Mozilla-Status2: 00000000" MSG_LINEBREAK));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // leave space for 60 bytes worth of keys/tags
+ rv = WriteLineToMailbox(NS_LITERAL_CSTRING(X_MOZILLA_KEYWORDS));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::SetPopServer(nsIPop3IncomingServer *server)
+{
+ m_popServer = server;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::GetPopServer(nsIPop3IncomingServer **aServer)
+{
+ NS_ENSURE_ARG_POINTER(aServer);
+ NS_IF_ADDREF(*aServer = m_popServer);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Sink::GetFolder(nsIMsgFolder **aFolder)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_IF_ADDREF(*aFolder = m_folder);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Sink::SetFolder(nsIMsgFolder * aFolder)
+{
+ m_folder = aFolder;
+ return NS_OK;
+}
+
+nsresult
+nsPop3Sink::GetServerFolder(nsIMsgFolder **aFolder)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+
+ if (m_popServer)
+ {
+ // not sure what this is used for - might be wrong if we have a deferred account.
+ nsCOMPtr <nsIMsgIncomingServer> incomingServer = do_QueryInterface(m_popServer);
+ if (incomingServer)
+ return incomingServer->GetRootFolder(aFolder);
+ }
+ *aFolder = nullptr;
+ return NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP nsPop3Sink::SetMsgsToDownload(uint32_t aNumMessages)
+{
+ m_numNewMessages = aNumMessages;
+ return NS_OK;
+}
+
+char*
+nsPop3Sink::GetDummyEnvelope(void)
+{
+ static char result[75];
+ char *ct;
+ time_t now = time ((time_t *) 0);
+#if defined (XP_WIN)
+ if (now < 0 || now > 0x7FFFFFFF)
+ now = 0x7FFFFFFF;
+#endif
+ ct = ctime(&now);
+ PR_ASSERT(ct[24] == '\r' || ct[24] == '\n');
+ ct[24] = 0;
+ /* This value must be in ctime() format, with English abbreviations.
+ strftime("... %c ...") is no good, because it is localized. */
+ PL_strcpy(result, "From - ");
+ PL_strcpy(result + 7, ct);
+ PL_strcpy(result + 7 + 24, MSG_LINEBREAK);
+ return result;
+}
+
+nsresult
+nsPop3Sink::IncorporateWrite(const char* block,
+ int32_t length)
+{
+ m_outputBuffer.Truncate();
+ if (!strncmp(block, "From ", 5))
+ m_outputBuffer.Assign('>');
+
+ m_outputBuffer.Append(block);
+
+ return WriteLineToMailbox(m_outputBuffer);
+}
+
+nsresult nsPop3Sink::WriteLineToMailbox(const nsACString& buffer)
+{
+ if (!buffer.IsEmpty())
+ {
+ uint32_t bufferLen = buffer.Length();
+ if (m_newMailParser)
+ m_newMailParser->HandleLine(buffer.BeginReading(), bufferLen);
+ // The following (!m_outFileStream etc) was added to make sure that we don't write somewhere
+ // where for some reason or another we can't write to and lose the messages
+ // See bug 62480
+ NS_ENSURE_TRUE(m_outFileStream, NS_ERROR_OUT_OF_MEMORY);
+
+ // To remove seeking to the end for each line to be written, remove the
+ // following line. See bug 1116055 for details.
+#define SEEK_TO_END
+#ifdef SEEK_TO_END
+ // seek to the end in case someone else has seeked elsewhere in our stream.
+ nsCOMPtr <nsISeekableStream> seekableOutStream = do_QueryInterface(m_outFileStream);
+
+ int64_t before_seek_pos;
+ nsresult rv2 = seekableOutStream->Tell(&before_seek_pos);
+ MOZ_ASSERT(NS_SUCCEEDED(rv2), "seekableOutStream->Tell(&before_seek_pos) failed");
+
+ // XXX Handle error such as network error for remote file system.
+ seekableOutStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
+
+ int64_t after_seek_pos;
+ nsresult rv3 = seekableOutStream->Tell(&after_seek_pos);
+ MOZ_ASSERT(NS_SUCCEEDED(rv3), "seekableOutStream->Tell(&after_seek_pos) failed");
+
+ if (NS_SUCCEEDED(rv2) && NS_SUCCEEDED(rv3)) {
+ if (before_seek_pos != after_seek_pos) {
+ nsCOMPtr<nsIMsgFolder> localFolder = do_QueryInterface(m_folder);
+ nsString folderName;
+ if (localFolder)
+ localFolder->GetPrettiestName(folderName);
+ // This merits a console message, it's poor man's telemetry.
+ MsgLogToConsole4(
+ NS_LITERAL_STRING("Unexpected file position change detected") +
+ (folderName.IsEmpty() ? EmptyString() : NS_LITERAL_STRING(" in folder ")) +
+ (folderName.IsEmpty() ? EmptyString() : folderName) + NS_LITERAL_STRING(". "
+ "If you can reliably reproduce this, please report the steps "
+ "you used to dev-apps-thunderbird@lists.mozilla.org or to bug 1308335 at bugzilla.mozilla.org. "
+ "Resolving this problem will allow speeding up message downloads."),
+ NS_LITERAL_STRING(__FILE__), __LINE__, nsIScriptError::errorFlag);
+#ifdef DEBUG
+ // Debugging, see bug 1116055.
+ if (!folderName.IsEmpty()) {
+ fprintf(stderr,"(seekdebug) WriteLineToMailbox() detected an unexpected file position change in folder %s.\n",
+ NS_ConvertUTF16toUTF8(folderName).get());
+ } else {
+ fprintf(stderr,"(seekdebug) WriteLineToMailbox() detected an unexpected file position change.\n");
+ }
+ fprintf(stderr,"(seekdebug) before_seek_pos=0x%016llx, after_seek_pos=0x%016llx\n",
+ (long long unsigned int) before_seek_pos, (long long unsigned int) after_seek_pos);
+#endif
+ }
+ }
+#endif
+
+ uint32_t bytesWritten;
+ m_outFileStream->Write(buffer.BeginReading(), bufferLen, &bytesWritten);
+ NS_ENSURE_TRUE(bytesWritten == bufferLen, NS_ERROR_FAILURE);
+ }
+ return NS_OK;
+}
+
+nsresult nsPop3Sink::HandleTempDownloadFailed(nsIMsgWindow *msgWindow)
+{
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/localMsgs.properties", getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsString fromStr, subjectStr, confirmString;
+
+ m_newMailParser->m_newMsgHdr->GetMime2DecodedSubject(subjectStr);
+ m_newMailParser->m_newMsgHdr->GetMime2DecodedAuthor(fromStr);
+ const char16_t *params[] = { fromStr.get(), subjectStr.get() };
+ bundle->FormatStringFromName(
+ u"pop3TmpDownloadError",
+ params, 2, getter_Copies(confirmString));
+ nsCOMPtr<mozIDOMWindowProxy> parentWindow;
+ nsCOMPtr<nsIPromptService> promptService = do_GetService(NS_PROMPTSERVICE_CONTRACTID);
+ nsCOMPtr<nsIDocShell> docShell;
+ if (msgWindow)
+ {
+ (void) msgWindow->GetRootDocShell(getter_AddRefs(docShell));
+ parentWindow = do_QueryInterface(docShell);
+ }
+ if (promptService && !confirmString.IsEmpty())
+ {
+ int32_t dlgResult = -1;
+ bool dummyValue = false;
+ rv = promptService->ConfirmEx(parentWindow, nullptr, confirmString.get(),
+ nsIPromptService::STD_YES_NO_BUTTONS,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &dummyValue,
+ &dlgResult);
+ m_newMailParser->m_newMsgHdr = nullptr;
+
+ return (dlgResult == 0) ? NS_OK : NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD;
+ }
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsPop3Sink::IncorporateComplete(nsIMsgWindow *aMsgWindow, int32_t aSize)
+{
+ if (m_buildMessageUri && !m_baseMessageUri.IsEmpty() && m_newMailParser &&
+ m_newMailParser->m_newMsgHdr)
+ {
+ nsMsgKey msgKey;
+ m_newMailParser->m_newMsgHdr->GetMessageKey(&msgKey);
+ m_messageUri.Truncate();
+ nsBuildLocalMessageURI(m_baseMessageUri.get(), msgKey, m_messageUri);
+ }
+
+ nsresult rv = WriteLineToMailbox(NS_LITERAL_CSTRING(MSG_LINEBREAK));
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool leaveOnServer = false;
+ m_popServer->GetLeaveMessagesOnServer(&leaveOnServer);
+ // We need to flush the output stream, in case mail filters move
+ // the new message, which relies on all the data being flushed.
+ rv = m_outFileStream->Flush(); // Make sure the message is written to the disk
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ASSERTION(m_newMailParser, "could not get m_newMailParser");
+ if (m_newMailParser)
+ {
+ // PublishMsgHdr clears m_newMsgHdr, so we need a comptr to
+ // hold onto it.
+ nsCOMPtr<nsIMsgDBHdr> hdr = m_newMailParser->m_newMsgHdr;
+ NS_ASSERTION(hdr, "m_newMailParser->m_newMsgHdr wasn't set");
+ if (!hdr)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(m_folder);
+ bool doSelect = false;
+
+ // aSize is only set for partial messages. For full messages,
+ // check to see if we're replacing an old partial message.
+ if (!aSize && localFolder)
+ (void) localFolder->DeleteDownloadMsg(hdr, &doSelect);
+
+ // If a header already exists for this message (for example, when
+ // getting a complete message when a partial exists), then update the new
+ // header from the old.
+ if (!m_origMessageUri.IsEmpty() && localFolder)
+ {
+ nsCOMPtr <nsIMsgDBHdr> oldMsgHdr;
+ rv = GetMsgDBHdrFromURI(m_origMessageUri.get(), getter_AddRefs(oldMsgHdr));
+ if (NS_SUCCEEDED(rv) && oldMsgHdr)
+ localFolder->UpdateNewMsgHdr(oldMsgHdr, hdr);
+ }
+
+ if (m_downloadingToTempFile)
+ {
+ // close file to give virus checkers a chance to do their thing...
+ m_outFileStream->Flush();
+ m_outFileStream->Close();
+ m_newMailParser->FinishHeader();
+ // need to re-open the inbox file stream.
+ bool exists;
+ m_tmpDownloadFile->Exists(&exists);
+ if (!exists)
+ return HandleTempDownloadFailed(aMsgWindow);
+
+ nsCOMPtr <nsIInputStream> inboxInputStream = do_QueryInterface(m_outFileStream);
+ rv = MsgReopenFileStream(m_tmpDownloadFile, inboxInputStream);
+ NS_ENSURE_SUCCESS(rv, HandleTempDownloadFailed(aMsgWindow));
+ if (m_outFileStream)
+ {
+ int64_t tmpDownloadFileSize;
+ uint32_t msgSize;
+ hdr->GetMessageSize(&msgSize);
+ // we need to clone because nsLocalFileUnix caches its stat result,
+ // so it doesn't realize the file has changed size.
+ nsCOMPtr <nsIFile> tmpClone;
+ rv = m_tmpDownloadFile->Clone(getter_AddRefs(tmpClone));
+ NS_ENSURE_SUCCESS(rv, rv);
+ tmpClone->GetFileSize(&tmpDownloadFileSize);
+
+ if (msgSize > tmpDownloadFileSize)
+ rv = NS_MSG_ERROR_WRITING_MAIL_FOLDER;
+ else
+ rv = m_newMailParser->AppendMsgFromStream(inboxInputStream, hdr,
+ msgSize, m_folder);
+ if (NS_FAILED(rv))
+ return HandleTempDownloadFailed(aMsgWindow);
+
+ m_outFileStream->Close(); // close so we can truncate.
+ m_tmpDownloadFile->SetFileSize(0);
+ }
+ else
+ {
+ return HandleTempDownloadFailed(aMsgWindow);
+ // need to give an error here.
+ }
+ }
+ else
+ {
+ m_msgStore->FinishNewMessage(m_outFileStream, hdr);
+ }
+ m_newMailParser->PublishMsgHeader(aMsgWindow);
+ // run any reply/forward filter after we've finished with the
+ // temp quarantine file, and/or moved the message to another folder.
+ m_newMailParser->ApplyForwardAndReplyFilter(aMsgWindow);
+ if (aSize)
+ hdr->SetUint32Property("onlineSize", aSize);
+
+ // if DeleteDownloadMsg requested it, select the new message
+ else if (doSelect)
+ (void) localFolder->SelectDownloadMsg();
+ }
+
+#ifdef DEBUG
+ printf("Incorporate message complete.\n");
+#endif
+ nsCOMPtr<nsIPop3Service> pop3Service(do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ pop3Service->NotifyDownloadProgress(m_folder, ++m_numMsgsDownloaded, m_numNewMessages);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::IncorporateAbort(bool uidlDownload)
+{
+ nsresult rv = m_outFileStream->Close();
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (!m_downloadingToTempFile && m_msgStore && m_newMailParser &&
+ m_newMailParser->m_newMsgHdr)
+ {
+ m_msgStore->DiscardNewMessage(m_outFileStream,
+ m_newMailParser->m_newMsgHdr);
+ }
+#ifdef DEBUG
+ printf("Incorporate message abort.\n");
+#endif
+ return rv;
+}
+
+nsresult
+nsPop3Sink::BiffGetNewMail()
+{
+#ifdef DEBUG
+ printf("Biff get new mail.\n");
+#endif
+ return NS_OK;
+}
+
+nsresult
+nsPop3Sink::SetBiffStateAndUpdateFE(uint32_t aBiffState, int32_t numNewMessages, bool notify)
+{
+ m_biffState = aBiffState;
+ if (m_newMailParser)
+ numNewMessages -= m_newMailParser->m_numNotNewMessages;
+
+ if (notify && m_folder && numNewMessages > 0 && numNewMessages != m_numNewMessages
+ && aBiffState == nsIMsgFolder::nsMsgBiffState_NewMail)
+ {
+ m_folder->SetNumNewMessages(numNewMessages);
+ m_folder->SetBiffState(aBiffState);
+ }
+ m_numNewMessages = numNewMessages;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::GetBuildMessageUri(bool *bVal)
+{
+ NS_ENSURE_ARG_POINTER(bVal);
+ *bVal = m_buildMessageUri;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::SetBuildMessageUri(bool bVal)
+{
+ m_buildMessageUri = bVal;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::GetMessageUri(char **messageUri)
+{
+ NS_ENSURE_ARG_POINTER(messageUri);
+ NS_ENSURE_TRUE(!m_messageUri.IsEmpty(), NS_ERROR_FAILURE);
+ *messageUri = ToNewCString(m_messageUri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::SetMessageUri(const char *messageUri)
+{
+ NS_ENSURE_ARG_POINTER(messageUri);
+ m_messageUri = messageUri;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::GetBaseMessageUri(char ** baseMessageUri)
+{
+ NS_ENSURE_ARG_POINTER(baseMessageUri);
+ NS_ENSURE_TRUE(!m_baseMessageUri.IsEmpty(), NS_ERROR_FAILURE);
+ *baseMessageUri = ToNewCString(m_baseMessageUri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::SetBaseMessageUri(const char *baseMessageUri)
+{
+ NS_ENSURE_ARG_POINTER(baseMessageUri);
+ m_baseMessageUri = baseMessageUri;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::GetOrigMessageUri(nsACString& aOrigMessageUri)
+{
+ aOrigMessageUri.Assign(m_origMessageUri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Sink::SetOrigMessageUri(const nsACString& aOrigMessageUri)
+{
+ m_origMessageUri.Assign(aOrigMessageUri);
+ return NS_OK;
+}