diff options
Diffstat (limited to 'mailnews/local/src/nsPop3Sink.cpp')
-rw-r--r-- | mailnews/local/src/nsPop3Sink.cpp | 1026 |
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; +} |