/* -*- 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 #include #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 messages; bool hasMore = false; bool isOpen = false; nsLocalFolderScanState folderScanState; nsCOMPtr db; nsCOMPtr 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 aSupport; uint32_t flags = 0; rv = messages->GetNext(getter_AddRefs(aSupport)); nsCOMPtr 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 localFolder = do_QueryInterface(m_folder); if (localFolder) localFolder->NotifyDelete(); } } nsresult nsPop3Sink::BeginMailDelivery(bool uidlDownload, nsIMsgWindow *aMsgWindow, bool* aBool) { nsresult rv; nsCOMPtr server = do_QueryInterface(m_popServer); if (!server) return NS_ERROR_UNEXPECTED; m_window = aMsgWindow; nsCOMPtr acctMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); nsCOMPtr account; NS_ENSURE_SUCCESS(rv, rv); acctMgr->FindAccountForServer(server, getter_AddRefs(account)); if (account) account->GetKey(m_accountKey); bool isLocked; nsCOMPtr supports = do_QueryInterface(static_cast(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 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 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 localFolder = do_QueryInterface(m_folder); if (localFolder) (void) localFolder->RefreshSizeOnDisk(); nsCOMPtr server = do_QueryInterface(m_popServer); if (server) { nsCOMPtr 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 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 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 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 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 supports = do_QueryInterface(static_cast(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 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 path; m_folder->GetFilePath(getter_AddRefs(path)); nsresult rv; nsCOMPtr pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); if (pPrefBranch) { nsCOMPtr 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 newHdr; nsCOMPtr server = do_QueryInterface(m_popServer); if (!server) return NS_ERROR_UNEXPECTED; if (m_downloadingToTempFile) { // need to create an nsIOFileStream from a temp file... nsCOMPtr 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 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 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 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 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 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 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 bundleService = mozilla::services::GetStringBundleService(); NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED); nsCOMPtr 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 parentWindow; nsCOMPtr promptService = do_GetService(NS_PROMPTSERVICE_CONTRACTID); nsCOMPtr 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 hdr = m_newMailParser->m_newMsgHdr; NS_ASSERTION(hdr, "m_newMailParser->m_newMsgHdr wasn't set"); if (!hdr) return NS_ERROR_FAILURE; nsCOMPtr 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 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 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 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 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; }