summaryrefslogtreecommitdiffstats
path: root/mailnews/local/src
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
committerMatt A. Tobin <email@mattatobin.com>2019-11-03 00:17:46 -0400
commit302bf1b523012e11b60425d6eee1221ebc2724eb (patch)
treeb191a895f8716efcbe42f454f37597a545a6f421 /mailnews/local/src
parent21b3f6247403c06f85e1f45d219f87549862198f (diff)
downloadUXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.gz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.lz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.tar.xz
UXP-302bf1b523012e11b60425d6eee1221ebc2724eb.zip
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
Diffstat (limited to 'mailnews/local/src')
-rw-r--r--mailnews/local/src/moz.build36
-rw-r--r--mailnews/local/src/nsLocalMailFolder.cpp3852
-rw-r--r--mailnews/local/src/nsLocalMailFolder.h276
-rw-r--r--mailnews/local/src/nsLocalUndoTxn.cpp560
-rw-r--r--mailnews/local/src/nsLocalUndoTxn.h86
-rw-r--r--mailnews/local/src/nsLocalUtils.cpp244
-rw-r--r--mailnews/local/src/nsLocalUtils.h30
-rw-r--r--mailnews/local/src/nsMailboxProtocol.cpp725
-rw-r--r--mailnews/local/src/nsMailboxProtocol.h125
-rw-r--r--mailnews/local/src/nsMailboxServer.cpp33
-rw-r--r--mailnews/local/src/nsMailboxServer.h22
-rw-r--r--mailnews/local/src/nsMailboxService.cpp677
-rw-r--r--mailnews/local/src/nsMailboxService.h57
-rw-r--r--mailnews/local/src/nsMailboxUrl.cpp556
-rw-r--r--mailnews/local/src/nsMailboxUrl.h110
-rw-r--r--mailnews/local/src/nsMovemailIncomingServer.cpp178
-rw-r--r--mailnews/local/src/nsMovemailIncomingServer.h40
-rw-r--r--mailnews/local/src/nsMovemailService.cpp694
-rw-r--r--mailnews/local/src/nsMovemailService.h32
-rw-r--r--mailnews/local/src/nsMsgBrkMBoxStore.cpp1124
-rw-r--r--mailnews/local/src/nsMsgBrkMBoxStore.h50
-rw-r--r--mailnews/local/src/nsMsgLocalStoreUtils.cpp345
-rw-r--r--mailnews/local/src/nsMsgLocalStoreUtils.h49
-rw-r--r--mailnews/local/src/nsMsgMaildirStore.cpp1453
-rw-r--r--mailnews/local/src/nsMsgMaildirStore.h38
-rw-r--r--mailnews/local/src/nsNoIncomingServer.cpp206
-rw-r--r--mailnews/local/src/nsNoIncomingServer.h41
-rw-r--r--mailnews/local/src/nsNoneService.cpp168
-rw-r--r--mailnews/local/src/nsNoneService.h28
-rw-r--r--mailnews/local/src/nsParseMailbox.cpp2624
-rw-r--r--mailnews/local/src/nsParseMailbox.h271
-rw-r--r--mailnews/local/src/nsPop3IncomingServer.cpp744
-rw-r--r--mailnews/local/src/nsPop3IncomingServer.h57
-rw-r--r--mailnews/local/src/nsPop3Protocol.cpp4176
-rw-r--r--mailnews/local/src/nsPop3Protocol.h402
-rw-r--r--mailnews/local/src/nsPop3Service.cpp711
-rw-r--r--mailnews/local/src/nsPop3Service.h52
-rw-r--r--mailnews/local/src/nsPop3Sink.cpp1026
-rw-r--r--mailnews/local/src/nsPop3Sink.h78
-rw-r--r--mailnews/local/src/nsPop3URL.cpp63
-rw-r--r--mailnews/local/src/nsPop3URL.h30
-rw-r--r--mailnews/local/src/nsRssIncomingServer.cpp260
-rw-r--r--mailnews/local/src/nsRssIncomingServer.h43
-rw-r--r--mailnews/local/src/nsRssService.cpp130
-rw-r--r--mailnews/local/src/nsRssService.h25
45 files changed, 22527 insertions, 0 deletions
diff --git a/mailnews/local/src/moz.build b/mailnews/local/src/moz.build
new file mode 100644
index 000000000..5f999ce46
--- /dev/null
+++ b/mailnews/local/src/moz.build
@@ -0,0 +1,36 @@
+# vim: set filetype=python:
+# 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/.
+
+SOURCES += [
+ 'nsLocalMailFolder.cpp',
+ 'nsLocalUndoTxn.cpp',
+ 'nsLocalUtils.cpp',
+ 'nsMailboxProtocol.cpp',
+ 'nsMailboxServer.cpp',
+ 'nsMailboxService.cpp',
+ 'nsMailboxUrl.cpp',
+ 'nsMsgBrkMBoxStore.cpp',
+ 'nsMsgLocalStoreUtils.cpp',
+ 'nsMsgMaildirStore.cpp',
+ 'nsNoIncomingServer.cpp',
+ 'nsNoneService.cpp',
+ 'nsParseMailbox.cpp',
+ 'nsPop3IncomingServer.cpp',
+ 'nsPop3Protocol.cpp',
+ 'nsPop3Service.cpp',
+ 'nsPop3Sink.cpp',
+ 'nsPop3URL.cpp',
+ 'nsRssIncomingServer.cpp',
+ 'nsRssService.cpp',
+]
+
+if CONFIG['MOZ_MOVEMAIL']:
+ SOURCES += [
+ 'nsMovemailIncomingServer.cpp',
+ 'nsMovemailService.cpp',
+ ]
+
+FINAL_LIBRARY = 'mail'
+
diff --git a/mailnews/local/src/nsLocalMailFolder.cpp b/mailnews/local/src/nsLocalMailFolder.cpp
new file mode 100644
index 000000000..14135fe46
--- /dev/null
+++ b/mailnews/local/src/nsLocalMailFolder.cpp
@@ -0,0 +1,3852 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "prlog.h"
+
+#include "msgCore.h" // precompiled header...
+#include "nsArrayEnumerator.h"
+#include "nsLocalMailFolder.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsMsgFolderFlags.h"
+#include "nsMsgMessageFlags.h"
+#include "prprf.h"
+#include "prmem.h"
+#include "nsIArray.h"
+#include "nsIServiceManager.h"
+#include "nsIMailboxService.h"
+#include "nsParseMailbox.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgWindow.h"
+#include "nsCOMPtr.h"
+#include "nsIRDFService.h"
+#include "nsMsgDBCID.h"
+#include "nsMsgUtils.h"
+#include "nsLocalUtils.h"
+#include "nsIPop3IncomingServer.h"
+#include "nsILocalMailIncomingServer.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgLocalCID.h"
+#include "nsStringGlue.h"
+#include "nsIMsgFolderCacheElement.h"
+#include "nsUnicharUtils.h"
+#include "nsMsgUtils.h"
+#include "nsICopyMsgStreamListener.h"
+#include "nsIMsgCopyService.h"
+#include "nsMsgTxn.h"
+#include "nsIMessenger.h"
+#include "nsMsgBaseCID.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsIDocShell.h"
+#include "nsIPrompt.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIPop3URL.h"
+#include "nsIMsgMailSession.h"
+#include "nsIMsgFolderCompactor.h"
+#include "nsNetCID.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsISpamSettings.h"
+#include "nsINoIncomingServer.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsMailHeaders.h"
+#include "nsCOMArray.h"
+#include "nsILineInputStream.h"
+#include "nsIFileStreams.h"
+#include "nsAutoPtr.h"
+#include "nsIRssIncomingServer.h"
+#include "nsNetUtil.h"
+#include "nsIMsgFolderNotificationService.h"
+#include "nsReadLine.h"
+#include "nsArrayUtils.h"
+#include "nsIMsgTraitService.h"
+#include "nsIStringEnumerator.h"
+#include "mozilla/Services.h"
+
+//////////////////////////////////////////////////////////////////////////////
+// nsLocal
+/////////////////////////////////////////////////////////////////////////////
+
+nsLocalMailCopyState::nsLocalMailCopyState() :
+ m_flags(0),
+ m_lastProgressTime(PR_IntervalToMilliseconds(PR_IntervalNow())),
+ m_curDstKey(nsMsgKey_None),
+ m_curCopyIndex(0),
+ m_totalMsgCount(0),
+ m_dataBufferSize(0),
+ m_leftOver(0),
+ m_isMove(false),
+ m_dummyEnvelopeNeeded(false),
+ m_fromLineSeen(false),
+ m_writeFailed(false),
+ m_notifyFolderLoaded(false)
+{
+}
+
+nsLocalMailCopyState::~nsLocalMailCopyState()
+{
+ PR_Free(m_dataBuffer);
+ if (m_fileStream)
+ m_fileStream->Close();
+ if (m_messageService)
+ {
+ nsCOMPtr <nsIMsgFolder> srcFolder = do_QueryInterface(m_srcSupport);
+ if (srcFolder && m_message)
+ {
+ nsCString uri;
+ srcFolder->GetUriForMsg(m_message, uri);
+ }
+ }
+}
+
+nsLocalFolderScanState::nsLocalFolderScanState() : m_uidl(nullptr)
+{
+}
+
+nsLocalFolderScanState::~nsLocalFolderScanState()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsMsgLocalMailFolder interface
+///////////////////////////////////////////////////////////////////////////////
+
+nsMsgLocalMailFolder::nsMsgLocalMailFolder(void)
+ : mCopyState(nullptr), mHaveReadNameFromDB(false),
+ mInitialized(false),
+ mCheckForNewMessagesAfterParsing(false), m_parsingFolder(false),
+ mDownloadState(DOWNLOAD_STATE_NONE)
+{
+}
+
+nsMsgLocalMailFolder::~nsMsgLocalMailFolder(void)
+{
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(nsMsgLocalMailFolder,
+ nsMsgDBFolder,
+ nsICopyMessageListener,
+ nsIMsgLocalMailFolder)
+
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::Init(const char* aURI)
+{
+ return nsMsgDBFolder::Init(aURI);
+}
+
+nsresult nsMsgLocalMailFolder::CreateChildFromURI(const nsCString &uri, nsIMsgFolder **folder)
+{
+ nsMsgLocalMailFolder *newFolder = new nsMsgLocalMailFolder;
+ if (!newFolder)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*folder = newFolder);
+ newFolder->Init(uri.get());
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::CreateLocalSubfolder(const nsAString &aFolderName,
+ nsIMsgFolder **aChild)
+{
+ NS_ENSURE_ARG_POINTER(aChild);
+ nsresult rv = CreateSubfolderInternal(aFolderName, nullptr, aChild);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolderNotificationService> notifier(
+ do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ if (notifier)
+ notifier->NotifyFolderAdded(*aChild);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::GetManyHeadersToDownload(bool *retval)
+{
+ bool isLocked;
+ // if the folder is locked, we're probably reparsing - let's build the
+ // view when we've finished reparsing.
+ GetLocked(&isLocked);
+ if (isLocked)
+ {
+ *retval = true;
+ return NS_OK;
+ }
+
+ return nsMsgDBFolder::GetManyHeadersToDownload(retval);
+}
+
+//run the url to parse the mailbox
+NS_IMETHODIMP nsMsgLocalMailFolder::ParseFolder(nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aListener)
+{
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ nsresult rv = GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (aListener != this)
+ mReparseListener = aListener;
+ // if parsing is synchronous, we need to set m_parsingFolder to
+ // true before starting. And we need to open the db before
+ // setting m_parsingFolder to true.
+// OpenDatabase();
+ rv = msgStore->RebuildIndex(this, mDatabase, aMsgWindow, this);
+ if (NS_SUCCEEDED(rv))
+ m_parsingFolder = true;
+
+ return rv;
+}
+
+// this won't force a reparse of the folder if the db is invalid.
+NS_IMETHODIMP
+nsMsgLocalMailFolder::GetMsgDatabase(nsIMsgDatabase** aMsgDatabase)
+{
+ return GetDatabaseWOReparse(aMsgDatabase);
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::GetSubFolders(nsISimpleEnumerator **aResult)
+{
+ if (!mInitialized)
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ nsresult rv = GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ // need to set this flag here to avoid infinite recursion
+ mInitialized = true;
+ rv = server->GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ // This should add all existing folders as sub-folders of this folder.
+ rv = msgStore->DiscoverSubFolders(this, true);
+
+ nsCOMPtr<nsIFile> path;
+ rv = GetFilePath(getter_AddRefs(path));
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool directory;
+ path->IsDirectory(&directory);
+ if (directory)
+ {
+ SetFlag(nsMsgFolderFlags::Mail | nsMsgFolderFlags::Elided |
+ nsMsgFolderFlags::Directory);
+
+ bool isServer;
+ GetIsServer(&isServer);
+ if (isServer)
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
+
+ nsCOMPtr<nsILocalMailIncomingServer> localMailServer;
+ localMailServer = do_QueryInterface(server, &rv);
+ NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
+
+ // first create the folders on disk (as empty files)
+ rv = localMailServer->CreateDefaultMailboxes();
+ if (NS_FAILED(rv) && rv != NS_MSG_FOLDER_EXISTS)
+ return rv;
+
+ // must happen after CreateSubFolders, or the folders won't exist.
+ rv = localMailServer->SetFlagsOnDefaultMailboxes();
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+ UpdateSummaryTotals(false);
+ }
+
+ return aResult ? NS_NewArrayEnumerator(aResult, mSubFolders) : NS_ERROR_NULL_POINTER;
+}
+
+nsresult nsMsgLocalMailFolder::GetDatabase()
+{
+ nsCOMPtr <nsIMsgDatabase> msgDB;
+ return GetDatabaseWOReparse(getter_AddRefs(msgDB));
+}
+
+//we treat failure as null db returned
+NS_IMETHODIMP nsMsgLocalMailFolder::GetDatabaseWOReparse(nsIMsgDatabase **aDatabase)
+{
+ NS_ENSURE_ARG(aDatabase);
+ if (m_parsingFolder)
+ return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
+
+ nsresult rv = NS_OK;
+ if (!mDatabase)
+ {
+ rv = OpenDatabase();
+ if (mDatabase)
+ {
+ mDatabase->AddListener(this);
+ UpdateNewMessages();
+ }
+ }
+ NS_IF_ADDREF(*aDatabase = mDatabase);
+ if (mDatabase)
+ mDatabase->SetLastUseTime(PR_Now());
+ return rv;
+}
+
+
+// Makes sure the database is open and exists. If the database is out of date,
+// then this call will run an async url to reparse the folder. The passed in
+// url listener will get called when the url is done.
+NS_IMETHODIMP nsMsgLocalMailFolder::GetDatabaseWithReparse(nsIUrlListener *aReparseUrlListener, nsIMsgWindow *aMsgWindow,
+ nsIMsgDatabase **aMsgDatabase)
+{
+ nsresult rv = NS_OK;
+ // if we're already reparsing, just remember the listener so we can notify it
+ // when we've finished.
+ if (m_parsingFolder)
+ {
+ NS_ASSERTION(!mReparseListener, "can't have an existing listener");
+ mReparseListener = aReparseUrlListener;
+ return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
+ }
+
+ if (!mDatabase)
+ {
+ nsCOMPtr <nsIFile> pathFile;
+ rv = GetFilePath(getter_AddRefs(pathFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool exists;
+ rv = pathFile->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (!exists)
+ return NS_ERROR_NULL_POINTER; //mDatabase will be null at this point.
+
+ nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsresult folderOpen = msgDBService->OpenFolderDB(this, true,
+ getter_AddRefs(mDatabase));
+ if (folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
+ {
+ nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
+ nsCOMPtr <nsIDBFolderInfo> transferInfo;
+ if (mDatabase)
+ {
+ mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
+ if (dbFolderInfo)
+ {
+ dbFolderInfo->SetNumMessages(0);
+ dbFolderInfo->SetNumUnreadMessages(0);
+ dbFolderInfo->GetTransferInfo(getter_AddRefs(transferInfo));
+ }
+ dbFolderInfo = nullptr;
+
+ // A backup message database might have been created earlier, for example
+ // if the user requested a reindex. We'll use the earlier one if we can,
+ // otherwise we'll try to backup at this point.
+ if (NS_FAILED(OpenBackupMsgDatabase()))
+ {
+ CloseAndBackupFolderDB(EmptyCString());
+ if (NS_FAILED(OpenBackupMsgDatabase()) && mBackupDatabase)
+ {
+ mBackupDatabase->RemoveListener(this);
+ mBackupDatabase = nullptr;
+ }
+ }
+ else
+ mDatabase->ForceClosed();
+
+ mDatabase = nullptr;
+ }
+ nsCOMPtr <nsIFile> summaryFile;
+ rv = GetSummaryFileLocation(pathFile, getter_AddRefs(summaryFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Remove summary file.
+ summaryFile->Remove(false);
+
+ // if it's out of date then reopen with upgrade.
+ rv = msgDBService->CreateNewDB(this, getter_AddRefs(mDatabase));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (transferInfo && mDatabase)
+ {
+ SetDBTransferInfo(transferInfo);
+ mDatabase->SetSummaryValid(false);
+ }
+ }
+ else if (folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
+ {
+ rv = msgDBService->CreateNewDB(this, getter_AddRefs(mDatabase));
+ }
+
+ if (mDatabase)
+ {
+ if (mAddListener)
+ mDatabase->AddListener(this);
+
+ // if we have to regenerate the folder, run the parser url.
+ if (folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING ||
+ folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE)
+ {
+ if (NS_FAILED(rv = ParseFolder(aMsgWindow, aReparseUrlListener)))
+ {
+ if (rv == NS_MSG_FOLDER_BUSY)
+ {
+ mDatabase->RemoveListener(this); //we need to null out the db so that parsing gets kicked off again.
+ mDatabase = nullptr;
+ ThrowAlertMsg("parsingFolderFailed", aMsgWindow);
+ }
+ return rv;
+ }
+
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ // We have a valid database so lets extract necessary info.
+ UpdateSummaryTotals(true);
+ }
+ }
+ NS_IF_ADDREF(*aMsgDatabase = mDatabase);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::UpdateFolder(nsIMsgWindow *aWindow)
+{
+ (void) RefreshSizeOnDisk();
+ nsresult rv;
+
+ if (!PromptForMasterPasswordIfNecessary())
+ return NS_ERROR_FAILURE;
+
+ //If we don't currently have a database, get it. Otherwise, the folder has been updated (presumably this
+ //changes when we download headers when opening inbox). If it's updated, send NotifyFolderLoaded.
+ if (!mDatabase)
+ {
+ // return of NS_ERROR_NOT_INITIALIZED means running parsing URL
+ // We don't need the return value, and assigning it to mDatabase which
+ // is already set internally leaks.
+ nsCOMPtr<nsIMsgDatabase> returnedDb;
+ rv = GetDatabaseWithReparse(this, aWindow, getter_AddRefs(returnedDb));
+ if (NS_SUCCEEDED(rv))
+ NotifyFolderEvent(mFolderLoadedAtom);
+ }
+ else
+ {
+ bool valid;
+ rv = mDatabase->GetSummaryValid(&valid);
+ // don't notify folder loaded or try compaction if db isn't valid
+ // (we're probably reparsing or copying msgs to it)
+ if (NS_SUCCEEDED(rv) && valid)
+ NotifyFolderEvent(mFolderLoadedAtom);
+ else if (mCopyState)
+ mCopyState->m_notifyFolderLoaded = true; //defer folder loaded notification
+ else if (!m_parsingFolder)// if the db was already open, it's probably OK to load it if not parsing
+ NotifyFolderEvent(mFolderLoadedAtom);
+ }
+ bool filtersRun;
+ bool hasNewMessages;
+ GetHasNewMessages(&hasNewMessages);
+ if (mDatabase)
+ ApplyRetentionSettings();
+ // if we have new messages, try the filter plugins.
+ if (NS_SUCCEEDED(rv) && hasNewMessages)
+ (void) CallFilterPlugins(aWindow, &filtersRun);
+ // Callers should rely on folder loaded event to ensure completion of loading. So we'll
+ // return NS_OK even if parsing is still in progress
+ if (rv == NS_ERROR_NOT_INITIALIZED)
+ rv = NS_OK;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::GetMessages(nsISimpleEnumerator **result)
+{
+ nsCOMPtr <nsIMsgDatabase> msgDB;
+ nsresult rv = GetDatabaseWOReparse(getter_AddRefs(msgDB));
+ return NS_SUCCEEDED(rv) ? msgDB->EnumerateMessages(result) : rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::GetFolderURL(nsACString& aUrl)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> path;
+ rv = GetFilePath(getter_AddRefs(path));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = NS_GetURLSpecFromFile(path, aUrl);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ aUrl.Replace(0, strlen("file:"), "mailbox:");
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::CreateStorageIfMissing(nsIUrlListener* aUrlListener)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgFolder> msgParent;
+ GetParent(getter_AddRefs(msgParent));
+
+ // parent is probably not set because *this* was probably created by rdf
+ // and not by folder discovery. So, we have to compute the parent.
+ if (!msgParent)
+ {
+ nsAutoCString folderName(mURI);
+ nsAutoCString uri;
+ int32_t leafPos = folderName.RFindChar('/');
+ nsAutoCString parentName(folderName);
+ if (leafPos > 0)
+ {
+ // If there is a hierarchy, there is a parent.
+ // Don't strip off slash if it's the first character
+ parentName.SetLength(leafPos);
+ // get the corresponding RDF resource
+ // RDF will create the folder resource if it doesn't already exist
+ nsCOMPtr<nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIRDFResource> resource;
+ rv = rdf->GetResource(parentName, getter_AddRefs(resource));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ msgParent = do_QueryInterface(resource, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+
+ if (msgParent)
+ {
+ nsString folderName;
+ GetName(folderName);
+ rv = msgParent->CreateSubfolder(folderName, nullptr);
+ // by definition, this is OK.
+ if (rv == NS_MSG_FOLDER_EXISTS)
+ return NS_OK;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::CreateSubfolder(const nsAString& folderName, nsIMsgWindow *msgWindow)
+{
+ nsCOMPtr<nsIMsgFolder> newFolder;
+ nsresult rv = CreateSubfolderInternal(folderName, msgWindow, getter_AddRefs(newFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ if (notifier)
+ notifier->NotifyFolderAdded(newFolder);
+
+ return NS_OK;
+}
+
+nsresult
+nsMsgLocalMailFolder::CreateSubfolderInternal(const nsAString& folderName,
+ nsIMsgWindow *msgWindow,
+ nsIMsgFolder **aNewFolder)
+{
+ nsresult rv = CheckIfFolderExists(folderName, this, msgWindow);
+ // No need for an assertion: we already throw an alert.
+ if (NS_FAILED(rv))
+ return rv;
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = msgStore->CreateFolder(this, folderName, aNewFolder);
+ if (rv == NS_MSG_ERROR_INVALID_FOLDER_NAME)
+ {
+ ThrowAlertMsg("folderCreationFailed", msgWindow);
+ }
+ else if (rv == NS_MSG_FOLDER_EXISTS)
+ {
+ ThrowAlertMsg("folderExists", msgWindow);
+ }
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgFolder> child = *aNewFolder;
+ //we need to notify explicitly the flag change because it failed when we did AddSubfolder
+ child->OnFlagChange(mFlags);
+ child->SetPrettyName(folderName); //because empty trash will create a new trash folder
+ NotifyItemAdded(child);
+ if (aNewFolder)
+ child.swap(*aNewFolder);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::CompactAll(nsIUrlListener *aListener,
+ nsIMsgWindow *aMsgWindow,
+ bool aCompactOfflineAlso)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMutableArray> folderArray;
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ nsCOMPtr<nsIArray> allDescendents;
+ rv = GetRootFolder(getter_AddRefs(rootFolder));
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ GetMsgStore(getter_AddRefs(msgStore));
+ bool storeSupportsCompaction;
+ msgStore->GetSupportsCompaction(&storeSupportsCompaction);
+ if (!storeSupportsCompaction)
+ return NotifyCompactCompleted();
+
+ if (NS_SUCCEEDED(rv) && rootFolder)
+ {
+ rv = rootFolder->GetDescendants(getter_AddRefs(allDescendents));
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint32_t cnt = 0;
+ rv = allDescendents->GetLength(&cnt);
+ NS_ENSURE_SUCCESS(rv, rv);
+ folderArray = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ int64_t expungedBytes = 0;
+ for (uint32_t i = 0; i < cnt; i++)
+ {
+ nsCOMPtr<nsIMsgFolder> folder = do_QueryElementAt(allDescendents, i, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ expungedBytes = 0;
+ if (folder)
+ rv = folder->GetExpungedBytes(&expungedBytes);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (expungedBytes > 0)
+ rv = folderArray->AppendElement(folder, false);
+ }
+ rv = folderArray->GetLength(&cnt);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (cnt == 0)
+ return NotifyCompactCompleted();
+ }
+ nsCOMPtr <nsIMsgFolderCompactor> folderCompactor = do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return folderCompactor->CompactFolders(folderArray, nullptr,
+ aListener, aMsgWindow);
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow)
+{
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ nsresult rv = GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool supportsCompaction;
+ msgStore->GetSupportsCompaction(&supportsCompaction);
+ if (supportsCompaction)
+ return msgStore->CompactFolder(this, aListener, aMsgWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::EmptyTrash(nsIMsgWindow *msgWindow,
+ nsIUrlListener *aListener)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgFolder> trashFolder;
+ rv = GetTrashFolder(getter_AddRefs(trashFolder));
+ if (NS_SUCCEEDED(rv))
+ {
+ uint32_t flags;
+ nsCString trashUri;
+ trashFolder->GetURI(trashUri);
+ trashFolder->GetFlags(&flags);
+ int32_t totalMessages = 0;
+ rv = trashFolder->GetTotalMessages(true, &totalMessages);
+ if (totalMessages <= 0)
+ {
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = trashFolder->GetSubFolders(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv,rv);
+ // Any folders to deal with?
+ bool hasMore;
+ rv = enumerator->HasMoreElements(&hasMore);
+ if (NS_FAILED(rv) || !hasMore)
+ return NS_OK;
+ }
+ nsCOMPtr<nsIMsgFolder> parentFolder;
+ rv = trashFolder->GetParent(getter_AddRefs(parentFolder));
+ if (NS_SUCCEEDED(rv) && parentFolder)
+ {
+ nsCOMPtr <nsIDBFolderInfo> transferInfo;
+ trashFolder->GetDBTransferInfo(getter_AddRefs(transferInfo));
+ trashFolder->SetParent(nullptr);
+ parentFolder->PropagateDelete(trashFolder, true, msgWindow);
+ parentFolder->CreateSubfolder(NS_LITERAL_STRING("Trash"), nullptr);
+ nsCOMPtr<nsIMsgFolder> newTrashFolder;
+ rv = GetTrashFolder(getter_AddRefs(newTrashFolder));
+ if (NS_SUCCEEDED(rv) && newTrashFolder)
+ {
+ nsCOMPtr <nsIMsgLocalMailFolder> localTrash = do_QueryInterface(newTrashFolder);
+ newTrashFolder->SetDBTransferInfo(transferInfo);
+ if (localTrash)
+ localTrash->RefreshSizeOnDisk();
+ // update the summary totals so the front end will
+ // show the right thing for the new trash folder
+ // see bug #161999
+ nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
+ nsCOMPtr<nsIMsgDatabase> db;
+ newTrashFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(db));
+ if (dbFolderInfo)
+ {
+ dbFolderInfo->SetNumUnreadMessages(0);
+ dbFolderInfo->SetNumMessages(0);
+ }
+ newTrashFolder->UpdateSummaryTotals(true);
+ }
+ }
+ }
+ return rv;
+}
+
+nsresult nsMsgLocalMailFolder::IsChildOfTrash(bool *result)
+{
+ NS_ENSURE_ARG_POINTER(result);
+ uint32_t parentFlags = 0;
+ *result = false;
+ bool isServer;
+ nsresult rv = GetIsServer(&isServer);
+ if (NS_FAILED(rv) || isServer)
+ return NS_OK;
+
+ rv= GetFlags(&parentFlags); //this is the parent folder
+ if (parentFlags & nsMsgFolderFlags::Trash)
+ {
+ *result = true;
+ return rv;
+ }
+
+ nsCOMPtr<nsIMsgFolder> parentFolder;
+ nsCOMPtr<nsIMsgFolder> thisFolder;
+ rv = QueryInterface(NS_GET_IID(nsIMsgFolder), (void **) getter_AddRefs(thisFolder));
+
+ while (!isServer)
+ {
+ thisFolder->GetParent(getter_AddRefs(parentFolder));
+ if (!parentFolder)
+ return NS_OK;
+
+ rv = parentFolder->GetIsServer(&isServer);
+ if (NS_FAILED(rv) || isServer)
+ return NS_OK;
+
+ rv = parentFolder->GetFlags(&parentFlags);
+ if (NS_FAILED(rv))
+ return NS_OK;
+
+ if (parentFlags & nsMsgFolderFlags::Trash)
+ {
+ *result = true;
+ return rv;
+ }
+
+ thisFolder = parentFolder;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::Delete()
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ msgDBService->CachedDBForFolder(this, getter_AddRefs(mDatabase));
+ if (mDatabase)
+ {
+ mDatabase->ForceClosed();
+ mDatabase = nullptr;
+ }
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+
+ rv = server->GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIFile> summaryFile;
+ rv = GetSummaryFile(getter_AddRefs(summaryFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ //Clean up .sbd folder if it exists.
+ // Remove summary file.
+ rv = summaryFile->Remove(false);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Could not delete msg summary file");
+
+ rv = msgStore->DeleteFolder(this);
+ if (rv == NS_ERROR_FILE_NOT_FOUND ||
+ rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
+ rv = NS_OK; // virtual folders do not have a msgStore file
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::DeleteSubFolders(nsIArray *folders, nsIMsgWindow *msgWindow)
+{
+ nsresult rv;
+ bool isChildOfTrash;
+ IsChildOfTrash(&isChildOfTrash);
+
+ // we don't allow multiple folder selection so this is ok.
+ nsCOMPtr<nsIMsgFolder> folder = do_QueryElementAt(folders, 0);
+ uint32_t folderFlags = 0;
+ if (folder)
+ folder->GetFlags(&folderFlags);
+ // when deleting from trash, or virtual folder, just delete it.
+ if (isChildOfTrash || folderFlags & nsMsgFolderFlags::Virtual)
+ return nsMsgDBFolder::DeleteSubFolders(folders, msgWindow);
+
+ nsCOMPtr<nsIMsgFolder> trashFolder;
+ rv = GetTrashFolder(getter_AddRefs(trashFolder));
+ if (NS_SUCCEEDED(rv))
+ {
+ if (folder)
+ {
+ nsCOMPtr<nsIMsgCopyService> copyService(do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = copyService->CopyFolders(folders, trashFolder, true, nullptr, msgWindow);
+ }
+ }
+ return rv;
+}
+
+nsresult nsMsgLocalMailFolder::ConfirmFolderDeletion(nsIMsgWindow *aMsgWindow,
+ nsIMsgFolder *aFolder, bool *aResult)
+{
+ NS_ENSURE_ARG(aResult);
+ NS_ENSURE_ARG(aMsgWindow);
+ NS_ENSURE_ARG(aFolder);
+ nsCOMPtr<nsIDocShell> docShell;
+ aMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
+ if (docShell)
+ {
+ bool confirmDeletion = true;
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ pPrefBranch->GetBoolPref("mailnews.confirm.moveFoldersToTrash", &confirmDeletion);
+ if (confirmDeletion)
+ {
+ 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);
+
+ nsAutoString folderName;
+ rv = aFolder->GetName(folderName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ const char16_t *formatStrings[1] = { folderName.get() };
+
+ nsAutoString deleteFolderDialogTitle;
+ rv = bundle->GetStringFromName(
+ u"pop3DeleteFolderDialogTitle",
+ getter_Copies(deleteFolderDialogTitle));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString deleteFolderButtonLabel;
+ rv = bundle->GetStringFromName(
+ u"pop3DeleteFolderButtonLabel",
+ getter_Copies(deleteFolderButtonLabel));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoString confirmationStr;
+ rv = bundle->FormatStringFromName(
+ u"pop3MoveFolderToTrash", formatStrings, 1,
+ getter_Copies(confirmationStr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
+ if (dialog)
+ {
+ int32_t buttonPressed = 0;
+ // Default the dialog to "cancel".
+ const uint32_t buttonFlags =
+ (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) +
+ (nsIPrompt::BUTTON_TITLE_CANCEL * nsIPrompt::BUTTON_POS_1);
+ bool dummyValue = false;
+ rv = dialog->ConfirmEx(deleteFolderDialogTitle.get(), confirmationStr.get(),
+ buttonFlags, deleteFolderButtonLabel.get(),
+ nullptr, nullptr, nullptr, &dummyValue,
+ &buttonPressed);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aResult = !buttonPressed; // "ok" is in position 0
+ }
+ }
+ else
+ *aResult = true;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::Rename(const nsAString& aNewName, nsIMsgWindow *msgWindow)
+{
+ // Renaming to the same name is easy
+ if (mName.Equals(aNewName))
+ return NS_OK;
+
+ nsCOMPtr<nsIMsgFolder> parentFolder;
+ nsresult rv = GetParent(getter_AddRefs(parentFolder));
+ if (!parentFolder)
+ return NS_ERROR_NULL_POINTER;
+
+ rv = CheckIfFolderExists(aNewName, parentFolder, msgWindow);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ nsCOMPtr<nsIMsgFolder> newFolder;
+ rv = GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = msgStore->RenameFolder(this, aNewName, getter_AddRefs(newFolder));
+ if (NS_FAILED(rv))
+ {
+ if (msgWindow)
+ (void) ThrowAlertMsg((rv == NS_MSG_FOLDER_EXISTS) ?
+ "folderExists" : "folderRenameFailed", msgWindow);
+ return rv;
+ }
+
+ int32_t count = mSubFolders.Count();
+ if (newFolder)
+ {
+ // Because we just renamed the db, w/o setting the pretty name in it,
+ // we need to force the pretty name to be correct.
+ // SetPrettyName won't write the name to the db if it doesn't think the
+ // name has changed. This hack forces the pretty name to get set in the db.
+ // We could set the new pretty name on the db before renaming the .msf file,
+ // but if the rename failed, it would be out of sync.
+ newFolder->SetPrettyName(EmptyString());
+ newFolder->SetPrettyName(aNewName);
+ bool changed = false;
+ MatchOrChangeFilterDestination(newFolder, true /*caseInsenstive*/, &changed);
+ if (changed)
+ AlertFilterChanged(msgWindow);
+
+ if (count > 0)
+ newFolder->RenameSubFolders(msgWindow, this);
+
+ // Discover the subfolders inside this folder (this is recursive)
+ nsCOMPtr<nsISimpleEnumerator> dummy;
+ newFolder->GetSubFolders(getter_AddRefs(dummy));
+
+ // the newFolder should have the same flags
+ newFolder->SetFlags(mFlags);
+ if (parentFolder)
+ {
+ SetParent(nullptr);
+ parentFolder->PropagateDelete(this, false, msgWindow);
+ parentFolder->NotifyItemAdded(newFolder);
+ }
+ SetFilePath(nullptr); // forget our path, since this folder object renamed itself
+ nsCOMPtr<nsIAtom> folderRenameAtom = MsgGetAtom("RenameCompleted");
+ newFolder->NotifyFolderEvent(folderRenameAtom);
+
+ nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ if (notifier)
+ notifier->NotifyFolderRenamed(this, newFolder);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::RenameSubFolders(nsIMsgWindow *msgWindow, nsIMsgFolder *oldFolder)
+{
+ nsresult rv =NS_OK;
+ mInitialized = true;
+
+ uint32_t flags;
+ oldFolder->GetFlags(&flags);
+ SetFlags(flags);
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = oldFolder->GetSubFolders(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
+ {
+ nsCOMPtr<nsISupports> item;
+ enumerator->GetNext(getter_AddRefs(item));
+
+ nsCOMPtr<nsIMsgFolder> msgFolder(do_QueryInterface(item));
+ if (!msgFolder)
+ continue;
+
+ nsString folderName;
+ rv = msgFolder->GetName(folderName);
+ nsCOMPtr <nsIMsgFolder> newFolder;
+ AddSubfolder(folderName, getter_AddRefs(newFolder));
+ if (newFolder)
+ {
+ newFolder->SetPrettyName(folderName);
+ bool changed = false;
+ msgFolder->MatchOrChangeFilterDestination(newFolder, true /*caseInsenstive*/, &changed);
+ if (changed)
+ msgFolder->AlertFilterChanged(msgWindow);
+ newFolder->RenameSubFolders(msgWindow, msgFolder);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::GetPrettyName(nsAString& prettyName)
+{
+ return nsMsgDBFolder::GetPrettyName(prettyName);
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::SetPrettyName(const nsAString& aName)
+{
+ nsresult rv = nsMsgDBFolder::SetPrettyName(aName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCString folderName;
+ rv = GetStringProperty("folderName", folderName);
+ NS_ConvertUTF16toUTF8 utf8FolderName(mName);
+ return NS_FAILED(rv) || !folderName.Equals(utf8FolderName) ? SetStringProperty("folderName", utf8FolderName) : rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::GetName(nsAString& aName)
+{
+ ReadDBFolderInfo(false);
+ return nsMsgDBFolder::GetName(aName);
+}
+
+nsresult nsMsgLocalMailFolder::OpenDatabase()
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr <nsIFile> file;
+ rv = GetFilePath(getter_AddRefs(file));
+
+ rv = msgDBService->OpenFolderDB(this, true, getter_AddRefs(mDatabase));
+ if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
+ {
+ // check if we're a real folder by looking at the parent folder.
+ nsCOMPtr<nsIMsgFolder> parent;
+ GetParent(getter_AddRefs(parent));
+ if (parent)
+ {
+ // This little dance creates an empty .msf file and then checks
+ // if the db is valid - this works if the folder is empty, which
+ // we don't have a direct way of checking.
+ nsCOMPtr<nsIMsgDatabase> db;
+ rv = msgDBService->CreateNewDB(this, getter_AddRefs(db));
+ if (db)
+ {
+ UpdateSummaryTotals(true);
+ db->Close(true);
+ mDatabase = nullptr;
+ db = nullptr;
+ rv = msgDBService->OpenFolderDB(this, false,
+ getter_AddRefs(mDatabase));
+ if (NS_FAILED(rv))
+ mDatabase = nullptr;
+ }
+ }
+ }
+ else if (NS_FAILED(rv))
+ mDatabase = nullptr;
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::GetDBFolderInfoAndDB(nsIDBFolderInfo **folderInfo, nsIMsgDatabase **db)
+{
+ if (!db || !folderInfo || !mPath || mIsServer)
+ return NS_ERROR_NULL_POINTER; //ducarroz: should we use NS_ERROR_INVALID_ARG?
+
+ nsresult rv;
+ if (mDatabase)
+ rv = NS_OK;
+ else
+ {
+ rv = OpenDatabase();
+
+ if (mAddListener && mDatabase)
+ mDatabase->AddListener(this);
+ }
+
+ NS_IF_ADDREF(*db = mDatabase);
+ if (NS_SUCCEEDED(rv) && *db)
+ rv = (*db)->GetDBFolderInfo(folderInfo);
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::ReadFromFolderCacheElem(nsIMsgFolderCacheElement *element)
+{
+ NS_ENSURE_ARG_POINTER(element);
+ nsresult rv = nsMsgDBFolder::ReadFromFolderCacheElem(element);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCString utf8Name;
+ rv = element->GetStringProperty("folderName", utf8Name);
+ NS_ENSURE_SUCCESS(rv, rv);
+ CopyUTF8toUTF16(utf8Name, mName);
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::WriteToFolderCacheElem(nsIMsgFolderCacheElement *element)
+{
+ NS_ENSURE_ARG_POINTER(element);
+ nsMsgDBFolder::WriteToFolderCacheElem(element);
+ return element->SetStringProperty("folderName", NS_ConvertUTF16toUTF8(mName));
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::GetDeletable(bool *deletable)
+{
+ NS_ENSURE_ARG_POINTER(deletable);
+
+ bool isServer;
+ GetIsServer(&isServer);
+ *deletable = !(isServer || (mFlags & nsMsgFolderFlags::SpecialUse));
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::RefreshSizeOnDisk()
+{
+ int64_t oldFolderSize = mFolderSize;
+ // we set this to unknown to force it to get recalculated from disk
+ mFolderSize = kSizeUnknown;
+ if (NS_SUCCEEDED(GetSizeOnDisk(&mFolderSize)))
+ NotifyIntPropertyChanged(kFolderSizeAtom, oldFolderSize, mFolderSize);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::GetSizeOnDisk(int64_t *aSize)
+{
+ NS_ENSURE_ARG_POINTER(aSize);
+
+ bool isServer = false;
+ nsresult rv = GetIsServer(&isServer);
+ // If this is the rootFolder, return 0 as a safe value.
+ if (NS_FAILED(rv) || isServer)
+ mFolderSize = 0;
+
+ if (mFolderSize == kSizeUnknown)
+ {
+ nsCOMPtr<nsIFile> file;
+ rv = GetFilePath(getter_AddRefs(file));
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Use a temporary variable so that we keep mFolderSize on kSizeUnknown
+ // if GetFileSize() fails.
+ int64_t folderSize;
+ rv = file->GetFileSize(&folderSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mFolderSize = folderSize;
+ }
+ *aSize = mFolderSize;
+ return NS_OK;
+}
+
+nsresult
+nsMsgLocalMailFolder::GetTrashFolder(nsIMsgFolder** result)
+{
+ NS_ENSURE_ARG_POINTER(result);
+ nsresult rv;
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ rv = GetRootFolder(getter_AddRefs(rootFolder));
+ if (NS_SUCCEEDED(rv))
+ {
+ rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Trash, result);
+ if (!*result)
+ rv = NS_ERROR_FAILURE;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::DeleteMessages(nsIArray *messages,
+ nsIMsgWindow *msgWindow,
+ bool deleteStorage, bool isMove,
+ nsIMsgCopyServiceListener* listener, bool allowUndo)
+{
+ NS_ENSURE_ARG_POINTER(messages);
+
+ uint32_t messageCount;
+ nsresult rv = messages->GetLength(&messageCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // shift delete case - (delete to trash is handled in EndMove)
+ // this is also the case when applying retention settings.
+ if (deleteStorage && !isMove)
+ {
+ MarkMsgsOnPop3Server(messages, POP3_DELETE);
+ }
+
+ bool isTrashFolder = mFlags & nsMsgFolderFlags::Trash;
+
+ // notify on delete from trash and shift-delete
+ if (!isMove && (deleteStorage || isTrashFolder))
+ {
+ nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ if (notifier)
+ notifier->NotifyMsgsDeleted(messages);
+ }
+
+ if (!deleteStorage && !isTrashFolder)
+ {
+ nsCOMPtr<nsIMsgFolder> trashFolder;
+ rv = GetTrashFolder(getter_AddRefs(trashFolder));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return copyService->CopyMessages(this, messages, trashFolder,
+ true, listener, msgWindow, allowUndo);
+ }
+ }
+ else
+ {
+ nsCOMPtr <nsIMsgDatabase> msgDB;
+ rv = GetDatabaseWOReparse(getter_AddRefs(msgDB));
+ if (NS_SUCCEEDED(rv))
+ {
+ if (deleteStorage && isMove && GetDeleteFromServerOnMove())
+ MarkMsgsOnPop3Server(messages, POP3_DELETE);
+
+ nsCOMPtr<nsISupports> msgSupport;
+ rv = EnableNotifications(allMessageCountNotifications, false, true /*dbBatching*/);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = GetMsgStore(getter_AddRefs(msgStore));
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = msgStore->DeleteMessages(messages);
+ nsCOMPtr<nsIMsgDBHdr> msgDBHdr;
+ for (uint32_t i = 0; i < messageCount; ++i)
+ {
+ msgDBHdr = do_QueryElementAt(messages, i, &rv);
+ rv = msgDB->DeleteHeader(msgDBHdr, nullptr, false, true);
+ }
+ }
+ }
+ else if (rv == NS_MSG_FOLDER_BUSY)
+ ThrowAlertMsg("deletingMsgsFailed", msgWindow);
+
+ // we are the source folder here for a move or shift delete
+ //enable notifications because that will close the file stream
+ // we've been caching, mark the db as valid, and commit it.
+ EnableNotifications(allMessageCountNotifications, true, true /*dbBatching*/);
+ if (!isMove)
+ NotifyFolderEvent(NS_SUCCEEDED(rv) ? mDeleteOrMoveMsgCompletedAtom : mDeleteOrMoveMsgFailedAtom);
+ if (msgWindow && !isMove)
+ AutoCompact(msgWindow);
+ }
+ }
+
+ if (msgWindow && !isMove && (deleteStorage || isTrashFolder)) {
+ // Clear undo and redo stack.
+ nsCOMPtr<nsITransactionManager> txnMgr;
+ msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
+ if (txnMgr) {
+ txnMgr->Clear();
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::AddMessageDispositionState(nsIMsgDBHdr *aMessage, nsMsgDispositionState aDispositionFlag)
+{
+ nsMsgMessageFlagType msgFlag = 0;
+ switch (aDispositionFlag) {
+ case nsIMsgFolder::nsMsgDispositionState_Replied:
+ msgFlag = nsMsgMessageFlags::Replied;
+ break;
+ case nsIMsgFolder::nsMsgDispositionState_Forwarded:
+ msgFlag = nsMsgMessageFlags::Forwarded;
+ break;
+ default:
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsresult rv = nsMsgDBFolder::AddMessageDispositionState(aMessage, aDispositionFlag);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ messages->AppendElement(aMessage, false);
+ return msgStore->ChangeFlags(messages, msgFlag, true);
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::MarkMessagesRead(nsIArray *aMessages, bool aMarkRead)
+{
+ nsresult rv = nsMsgDBFolder::MarkMessagesRead(aMessages, aMarkRead);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return msgStore->ChangeFlags(aMessages, nsMsgMessageFlags::Read, aMarkRead);
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::MarkMessagesFlagged(nsIArray *aMessages,
+ bool aMarkFlagged)
+{
+ nsresult rv = nsMsgDBFolder::MarkMessagesFlagged(aMessages, aMarkFlagged);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return msgStore->ChangeFlags(aMessages, nsMsgMessageFlags::Marked,
+ aMarkFlagged);
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::MarkAllMessagesRead(nsIMsgWindow *aMsgWindow)
+{
+ nsresult rv = GetDatabase();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsMsgKey *thoseMarked = nullptr;
+ uint32_t numMarked = 0;
+ EnableNotifications(allMessageCountNotifications, false, true /*dbBatching*/);
+ rv = mDatabase->MarkAllRead(&numMarked, &thoseMarked);
+ EnableNotifications(allMessageCountNotifications, true, true /*dbBatching*/);
+ if (NS_FAILED(rv) || !numMarked || !thoseMarked)
+ return rv;
+
+ do {
+ nsCOMPtr<nsIMutableArray> messages;
+ rv = MsgGetHdrsFromKeys(mDatabase, thoseMarked, numMarked, getter_AddRefs(messages));
+ if (NS_FAILED(rv))
+ break;
+
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = GetMsgStore(getter_AddRefs(msgStore));
+ if (NS_FAILED(rv))
+ break;
+
+ rv = msgStore->ChangeFlags(messages, nsMsgMessageFlags::Read, true);
+ if (NS_FAILED(rv))
+ break;
+
+ mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
+
+ // Setup a undo-state
+ if (aMsgWindow)
+ rv = AddMarkAllReadUndoAction(aMsgWindow, thoseMarked, numMarked);
+ } while (false);
+
+ free(thoseMarked);
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::MarkThreadRead(nsIMsgThread *thread)
+{
+ nsresult rv = GetDatabase();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsMsgKey *thoseMarked = nullptr;
+ uint32_t numMarked = 0;
+ rv = mDatabase->MarkThreadRead(thread, nullptr, &numMarked, &thoseMarked);
+ if (NS_FAILED(rv) || !numMarked || !thoseMarked)
+ return rv;
+
+ do {
+ nsCOMPtr<nsIMutableArray> messages;
+ rv = MsgGetHdrsFromKeys(mDatabase, thoseMarked, numMarked, getter_AddRefs(messages));
+ if (NS_FAILED(rv))
+ break;
+
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = GetMsgStore(getter_AddRefs(msgStore));
+ if (NS_FAILED(rv))
+ break;
+
+ rv = msgStore->ChangeFlags(messages, nsMsgMessageFlags::Read, true);
+ if (NS_FAILED(rv))
+ break;
+
+ mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
+ } while (false);
+
+ free(thoseMarked);
+ return rv;
+}
+
+nsresult
+nsMsgLocalMailFolder::InitCopyState(nsISupports* aSupport,
+ nsIArray* messages,
+ bool isMove,
+ nsIMsgCopyServiceListener* listener,
+ nsIMsgWindow *msgWindow, bool isFolder,
+ bool allowUndo)
+{
+ nsCOMPtr<nsIFile> path;
+
+ NS_ASSERTION(!mCopyState, "already copying a msg into this folder");
+ if (mCopyState)
+ return NS_ERROR_FAILURE; // already has a copy in progress
+
+ // get mDatabase set, so we can use it to add new hdrs to this db.
+ // calling GetDatabase will set mDatabase - we use the comptr
+ // here to avoid doubling the refcnt on mDatabase. We don't care if this
+ // fails - we just want to give it a chance. It will definitely fail in
+ // nsLocalMailFolder::EndCopy because we will have written data to the folder
+ // and changed its size.
+ nsCOMPtr <nsIMsgDatabase> msgDB;
+ GetDatabaseWOReparse(getter_AddRefs(msgDB));
+ bool isLocked;
+
+ GetLocked(&isLocked);
+ if (isLocked)
+ return NS_MSG_FOLDER_BUSY;
+
+ AcquireSemaphore(static_cast<nsIMsgLocalMailFolder*>(this));
+
+ mCopyState = new nsLocalMailCopyState();
+ NS_ENSURE_TRUE(mCopyState, NS_ERROR_OUT_OF_MEMORY);
+
+ mCopyState->m_dataBuffer = (char*) PR_CALLOC(COPY_BUFFER_SIZE+1);
+ NS_ENSURE_TRUE(mCopyState->m_dataBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ mCopyState->m_dataBufferSize = COPY_BUFFER_SIZE;
+ mCopyState->m_destDB = msgDB;
+
+ //Before we continue we should verify that there is enough diskspace.
+ //XXX How do we do this?
+ nsresult rv;
+ mCopyState->m_srcSupport = do_QueryInterface(aSupport, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mCopyState->m_messages = messages;
+ mCopyState->m_curCopyIndex = 0;
+ mCopyState->m_isMove = isMove;
+ mCopyState->m_isFolder = isFolder;
+ mCopyState->m_allowUndo = allowUndo;
+ mCopyState->m_msgWindow = msgWindow;
+ rv = messages->GetLength(&mCopyState->m_totalMsgCount);
+ if (listener)
+ mCopyState->m_listener = do_QueryInterface(listener, &rv);
+ mCopyState->m_copyingMultipleMessages = false;
+ mCopyState->m_wholeMsgInStream = false;
+
+ // If we have source messages then we need destination messages too.
+ if (messages)
+ mCopyState->m_destMessages = do_CreateInstance(NS_ARRAY_CONTRACTID);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::OnAnnouncerGoingAway(nsIDBChangeAnnouncer *instigator)
+{
+ if (mCopyState)
+ mCopyState->m_destDB = nullptr;
+ return nsMsgDBFolder::OnAnnouncerGoingAway(instigator);
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::OnCopyCompleted(nsISupports *srcSupport, bool moveCopySucceeded)
+{
+ if (mCopyState && mCopyState->m_notifyFolderLoaded)
+ NotifyFolderEvent(mFolderLoadedAtom);
+
+ (void) RefreshSizeOnDisk();
+ // we are the destination folder for a move/copy
+ bool haveSemaphore;
+ nsresult rv = TestSemaphore(static_cast<nsIMsgLocalMailFolder*>(this), &haveSemaphore);
+ if (NS_SUCCEEDED(rv) && haveSemaphore)
+ ReleaseSemaphore(static_cast<nsIMsgLocalMailFolder*>(this));
+
+ if (mCopyState && !mCopyState->m_newMsgKeywords.IsEmpty() &&
+ mCopyState->m_newHdr)
+ {
+ nsCOMPtr<nsIMutableArray> messageArray(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_TRUE(messageArray, rv);
+ messageArray->AppendElement(mCopyState->m_newHdr, false);
+ AddKeywordsToMessages(messageArray, mCopyState->m_newMsgKeywords);
+ }
+ if (moveCopySucceeded && mDatabase)
+ {
+ mDatabase->SetSummaryValid(true);
+ (void) CloseDBIfFolderNotOpen();
+ }
+
+ delete mCopyState;
+ mCopyState = nullptr;
+ nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return copyService->NotifyCompletion(srcSupport, this, moveCopySucceeded ? NS_OK : NS_ERROR_FAILURE);
+}
+
+bool nsMsgLocalMailFolder::CheckIfSpaceForCopy(nsIMsgWindow *msgWindow,
+ nsIMsgFolder *srcFolder,
+ nsISupports *srcSupports,
+ bool isMove,
+ int64_t totalMsgSize)
+{
+ bool spaceNotAvailable = true;
+ nsresult rv = WarnIfLocalFileTooBig(msgWindow, totalMsgSize, &spaceNotAvailable);
+ if (NS_FAILED(rv) || spaceNotAvailable)
+ {
+ if (isMove && srcFolder)
+ srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom);
+ OnCopyCompleted(srcSupports, false);
+ return false;
+ }
+ return true;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::CopyMessages(nsIMsgFolder* srcFolder, nsIArray*
+ messages, bool isMove,
+ nsIMsgWindow *msgWindow,
+ nsIMsgCopyServiceListener* listener,
+ bool isFolder, bool allowUndo)
+{
+ nsCOMPtr<nsISupports> srcSupport = do_QueryInterface(srcFolder);
+ bool isServer;
+ nsresult rv = GetIsServer(&isServer);
+ if (NS_SUCCEEDED(rv) && isServer)
+ {
+ NS_ERROR("Destination is the root folder. Cannot move/copy here");
+ if (isMove)
+ srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom);
+ return OnCopyCompleted(srcSupport, false);
+ }
+
+ UpdateTimestamps(allowUndo);
+ nsCString protocolType;
+ rv = srcFolder->GetURI(protocolType);
+ protocolType.SetLength(protocolType.FindChar(':'));
+
+ bool needOfflineBody = (WeAreOffline() &&
+ (MsgLowerCaseEqualsLiteral(protocolType, "imap") ||
+ MsgLowerCaseEqualsLiteral(protocolType, "news")));
+ int64_t totalMsgSize = 0;
+ uint32_t numMessages = 0;
+ messages->GetLength(&numMessages);
+ for (uint32_t i = 0; i < numMessages; i++)
+ {
+ nsCOMPtr<nsIMsgDBHdr> message(do_QueryElementAt(messages, i, &rv));
+ if (NS_SUCCEEDED(rv) && message)
+ {
+ nsMsgKey key;
+ uint32_t msgSize;
+ message->GetMessageSize(&msgSize);
+
+ /* 200 is a per-message overhead to account for any extra data added
+ to the message.
+ */
+ totalMsgSize += msgSize + 200;
+
+ if (needOfflineBody)
+ {
+ bool hasMsgOffline = false;
+ message->GetMessageKey(&key);
+ srcFolder->HasMsgOffline(key, &hasMsgOffline);
+ if (!hasMsgOffline)
+ {
+ if (isMove)
+ srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom);
+ ThrowAlertMsg("cantMoveMsgWOBodyOffline", msgWindow);
+ return OnCopyCompleted(srcSupport, false);
+ }
+ }
+ }
+ }
+
+ if (!CheckIfSpaceForCopy(msgWindow, srcFolder, srcSupport, isMove,
+ totalMsgSize))
+ return NS_OK;
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool storeDidCopy = false;
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsITransaction> undoTxn;
+ nsCOMPtr<nsIArray> dstHdrs;
+ rv = msgStore->CopyMessages(isMove, messages, this, listener,
+ getter_AddRefs(dstHdrs), getter_AddRefs(undoTxn),
+ &storeDidCopy);
+ if (storeDidCopy)
+ {
+ NS_ASSERTION(undoTxn, "if store does copy, it needs to add undo action");
+ if (msgWindow && undoTxn)
+ {
+ nsCOMPtr<nsITransactionManager> txnMgr;
+ msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
+ if (txnMgr)
+ txnMgr->DoTransaction(undoTxn);
+ }
+ if (isMove)
+ srcFolder->NotifyFolderEvent(NS_SUCCEEDED(rv) ? mDeleteOrMoveMsgCompletedAtom :
+ mDeleteOrMoveMsgFailedAtom);
+
+ if (NS_SUCCEEDED(rv)) {
+ // If the store did the copy, like maildir, we need to mark messages on the server.
+ // Otherwise that's done in EndMove().
+ nsCOMPtr <nsIMsgLocalMailFolder> localDstFolder;
+ QueryInterface(NS_GET_IID(nsIMsgLocalMailFolder), getter_AddRefs(localDstFolder));
+ if (localDstFolder)
+ {
+ // If we are the trash and a local msg is being moved to us, mark the source
+ // for delete from server, if so configured.
+ if (mFlags & nsMsgFolderFlags::Trash)
+ {
+ // If we're deleting on all moves, we'll mark this message for deletion when
+ // we call DeleteMessages on the source folder. So don't mark it for deletion
+ // here, in that case.
+ if (!GetDeleteFromServerOnMove())
+ localDstFolder->MarkMsgsOnPop3Server(dstHdrs, POP3_DELETE);
+ }
+ }
+ }
+ return rv;
+ }
+ // If the store doesn't do the copy, we'll stream the source messages into
+ // the target folder, using getMsgInputStream and getNewMsgOutputStream.
+
+ // don't update the counts in the dest folder until it is all over
+ EnableNotifications(allMessageCountNotifications, false, false /*dbBatching*/); //dest folder doesn't need db batching
+
+ // sort the message array by key
+ uint32_t numMsgs = 0;
+ messages->GetLength(&numMsgs);
+ nsTArray<nsMsgKey> keyArray(numMsgs);
+ if (numMsgs > 1)
+ {
+ for (uint32_t i = 0; i < numMsgs; i++)
+ {
+ nsCOMPtr<nsIMsgDBHdr> aMessage = do_QueryElementAt(messages, i, &rv);
+ if (NS_SUCCEEDED(rv) && aMessage)
+ {
+ nsMsgKey key;
+ aMessage->GetMessageKey(&key);
+ keyArray.AppendElement(key);
+ }
+ }
+
+ keyArray.Sort();
+
+ nsCOMPtr<nsIMutableArray> sortedMsgs(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ rv = MessagesInKeyOrder(keyArray, srcFolder, sortedMsgs);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = InitCopyState(srcSupport, sortedMsgs, isMove, listener, msgWindow, isFolder, allowUndo);
+ }
+ else
+ rv = InitCopyState(srcSupport, messages, isMove, listener, msgWindow, isFolder, allowUndo);
+
+ if (NS_FAILED(rv))
+ {
+ ThrowAlertMsg("operationFailedFolderBusy", msgWindow);
+ (void) OnCopyCompleted(srcSupport, false);
+ return rv;
+ }
+
+ if (!MsgLowerCaseEqualsLiteral(protocolType, "mailbox"))
+ {
+ mCopyState->m_dummyEnvelopeNeeded = true;
+ nsParseMailMessageState* parseMsgState = new nsParseMailMessageState();
+ if (parseMsgState)
+ {
+ nsCOMPtr<nsIMsgDatabase> msgDb;
+ mCopyState->m_parseMsgState = parseMsgState;
+ GetDatabaseWOReparse(getter_AddRefs(msgDb));
+ if (msgDb)
+ parseMsgState->SetMailDB(msgDb);
+ }
+ }
+
+ // undo stuff
+ if (allowUndo) //no undo for folder move/copy or or move/copy from search window
+ {
+ RefPtr<nsLocalMoveCopyMsgTxn> msgTxn = new nsLocalMoveCopyMsgTxn;
+ if (msgTxn && NS_SUCCEEDED(msgTxn->Init(srcFolder, this, isMove)))
+ {
+ msgTxn->SetMsgWindow(msgWindow);
+ if (isMove)
+ {
+ if (mFlags & nsMsgFolderFlags::Trash)
+ msgTxn->SetTransactionType(nsIMessenger::eDeleteMsg);
+ else
+ msgTxn->SetTransactionType(nsIMessenger::eMoveMsg);
+ }
+ else
+ msgTxn->SetTransactionType(nsIMessenger::eCopyMsg);
+ msgTxn.swap(mCopyState->m_undoMsgTxn);
+ }
+ }
+
+ if (numMsgs > 1 && ((MsgLowerCaseEqualsLiteral(protocolType, "imap") && !WeAreOffline()) ||
+ MsgLowerCaseEqualsLiteral(protocolType, "mailbox")))
+ {
+ mCopyState->m_copyingMultipleMessages = true;
+ rv = CopyMessagesTo(mCopyState->m_messages, keyArray, msgWindow, this, isMove);
+ if (NS_FAILED(rv))
+ {
+ NS_ERROR("copy message failed");
+ (void) OnCopyCompleted(srcSupport, false);
+ }
+ }
+ else
+ {
+ nsCOMPtr<nsISupports> msgSupport = do_QueryElementAt(mCopyState->m_messages, 0);
+ if (msgSupport)
+ {
+ rv = CopyMessageTo(msgSupport, this, msgWindow, isMove);
+ if (NS_FAILED(rv))
+ {
+ NS_ASSERTION(false, "copy message failed");
+ (void) OnCopyCompleted(srcSupport, false);
+ }
+ }
+ }
+ // if this failed immediately, need to turn back on notifications and inform FE.
+ if (NS_FAILED(rv))
+ {
+ if (isMove)
+ srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom);
+ EnableNotifications(allMessageCountNotifications, true, false /*dbBatching*/); //dest folder doesn't need db batching
+ }
+ return rv;
+}
+// for srcFolder that are on different server than the dstFolder.
+// "this" is the parent of the new dest folder.
+nsresult
+nsMsgLocalMailFolder::CopyFolderAcrossServer(nsIMsgFolder* srcFolder, nsIMsgWindow *msgWindow,
+ nsIMsgCopyServiceListener *listener )
+{
+ mInitialized = true;
+
+ nsString folderName;
+ srcFolder->GetName(folderName);
+
+ nsCOMPtr<nsIMsgFolder> newMsgFolder;
+ nsresult rv = CreateSubfolderInternal(folderName, msgWindow, getter_AddRefs(newMsgFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISimpleEnumerator> messages;
+ rv = srcFolder->GetMessages(getter_AddRefs(messages));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMutableArray> msgArray(do_CreateInstance(NS_ARRAY_CONTRACTID));
+
+ bool hasMoreElements = false;
+ nsCOMPtr<nsISupports> aSupport;
+
+ if (messages)
+ rv = messages->HasMoreElements(&hasMoreElements);
+
+ while (NS_SUCCEEDED(rv) && hasMoreElements)
+ {
+ rv = messages->GetNext(getter_AddRefs(aSupport));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = msgArray->AppendElement(aSupport, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = messages->HasMoreElements(&hasMoreElements);
+ }
+
+ uint32_t numMsgs=0;
+ msgArray->GetLength(&numMsgs);
+
+ if (numMsgs > 0 ) //if only srcFolder has messages..
+ newMsgFolder->CopyMessages(srcFolder, msgArray, false, msgWindow, listener, true /* is folder*/, false /* allowUndo */);
+ else
+ {
+ nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(newMsgFolder);
+ if (localFolder)
+ {
+ // normally these would get called from ::EndCopy when the last message
+ // was finished copying. But since there are no messages, we have to call
+ // them explicitly.
+ nsCOMPtr<nsISupports> srcSupports = do_QueryInterface(newMsgFolder);
+ localFolder->CopyAllSubFolders(srcFolder, msgWindow, listener);
+ return localFolder->OnCopyCompleted(srcSupports, true);
+ }
+ }
+ return NS_OK; // otherwise the front-end will say Exception::CopyFolder
+}
+
+nsresult //copy the sub folders
+nsMsgLocalMailFolder::CopyAllSubFolders(nsIMsgFolder *srcFolder,
+ nsIMsgWindow *msgWindow,
+ nsIMsgCopyServiceListener *listener )
+{
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ nsresult rv = srcFolder->GetSubFolders(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
+ {
+ nsCOMPtr<nsISupports> item;
+ enumerator->GetNext(getter_AddRefs(item));
+
+ nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(item));
+ if (folder)
+ CopyFolderAcrossServer(folder, msgWindow, listener);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::CopyFolder( nsIMsgFolder* srcFolder, bool isMoveFolder,
+ nsIMsgWindow *msgWindow,
+ nsIMsgCopyServiceListener* listener)
+{
+ NS_ENSURE_ARG_POINTER(srcFolder);
+ // isMoveFolder == true when "this" and srcFolder are on same server
+ return isMoveFolder ? CopyFolderLocal(srcFolder, isMoveFolder, msgWindow, listener ) :
+ CopyFolderAcrossServer(srcFolder, msgWindow, listener );
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::CopyFolderLocal(nsIMsgFolder *srcFolder,
+ bool isMoveFolder,
+ nsIMsgWindow *msgWindow,
+ nsIMsgCopyServiceListener *aListener)
+{
+ mInitialized = true;
+ bool isChildOfTrash;
+ nsresult rv = IsChildOfTrash(&isChildOfTrash);
+ if (NS_SUCCEEDED(rv) && isChildOfTrash)
+ {
+ // do it just for the parent folder (isMoveFolder is true for parent only) if we are deleting/moving a folder tree
+ // don't confirm for rss folders.
+ if (isMoveFolder)
+ {
+ // if there's a msgWindow, confirm the deletion
+ if (msgWindow)
+ {
+
+ bool okToDelete = false;
+ ConfirmFolderDeletion(msgWindow, srcFolder, &okToDelete);
+ if (!okToDelete)
+ return NS_MSG_ERROR_COPY_FOLDER_ABORTED;
+ }
+ // if we are moving a favorite folder to trash, we should clear the favorites flag
+ // so it gets removed from the view.
+ srcFolder->ClearFlag(nsMsgFolderFlags::Favorite);
+ }
+
+ bool match = false;
+ rv = srcFolder->MatchOrChangeFilterDestination(nullptr, false, &match);
+ if (match && msgWindow)
+ {
+ bool confirmed = false;
+ srcFolder->ConfirmFolderDeletionForFilter(msgWindow, &confirmed);
+ if (!confirmed)
+ return NS_MSG_ERROR_COPY_FOLDER_ABORTED;
+ }
+ }
+
+ nsAutoString newFolderName;
+ nsAutoString folderName;
+ rv = srcFolder->GetName(folderName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!isMoveFolder) {
+ rv = CheckIfFolderExists(folderName, this, msgWindow);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+ else
+ {
+ // If folder name already exists in destination, generate a new unique name.
+ bool containsChild = true;
+ uint32_t i;
+ for (i = 1; containsChild; i++) {
+ newFolderName.Assign(folderName);
+ if (i > 1) {
+ // This could be localizable but Toolkit is fine without it, see
+ // mozilla/toolkit/content/contentAreaUtils.js::uniqueFile()
+ newFolderName.Append('(');
+ newFolderName.AppendInt(i);
+ newFolderName.Append(')');
+ }
+ rv = ContainsChildNamed(newFolderName, &containsChild);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ }
+
+ // 'i' is one more than the number of iterations done
+ // and the number tacked onto the name of the folder.
+ if (i > 2 && !isChildOfTrash) {
+ // Folder name already exists, ask if rename is OK.
+ // If moving to Trash, don't ask and do it.
+ if (!ConfirmAutoFolderRename(msgWindow, folderName, newFolderName))
+ return NS_MSG_ERROR_COPY_FOLDER_ABORTED;
+ }
+ }
+
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = GetMsgStore(getter_AddRefs(msgStore));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return msgStore->CopyFolder(srcFolder, this, isMoveFolder, msgWindow,
+ aListener, newFolderName);
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::CopyFileMessage(nsIFile* aFile,
+ nsIMsgDBHdr *msgToReplace,
+ bool isDraftOrTemplate,
+ uint32_t newMsgFlags,
+ const nsACString &aNewMsgKeywords,
+ nsIMsgWindow *msgWindow,
+ nsIMsgCopyServiceListener* listener)
+{
+ NS_ENSURE_ARG_POINTER(aFile);
+ nsresult rv = NS_ERROR_NULL_POINTER;
+ nsParseMailMessageState* parseMsgState = nullptr;
+ int64_t fileSize = 0;
+
+ nsCOMPtr<nsISupports> fileSupport(do_QueryInterface(aFile, &rv));
+
+ aFile->GetFileSize(&fileSize);
+ if (!CheckIfSpaceForCopy(msgWindow, nullptr, fileSupport, false, fileSize))
+ return NS_OK;
+
+ nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID));
+
+ if (msgToReplace)
+ messages->AppendElement(msgToReplace, false);
+
+ rv = InitCopyState(fileSupport, messages, msgToReplace ? true : false,
+ listener, msgWindow, false, false);
+ if (NS_SUCCEEDED(rv))
+ {
+ if (mCopyState)
+ mCopyState->m_newMsgKeywords = aNewMsgKeywords;
+
+ parseMsgState = new nsParseMailMessageState();
+ NS_ENSURE_TRUE(parseMsgState, NS_ERROR_OUT_OF_MEMORY);
+ nsCOMPtr<nsIMsgDatabase> msgDb;
+ mCopyState->m_parseMsgState = parseMsgState;
+ GetDatabaseWOReparse(getter_AddRefs(msgDb));
+ if (msgDb)
+ parseMsgState->SetMailDB(msgDb);
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile);
+
+ // All or none for adding a message file to the store
+ if (NS_SUCCEEDED(rv) && fileSize > PR_INT32_MAX)
+ rv = NS_ERROR_ILLEGAL_VALUE; // may need error code for max msg size
+
+ if (NS_SUCCEEDED(rv) && inputStream)
+ {
+ char buffer[5];
+ uint32_t readCount;
+ rv = inputStream->Read(buffer, 5, &readCount);
+ if (NS_SUCCEEDED(rv))
+ {
+ if (strncmp(buffer, "From ", 5))
+ mCopyState->m_dummyEnvelopeNeeded = true;
+ nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(inputStream, &rv);
+ if (NS_SUCCEEDED(rv))
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ }
+ }
+
+ mCopyState->m_wholeMsgInStream = true;
+ if (NS_SUCCEEDED(rv))
+ rv = BeginCopy(nullptr);
+
+ if (NS_SUCCEEDED(rv))
+ rv = CopyData(inputStream, (int32_t) fileSize);
+
+ if (NS_SUCCEEDED(rv))
+ rv = EndCopy(true);
+
+ //mDatabase should have been initialized above - if we got msgDb
+ // If we were going to delete, here is where we would do it. But because
+ // existing code already supports doing those deletes, we are just going
+ // to end the copy.
+ if (NS_SUCCEEDED(rv) && msgToReplace && mDatabase)
+ rv = OnCopyCompleted(fileSupport, true);
+
+ if (inputStream)
+ inputStream->Close();
+ }
+
+ if (NS_FAILED(rv))
+ (void) OnCopyCompleted(fileSupport, false);
+
+ return rv;
+}
+
+nsresult nsMsgLocalMailFolder::DeleteMessage(nsISupports *message,
+ nsIMsgWindow *msgWindow,
+ bool deleteStorage, bool commit)
+{
+ nsresult rv = NS_OK;
+ if (deleteStorage)
+ {
+ nsCOMPtr <nsIMsgDBHdr> msgDBHdr(do_QueryInterface(message, &rv));
+
+ if (NS_SUCCEEDED(rv))
+ {
+ GetDatabase();
+ if (mDatabase)
+ rv = mDatabase->DeleteHeader(msgDBHdr, nullptr, commit, true);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::GetNewMessages(nsIMsgWindow *aWindow, nsIUrlListener *aListener)
+{
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ nsresult rv = GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
+
+ nsCOMPtr<nsILocalMailIncomingServer> localMailServer = do_QueryInterface(server, &rv);
+ NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
+
+ // XXX todo, move all this into nsILocalMailIncomingServer's GetNewMail
+ // so that we don't have to have RSS foo here.
+ nsCOMPtr<nsIRssIncomingServer> rssServer = do_QueryInterface(server, &rv);
+ if (NS_SUCCEEDED(rv))
+ return localMailServer->GetNewMail(aWindow, aListener, this, nullptr);
+
+ nsCOMPtr<nsIMsgFolder> inbox;
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ rv = server->GetRootMsgFolder(getter_AddRefs(rootFolder));
+ if (NS_SUCCEEDED(rv) && rootFolder)
+ {
+ rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox, getter_AddRefs(inbox));
+ }
+ nsCOMPtr<nsIMsgLocalMailFolder> localInbox = do_QueryInterface(inbox, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ bool valid = false;
+ nsCOMPtr <nsIMsgDatabase> db;
+ // this will kick off a reparse if the db is out of date.
+ rv = localInbox->GetDatabaseWithReparse(nullptr, aWindow, getter_AddRefs(db));
+ if (NS_SUCCEEDED(rv))
+ {
+ db->GetSummaryValid(&valid);
+ rv = valid ? localMailServer->GetNewMail(aWindow, aListener, inbox, nullptr) :
+ localInbox->SetCheckForNewMessagesAfterParsing(true);
+ }
+ }
+ return rv;
+}
+
+nsresult nsMsgLocalMailFolder::WriteStartOfNewMessage()
+{
+ nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(mCopyState->m_fileStream);
+ int64_t filePos;
+ seekableStream->Tell(&filePos);
+
+ // CopyFileMessage() and CopyMessages() from servers other than pop3
+ if (mCopyState->m_parseMsgState)
+ {
+ if (mCopyState->m_parseMsgState->m_newMsgHdr)
+ mCopyState->m_parseMsgState->m_newMsgHdr->GetMessageKey(&mCopyState->m_curDstKey);
+ mCopyState->m_parseMsgState->SetEnvelopePos(filePos);
+ mCopyState->m_parseMsgState->SetState(nsIMsgParseMailMsgState::ParseHeadersState);
+ }
+ if (mCopyState->m_dummyEnvelopeNeeded)
+ {
+ nsCString result;
+ nsAutoCString nowStr;
+ MsgGenerateNowStr(nowStr);
+ result.AppendLiteral("From - ");
+ result.Append(nowStr);
+ result.Append(MSG_LINEBREAK);
+
+ // *** jt - hard code status line for now; come back later
+ nsresult rv;
+ nsCOMPtr <nsIMsgDBHdr> curSourceMessage = do_QueryElementAt(mCopyState->m_messages,
+ mCopyState->m_curCopyIndex, &rv);
+
+ char statusStrBuf[50];
+ if (curSourceMessage)
+ {
+ uint32_t dbFlags = 0;
+ curSourceMessage->GetFlags(&dbFlags);
+
+ // write out x-mozilla-status, but make sure we don't write out nsMsgMessageFlags::Offline
+ PR_snprintf(statusStrBuf, sizeof(statusStrBuf), X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK,
+ dbFlags & ~(nsMsgMessageFlags::RuntimeOnly | nsMsgMessageFlags::Offline) & 0x0000FFFF);
+ }
+ else
+ strcpy(statusStrBuf, "X-Mozilla-Status: 0001" MSG_LINEBREAK);
+ uint32_t bytesWritten;
+ mCopyState->m_fileStream->Write(result.get(), result.Length(), &bytesWritten);
+ if (mCopyState->m_parseMsgState)
+ mCopyState->m_parseMsgState->ParseAFolderLine(
+ result.get(), result.Length());
+ mCopyState->m_fileStream->Write(statusStrBuf, strlen(statusStrBuf), &bytesWritten);
+ if (mCopyState->m_parseMsgState)
+ mCopyState->m_parseMsgState->ParseAFolderLine(
+ statusStrBuf, strlen(statusStrBuf));
+ result = "X-Mozilla-Status2: 00000000" MSG_LINEBREAK;
+ mCopyState->m_fileStream->Write(result.get(), result.Length(), &bytesWritten);
+ if (mCopyState->m_parseMsgState)
+ mCopyState->m_parseMsgState->ParseAFolderLine(
+ result.get(), result.Length());
+ result = X_MOZILLA_KEYWORDS;
+ mCopyState->m_fileStream->Write(result.get(), result.Length(), &bytesWritten);
+ if (mCopyState->m_parseMsgState)
+ mCopyState->m_parseMsgState->ParseAFolderLine(
+ result.get(), result.Length());
+ mCopyState->m_fromLineSeen = true;
+ }
+ else
+ mCopyState->m_fromLineSeen = false;
+
+ mCopyState->m_curCopyIndex++;
+ return NS_OK;
+}
+
+nsresult nsMsgLocalMailFolder::InitCopyMsgHdrAndFileStream()
+{
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ nsresult rv = GetMsgStore(getter_AddRefs(mCopyState->m_msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool reusable;
+ rv = mCopyState->m_msgStore->
+ GetNewMsgOutputStream(this, getter_AddRefs(mCopyState->m_newHdr),
+ &reusable,
+ getter_AddRefs(mCopyState->m_fileStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (mCopyState->m_parseMsgState)
+ mCopyState->m_parseMsgState->SetNewMsgHdr(mCopyState->m_newHdr);
+ return rv;
+}
+
+//nsICopyMessageListener
+NS_IMETHODIMP nsMsgLocalMailFolder::BeginCopy(nsIMsgDBHdr *message)
+{
+ if (!mCopyState)
+ return NS_ERROR_NULL_POINTER;
+
+ nsresult rv;
+ if (!mCopyState->m_copyingMultipleMessages)
+ {
+ rv = InitCopyMsgHdrAndFileStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(mCopyState->m_fileStream, &rv);
+
+ // XXX ToDo: When copying multiple messages from a non-offline-enabled IMAP
+ // server, this fails. (The copy succeeds because the file stream is created
+ // subsequently in StartMessage) We should not be warning on an expected error.
+ // Perhaps there are unexpected consequences of returning early?
+ NS_ENSURE_SUCCESS(rv, rv);
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
+
+ int32_t messageIndex = (mCopyState->m_copyingMultipleMessages) ? mCopyState->m_curCopyIndex - 1 : mCopyState->m_curCopyIndex;
+ NS_ASSERTION(!mCopyState->m_copyingMultipleMessages || messageIndex >= 0, "messageIndex invalid");
+ // by the time we get here, m_curCopyIndex is 1 relative because WriteStartOfNewMessage increments it
+ mCopyState->m_messages->QueryElementAt(messageIndex, NS_GET_IID(nsIMsgDBHdr),
+ (void **)getter_AddRefs(mCopyState->m_message));
+ // The flags of the source message can get changed when it is deleted, so
+ // save them here.
+ if (mCopyState->m_message)
+ mCopyState->m_message->GetFlags(&(mCopyState->m_flags));
+ DisplayMoveCopyStatusMsg();
+ if (mCopyState->m_listener)
+ mCopyState->m_listener->OnProgress(mCopyState->m_curCopyIndex, mCopyState->m_totalMsgCount);
+ // if we're copying more than one message, StartMessage will handle this.
+ return !mCopyState->m_copyingMultipleMessages ? WriteStartOfNewMessage() : rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::CopyData(nsIInputStream *aIStream, int32_t aLength)
+{
+ //check to make sure we have control of the write.
+ bool haveSemaphore;
+ nsresult rv = NS_OK;
+
+ rv = TestSemaphore(static_cast<nsIMsgLocalMailFolder*>(this), &haveSemaphore);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!haveSemaphore)
+ return NS_MSG_FOLDER_BUSY;
+
+ if (!mCopyState)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ uint32_t readCount;
+ //allocate one extra byte for '\0' at the end and another extra byte at the
+ //front to insert a '>' if we have a "From" line
+ //allocate 2 more for crlf that may be needed for those without crlf at end of file
+ if ( aLength + mCopyState->m_leftOver + 4 > mCopyState->m_dataBufferSize )
+ {
+ char *newBuffer = (char *) PR_REALLOC(mCopyState->m_dataBuffer, aLength + mCopyState->m_leftOver + 4);
+ if (!newBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ mCopyState->m_dataBuffer = newBuffer;
+ mCopyState->m_dataBufferSize = aLength + mCopyState->m_leftOver + 3;
+ }
+
+ nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(mCopyState->m_fileStream, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
+
+ rv = aIStream->Read(mCopyState->m_dataBuffer + mCopyState->m_leftOver + 1, aLength, &readCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mCopyState->m_leftOver += readCount;
+ mCopyState->m_dataBuffer[mCopyState->m_leftOver + 1] ='\0';
+ char *start = mCopyState->m_dataBuffer + 1;
+ char *endBuffer = mCopyState->m_dataBuffer + mCopyState->m_leftOver + 1;
+
+ uint32_t lineLength;
+ uint32_t bytesWritten;
+
+ while (1)
+ {
+ char *end = PL_strnpbrk(start, "\r\n", endBuffer - start);
+ if (!end)
+ {
+ mCopyState->m_leftOver -= (start - mCopyState->m_dataBuffer - 1);
+ // In CopyFileMessage, a complete message is being copied in a single
+ // call to CopyData, and if it does not have a LINEBREAK at the EOF,
+ // then end will be null after reading the last line, and we need
+ // to append the LINEBREAK to the buffer to enable transfer of the last line.
+ if (mCopyState->m_wholeMsgInStream)
+ {
+ end = start + mCopyState->m_leftOver;
+ memcpy(end, MSG_LINEBREAK "\0", MSG_LINEBREAK_LEN + 1);
+ }
+ else
+ {
+ memmove (mCopyState->m_dataBuffer + 1, start, mCopyState->m_leftOver);
+ break;
+ }
+ }
+
+ //need to set the linebreak_len each time
+ uint32_t linebreak_len = 1; //assume CR or LF
+ if (*end == '\r' && *(end+1) == '\n')
+ linebreak_len = 2; //CRLF
+
+ if (!mCopyState->m_fromLineSeen)
+ {
+ mCopyState->m_fromLineSeen = true;
+ NS_ASSERTION(strncmp(start, "From ", 5) == 0,
+ "Fatal ... bad message format\n");
+ }
+ else if (strncmp(start, "From ", 5) == 0)
+ {
+ //if we're at the beginning of the buffer, we've reserved a byte to
+ //insert a '>'. If we're in the middle, we're overwriting the previous
+ //line ending, but we've already written it to m_fileStream, so it's OK.
+ *--start = '>';
+ }
+
+ lineLength = end - start + linebreak_len;
+ rv = mCopyState->m_fileStream->Write(start, lineLength, &bytesWritten);
+ if (bytesWritten != lineLength || NS_FAILED(rv))
+ {
+ ThrowAlertMsg("copyMsgWriteFailed", mCopyState->m_msgWindow);
+ mCopyState->m_writeFailed = true;
+ return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
+ }
+
+ if (mCopyState->m_parseMsgState)
+ mCopyState->m_parseMsgState->ParseAFolderLine(start, lineLength);
+
+ start = end + linebreak_len;
+ if (start >= endBuffer)
+ {
+ mCopyState->m_leftOver = 0;
+ break;
+ }
+ }
+ return rv;
+}
+
+void nsMsgLocalMailFolder::CopyPropertiesToMsgHdr(nsIMsgDBHdr *destHdr,
+ nsIMsgDBHdr *srcHdr,
+ bool aIsMove)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ nsCString dontPreserve;
+
+ // These preferences exist so that extensions can control which properties
+ // are preserved in the database when a message is moved or copied. All
+ // properties are preserved except those listed in these preferences
+ if (aIsMove)
+ prefBranch->GetCharPref("mailnews.database.summary.dontPreserveOnMove",
+ getter_Copies(dontPreserve));
+ else
+ prefBranch->GetCharPref("mailnews.database.summary.dontPreserveOnCopy",
+ getter_Copies(dontPreserve));
+
+ CopyHdrPropertiesWithSkipList(destHdr, srcHdr, dontPreserve);
+}
+
+void
+nsMsgLocalMailFolder::CopyHdrPropertiesWithSkipList(nsIMsgDBHdr *destHdr,
+ nsIMsgDBHdr *srcHdr,
+ const nsCString &skipList)
+{
+ nsCOMPtr<nsIUTF8StringEnumerator> propertyEnumerator;
+ nsresult rv = srcHdr->GetPropertyEnumerator(getter_AddRefs(propertyEnumerator));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ // We'll add spaces at beginning and end so we can search for space-name-space
+ nsCString dontPreserveEx(NS_LITERAL_CSTRING(" "));
+ dontPreserveEx.Append(skipList);
+ dontPreserveEx.AppendLiteral(" ");
+
+ nsAutoCString property;
+ nsCString sourceString;
+ bool hasMore;
+ while (NS_SUCCEEDED(propertyEnumerator->HasMore(&hasMore)) && hasMore)
+ {
+ propertyEnumerator->GetNext(property);
+ nsAutoCString propertyEx(NS_LITERAL_CSTRING(" "));
+ propertyEx.Append(property);
+ propertyEx.AppendLiteral(" ");
+ if (dontPreserveEx.Find(propertyEx) != -1) // -1 is not found
+ continue;
+
+ srcHdr->GetStringProperty(property.get(), getter_Copies(sourceString));
+ destHdr->SetStringProperty(property.get(), sourceString.get());
+ }
+
+ nsMsgLabelValue label = 0;
+ srcHdr->GetLabel(&label);
+ destHdr->SetLabel(label);
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::EndCopy(bool aCopySucceeded)
+{
+ if (!mCopyState)
+ return NS_OK;
+
+ // we are the destination folder for a move/copy
+ nsresult rv = aCopySucceeded ? NS_OK : NS_ERROR_FAILURE;
+
+ if (!aCopySucceeded || mCopyState->m_writeFailed)
+ {
+ if (mCopyState->m_fileStream)
+ {
+ if (mCopyState->m_curDstKey != nsMsgKey_None)
+ mCopyState->m_msgStore->DiscardNewMessage(mCopyState->m_fileStream,
+ mCopyState->m_newHdr);
+ mCopyState->m_fileStream->Close();
+ }
+
+ if (!mCopyState->m_isMove)
+ {
+ // passing true because the messages that have been successfully
+ // copied have their corresponding hdrs in place. The message that has
+ // failed has been truncated so the msf file and berkeley mailbox
+ // are in sync.
+ (void) OnCopyCompleted(mCopyState->m_srcSupport, true);
+ // enable the dest folder
+ EnableNotifications(allMessageCountNotifications, true, false /*dbBatching*/); //dest folder doesn't need db batching
+ }
+ return NS_OK;
+ }
+
+ bool multipleCopiesFinished = (mCopyState->m_curCopyIndex >= mCopyState->m_totalMsgCount);
+
+ RefPtr<nsLocalMoveCopyMsgTxn> localUndoTxn = mCopyState->m_undoMsgTxn;
+
+ NS_ASSERTION(mCopyState->m_leftOver == 0, "whoops, something wrong with previous copy");
+ mCopyState->m_leftOver = 0; // reset to 0.
+ // need to reset this in case we're move/copying multiple msgs.
+ mCopyState->m_fromLineSeen = false;
+
+ // flush the copied message. We need a close at the end to get the
+ // file size and time updated correctly.
+ //
+ // These filestream closes are handled inconsistently in the code. In some
+ // cases, this is done in EndMessage, while in others it is done here in
+ // EndCopy. When we do the close in EndMessage, we'll set
+ // mCopyState->m_fileStream to null since it is no longer needed, and detect
+ // here the null stream so we know that we don't have to close it here.
+ //
+ // Similarly, m_parseMsgState->GetNewMsgHdr() returns a null hdr if the hdr
+ // has already been processed by EndMessage so it is not doubly added here.
+
+ nsCOMPtr <nsISeekableStream> seekableStream(do_QueryInterface(mCopyState->m_fileStream));
+ if (seekableStream)
+ {
+ if (mCopyState->m_dummyEnvelopeNeeded)
+ {
+ uint32_t bytesWritten;
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
+ mCopyState->m_fileStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &bytesWritten);
+ if (mCopyState->m_parseMsgState)
+ mCopyState->m_parseMsgState->ParseAFolderLine(CRLF, MSG_LINEBREAK_LEN);
+ }
+ rv = mCopyState->m_msgStore->FinishNewMessage(mCopyState->m_fileStream,
+ mCopyState->m_newHdr);
+ if (NS_SUCCEEDED(rv) && mCopyState->m_newHdr)
+ mCopyState->m_newHdr->GetMessageKey(&mCopyState->m_curDstKey);
+ if (multipleCopiesFinished)
+ mCopyState->m_fileStream->Close();
+ else
+ mCopyState->m_fileStream->Flush();
+ }
+ //Copy the header to the new database
+ if (mCopyState->m_message)
+ {
+ // CopyMessages() goes here, and CopyFileMessages() with metadata to save;
+ nsCOMPtr<nsIMsgDBHdr> newHdr;
+ if (!mCopyState->m_parseMsgState)
+ {
+ if (mCopyState->m_destDB)
+ {
+ if (mCopyState->m_newHdr)
+ {
+ newHdr = mCopyState->m_newHdr;
+ CopyHdrPropertiesWithSkipList(newHdr, mCopyState->m_message,
+ NS_LITERAL_CSTRING("storeToken msgOffset"));
+// UpdateNewMsgHdr(mCopyState->m_message, newHdr);
+ // We need to copy more than just what UpdateNewMsgHdr does. In fact,
+ // I think we want to copy almost every property other than
+ // storeToken and msgOffset.
+ mCopyState->m_destDB->AddNewHdrToDB(newHdr, true);
+ }
+ else
+ {
+ rv = mCopyState->m_destDB->CopyHdrFromExistingHdr(mCopyState->m_curDstKey,
+ mCopyState->m_message, true,
+ getter_AddRefs(newHdr));
+ }
+ uint32_t newHdrFlags;
+ if (newHdr)
+ {
+ // turn off offline flag - it's not valid for local mail folders.
+ newHdr->AndFlags(~nsMsgMessageFlags::Offline, &newHdrFlags);
+ mCopyState->m_destMessages->AppendElement(newHdr, false);
+ }
+ }
+ // we can do undo with the dest folder db, see bug #198909
+ //else
+ // mCopyState->m_undoMsgTxn = nullptr; //null out the transaction because we can't undo w/o the msg db
+ }
+
+ // if we plan on allowing undo, (if we have a mCopyState->m_parseMsgState or not)
+ // we need to save the source and dest keys on the undo txn.
+ // see bug #179856 for details
+ bool isImap;
+ if (NS_SUCCEEDED(rv) && localUndoTxn) {
+ localUndoTxn->GetSrcIsImap(&isImap);
+ if (!isImap || !mCopyState->m_copyingMultipleMessages)
+ {
+ nsMsgKey aKey;
+ uint32_t statusOffset;
+ mCopyState->m_message->GetMessageKey(&aKey);
+ mCopyState->m_message->GetStatusOffset(&statusOffset);
+ localUndoTxn->AddSrcKey(aKey);
+ localUndoTxn->AddSrcStatusOffset(statusOffset);
+ localUndoTxn->AddDstKey(mCopyState->m_curDstKey);
+ }
+ }
+ }
+ nsCOMPtr<nsIMsgDBHdr> newHdr;
+ // CopyFileMessage() and CopyMessages() from servers other than mailbox
+ if (mCopyState->m_parseMsgState)
+ {
+ nsCOMPtr<nsIMsgDatabase> msgDb;
+ mCopyState->m_parseMsgState->FinishHeader();
+ GetDatabaseWOReparse(getter_AddRefs(msgDb));
+ if (msgDb)
+ {
+ nsresult result = mCopyState->m_parseMsgState->GetNewMsgHdr(getter_AddRefs(newHdr));
+ // we need to copy newHdr because mCopyState will get cleared
+ // in OnCopyCompleted, but we need OnCopyCompleted to know about
+ // the newHdr, via mCopyState. And we send a notification about newHdr
+ // after OnCopyCompleted.
+ mCopyState->m_newHdr = newHdr;
+ if (NS_SUCCEEDED(result) && newHdr)
+ {
+ // Copy message metadata.
+ if (mCopyState->m_message)
+ {
+ // Propagate the new flag on an imap to local folder filter action
+ // Flags may get changed when deleting the original source message in
+ // IMAP. We have a copy of the original flags, but parseMsgState has
+ // already tried to decide what those flags should be. Who to believe?
+ // Let's only deal here with the flags that might get changed, Read
+ // and New, and trust upstream code for everything else.
+ uint32_t readAndNew = nsMsgMessageFlags::New | nsMsgMessageFlags::Read;
+ uint32_t newFlags;
+ newHdr->GetFlags(&newFlags);
+ newHdr->SetFlags( (newFlags & ~readAndNew) |
+ ((mCopyState->m_flags) & readAndNew));
+
+ // Copy other message properties.
+ CopyPropertiesToMsgHdr(newHdr, mCopyState->m_message, mCopyState->m_isMove);
+ }
+ msgDb->AddNewHdrToDB(newHdr, true);
+ if (localUndoTxn)
+ {
+ // ** jt - recording the message size for possible undo use; the
+ // message size is different for pop3 and imap4 messages
+ uint32_t msgSize;
+ newHdr->GetMessageSize(&msgSize);
+ localUndoTxn->AddDstMsgSize(msgSize);
+ }
+
+ mCopyState->m_destMessages->AppendElement(newHdr, false);
+ }
+ // msgDb->SetSummaryValid(true);
+ // msgDb->Commit(nsMsgDBCommitType::kLargeCommit);
+ }
+ else
+ mCopyState->m_undoMsgTxn = nullptr; //null out the transaction because we can't undo w/o the msg db
+
+ mCopyState->m_parseMsgState->Clear();
+ if (mCopyState->m_listener) // CopyFileMessage() only
+ mCopyState->m_listener->SetMessageKey(mCopyState->m_curDstKey);
+ }
+
+ if (!multipleCopiesFinished && !mCopyState->m_copyingMultipleMessages)
+ {
+ // CopyMessages() goes here; CopyFileMessage() never gets in here because
+ // curCopyIndex will always be less than the mCopyState->m_totalMsgCount
+ nsCOMPtr<nsISupports> aSupport = do_QueryElementAt(mCopyState->m_messages, mCopyState->m_curCopyIndex);
+ rv = CopyMessageTo(aSupport, this, mCopyState->m_msgWindow, mCopyState->m_isMove);
+ }
+ else
+ {
+ // If we have some headers, then there is a source, so notify itemMoveCopyCompleted.
+ // If we don't have any headers already, (eg save as draft, send) then notify itemAdded.
+ // This notification is done after the messages are deleted, so that saving a new draft
+ // of a message works correctly -- first an itemDeleted is sent for the old draft, then
+ // an itemAdded for the new draft.
+ uint32_t numHdrs;
+ mCopyState->m_messages->GetLength(&numHdrs);
+
+ if (multipleCopiesFinished && numHdrs && !mCopyState->m_isFolder)
+ {
+ // we need to send this notification before we delete the source messages,
+ // because deleting the source messages clears out the src msg db hdr.
+ nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ if (notifier)
+ notifier->NotifyMsgsMoveCopyCompleted(mCopyState->m_isMove,
+ mCopyState->m_messages,
+ this, mCopyState->m_destMessages);
+ }
+
+ if (!mCopyState->m_isMove)
+ {
+ if (multipleCopiesFinished)
+ {
+ nsCOMPtr<nsIMsgFolder> srcFolder;
+ srcFolder = do_QueryInterface(mCopyState->m_srcSupport);
+ if (mCopyState->m_isFolder)
+ CopyAllSubFolders(srcFolder, nullptr, nullptr); //Copy all subfolders then notify completion
+
+ if (mCopyState->m_msgWindow && mCopyState->m_undoMsgTxn)
+ {
+ nsCOMPtr<nsITransactionManager> txnMgr;
+ mCopyState->m_msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
+ if (txnMgr)
+ txnMgr->DoTransaction(mCopyState->m_undoMsgTxn);
+ }
+
+ // enable the dest folder
+ EnableNotifications(allMessageCountNotifications, true, false /*dbBatching*/); //dest folder doesn't need db batching
+ if (srcFolder && !mCopyState->m_isFolder)
+ {
+ // I'm not too sure of the proper location of this event. It seems to need to be
+ // after the EnableNotifications, or the folder counts can be incorrect
+ // during the mDeleteOrMoveMsgCompletedAtom call.
+ srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom);
+ }
+ (void) OnCopyCompleted(mCopyState->m_srcSupport, true);
+ }
+ }
+ // Send the itemAdded notification in case we didn't send the itemMoveCopyCompleted notification earlier.
+ // Posting news messages involves this, yet doesn't have the newHdr initialized, so don't send any
+ // notifications in that case.
+ if (!numHdrs && newHdr)
+ {
+ nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ if (notifier)
+ {
+ notifier->NotifyMsgAdded(newHdr);
+ // We do not appear to trigger classification in this case, so let's
+ // paper over the abyss by just sending the classification notification.
+ nsCOMPtr <nsIMutableArray> oneHeaderArray =
+ do_CreateInstance(NS_ARRAY_CONTRACTID);
+ oneHeaderArray->AppendElement(newHdr, false);
+ notifier->NotifyMsgsClassified(oneHeaderArray, false, false);
+ // (We do not add the NotReportedClassified processing flag since we
+ // just reported it!)
+ }
+ }
+ }
+ return rv;
+}
+
+static bool gGotGlobalPrefs;
+static bool gDeleteFromServerOnMove;
+
+bool nsMsgLocalMailFolder::GetDeleteFromServerOnMove()
+{
+ if (!gGotGlobalPrefs)
+ {
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (pPrefBranch)
+ {
+ pPrefBranch->GetBoolPref("mail.pop3.deleteFromServerOnMove", &gDeleteFromServerOnMove);
+ gGotGlobalPrefs = true;
+ }
+ }
+ return gDeleteFromServerOnMove;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::EndMove(bool moveSucceeded)
+{
+ nsresult rv;
+ if (!mCopyState)
+ return NS_OK;
+
+ if (!moveSucceeded || mCopyState->m_writeFailed)
+ {
+ //Notify that a completion finished.
+ nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryInterface(mCopyState->m_srcSupport, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ srcFolder->NotifyFolderEvent(mDeleteOrMoveMsgFailedAtom);
+
+ /* passing true because the messages that have been successfully copied have their corressponding
+ hdrs in place. The message that has failed has been truncated so the msf file and berkeley mailbox
+ are in sync*/
+
+ (void) OnCopyCompleted(mCopyState->m_srcSupport, true);
+ // enable the dest folder
+ EnableNotifications(allMessageCountNotifications, true, false /*dbBatching*/ ); //dest folder doesn't need db batching
+ return NS_OK;
+ }
+
+ if (mCopyState && mCopyState->m_curCopyIndex >= mCopyState->m_totalMsgCount)
+ {
+ //Notify that a completion finished.
+ nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryInterface(mCopyState->m_srcSupport, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIMsgLocalMailFolder> localSrcFolder = do_QueryInterface(srcFolder);
+ if (localSrcFolder)
+ {
+ // if we are the trash and a local msg is being moved to us, mark the source
+ // for delete from server, if so configured.
+ if (mFlags & nsMsgFolderFlags::Trash)
+ {
+ // if we're deleting on all moves, we'll mark this message for deletion when
+ // we call DeleteMessages on the source folder. So don't mark it for deletion
+ // here, in that case.
+ if (!GetDeleteFromServerOnMove())
+ localSrcFolder->MarkMsgsOnPop3Server(mCopyState->m_messages, POP3_DELETE);
+ }
+ }
+ // lets delete these all at once - much faster that way
+ rv = srcFolder->DeleteMessages(mCopyState->m_messages, mCopyState->m_msgWindow, true, true, nullptr, mCopyState->m_allowUndo);
+ AutoCompact(mCopyState->m_msgWindow);
+
+ // enable the dest folder
+ EnableNotifications(allMessageCountNotifications, true, false /*dbBatching*/); //dest folder doesn't need db batching
+ // I'm not too sure of the proper location of this event. It seems to need to be
+ // after the EnableNotifications, or the folder counts can be incorrect
+ // during the mDeleteOrMoveMsgCompletedAtom call.
+ srcFolder->NotifyFolderEvent(NS_SUCCEEDED(rv) ? mDeleteOrMoveMsgCompletedAtom : mDeleteOrMoveMsgFailedAtom);
+
+ if (NS_SUCCEEDED(rv) && mCopyState->m_msgWindow && mCopyState->m_undoMsgTxn)
+ {
+ nsCOMPtr<nsITransactionManager> txnMgr;
+ mCopyState->m_msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
+ if (txnMgr)
+ txnMgr->DoTransaction(mCopyState->m_undoMsgTxn);
+ }
+ (void) OnCopyCompleted(mCopyState->m_srcSupport, NS_SUCCEEDED(rv) ? true : false); //clear the copy state so that the next message from a different folder can be move
+ }
+
+ return NS_OK;
+
+}
+
+// this is the beginning of the next message copied
+NS_IMETHODIMP nsMsgLocalMailFolder::StartMessage()
+{
+ // We get crashes that we don't understand (bug 284876), so stupidly prevent that.
+ NS_ENSURE_ARG_POINTER(mCopyState);
+ nsresult rv = InitCopyMsgHdrAndFileStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+ return WriteStartOfNewMessage();
+}
+
+// just finished the current message.
+NS_IMETHODIMP nsMsgLocalMailFolder::EndMessage(nsMsgKey key)
+{
+ NS_ENSURE_ARG_POINTER(mCopyState);
+
+ RefPtr<nsLocalMoveCopyMsgTxn> localUndoTxn = mCopyState->m_undoMsgTxn;
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ nsresult rv;
+
+ if (localUndoTxn)
+ {
+ localUndoTxn->GetMsgWindow(getter_AddRefs(msgWindow));
+ localUndoTxn->AddSrcKey(key);
+ localUndoTxn->AddDstKey(mCopyState->m_curDstKey);
+ }
+
+ // I think this is always true for online to offline copy
+ mCopyState->m_dummyEnvelopeNeeded = true;
+ nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(mCopyState->m_fileStream, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
+ uint32_t bytesWritten;
+ mCopyState->m_fileStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &bytesWritten);
+ if (mCopyState->m_parseMsgState)
+ mCopyState->m_parseMsgState->ParseAFolderLine(CRLF, MSG_LINEBREAK_LEN);
+
+ rv = mCopyState->m_msgStore->FinishNewMessage(mCopyState->m_fileStream,
+ mCopyState->m_newHdr);
+ mCopyState->m_fileStream->Close();
+ mCopyState->m_fileStream = nullptr; // all done with the file stream
+
+ // CopyFileMessage() and CopyMessages() from servers other than mailbox
+ if (mCopyState->m_parseMsgState)
+ {
+ nsCOMPtr<nsIMsgDatabase> msgDb;
+ nsCOMPtr<nsIMsgDBHdr> newHdr;
+
+ mCopyState->m_parseMsgState->FinishHeader();
+
+ rv = mCopyState->m_parseMsgState->GetNewMsgHdr(getter_AddRefs(newHdr));
+ if (NS_SUCCEEDED(rv) && newHdr)
+ {
+ nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryInterface(mCopyState->m_srcSupport, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgDatabase> srcDB;
+ srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
+ if (srcDB)
+ {
+ nsCOMPtr <nsIMsgDBHdr> srcMsgHdr;
+ srcDB->GetMsgHdrForKey(key, getter_AddRefs(srcMsgHdr));
+ if (srcMsgHdr)
+ CopyPropertiesToMsgHdr(newHdr, srcMsgHdr, mCopyState->m_isMove);
+ }
+ rv = GetDatabaseWOReparse(getter_AddRefs(msgDb));
+ if (NS_SUCCEEDED(rv) && msgDb)
+ {
+ msgDb->AddNewHdrToDB(newHdr, true);
+ if (localUndoTxn)
+ {
+ // ** jt - recording the message size for possible undo use; the
+ // message size is different for pop3 and imap4 messages
+ uint32_t msgSize;
+ newHdr->GetMessageSize(&msgSize);
+ localUndoTxn->AddDstMsgSize(msgSize);
+ }
+ }
+ else
+ mCopyState->m_undoMsgTxn = nullptr; //null out the transaction because we can't undo w/o the msg db
+ }
+ mCopyState->m_parseMsgState->Clear();
+
+ if (mCopyState->m_listener) // CopyFileMessage() only
+ mCopyState->m_listener->SetMessageKey(mCopyState->m_curDstKey);
+ }
+
+ if (mCopyState->m_fileStream)
+ mCopyState->m_fileStream->Flush();
+ return NS_OK;
+}
+
+
+nsresult nsMsgLocalMailFolder::CopyMessagesTo(nsIArray *messages, nsTArray<nsMsgKey> &keyArray,
+ nsIMsgWindow *aMsgWindow, nsIMsgFolder *dstFolder,
+ bool isMove)
+{
+ if (!mCopyState)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv;
+
+ nsCOMPtr<nsICopyMessageStreamListener> copyStreamListener = do_CreateInstance(NS_COPYMESSAGESTREAMLISTENER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsICopyMessageListener> copyListener(do_QueryInterface(dstFolder, &rv));
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
+
+ nsCOMPtr<nsIMsgFolder> srcFolder(do_QueryInterface(mCopyState->m_srcSupport, &rv));
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
+
+ rv = copyStreamListener->Init(srcFolder, copyListener, nullptr);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!mCopyState->m_messageService)
+ {
+ nsCString uri;
+ srcFolder->GetURI(uri);
+ rv = GetMessageServiceFromURI(uri, getter_AddRefs(mCopyState->m_messageService));
+ }
+
+ if (NS_SUCCEEDED(rv) && mCopyState->m_messageService)
+ {
+ nsCOMPtr<nsIStreamListener> streamListener(do_QueryInterface(copyStreamListener, &rv));
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
+
+ mCopyState->m_curCopyIndex = 0;
+ // we need to kick off the first message - subsequent messages
+ // are kicked off by nsMailboxProtocol when it finishes a message
+ // before starting the next message. Only do this if the source folder
+ // is a local folder, however. IMAP will handle calling StartMessage for
+ // each message that gets downloaded, and news doesn't go through here
+ // because news only downloads one message at a time, and this routine
+ // is for multiple message copy.
+ nsCOMPtr <nsIMsgLocalMailFolder> srcLocalFolder = do_QueryInterface(srcFolder);
+ if (srcLocalFolder)
+ StartMessage();
+ nsCOMPtr<nsIURI> dummyNull;
+ rv = mCopyState->m_messageService->CopyMessages(keyArray.Length(),
+ keyArray.Elements(),
+ srcFolder, streamListener,
+ isMove, nullptr, aMsgWindow,
+ getter_AddRefs(dummyNull));
+ }
+ return rv;
+}
+
+nsresult nsMsgLocalMailFolder::CopyMessageTo(nsISupports *message,
+ nsIMsgFolder *dstFolder /* dst same as "this" */,
+ nsIMsgWindow *aMsgWindow,
+ bool isMove)
+{
+ if (!mCopyState)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgDBHdr> msgHdr(do_QueryInterface(message, &rv));
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
+
+ mCopyState->m_message = do_QueryInterface(msgHdr, &rv);
+
+ nsCOMPtr<nsIMsgFolder> srcFolder(do_QueryInterface(mCopyState->m_srcSupport, &rv));
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
+ nsCString uri;
+ srcFolder->GetUriForMsg(msgHdr, uri);
+
+ nsCOMPtr<nsICopyMessageStreamListener> copyStreamListener = do_CreateInstance(NS_COPYMESSAGESTREAMLISTENER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsICopyMessageListener> copyListener(do_QueryInterface(dstFolder, &rv));
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
+
+ rv = copyStreamListener->Init(srcFolder, copyListener, nullptr);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!mCopyState->m_messageService)
+ rv = GetMessageServiceFromURI(uri, getter_AddRefs(mCopyState->m_messageService));
+
+ if (NS_SUCCEEDED(rv) && mCopyState->m_messageService)
+ {
+ nsCOMPtr<nsIStreamListener> streamListener(do_QueryInterface(copyStreamListener, &rv));
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
+ nsCOMPtr<nsIURI> dummyNull;
+ rv = mCopyState->m_messageService->CopyMessage(uri.get(), streamListener, isMove, nullptr, aMsgWindow,
+ getter_AddRefs(dummyNull));
+ }
+ return rv;
+}
+
+// A message is being deleted from a POP3 mail file, so check and see if we have the message
+// being deleted in the server. If so, then we need to remove the message from the server as well.
+// We have saved the UIDL of the message in the popstate.dat file and we must match this uidl, so
+// read the message headers and see if we have it, then mark the message for deletion from the server.
+// The next time we look at mail the message will be deleted from the server.
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::MarkMsgsOnPop3Server(nsIArray *aMessages, int32_t aMark)
+{
+ nsLocalFolderScanState folderScanState;
+ nsCOMPtr<nsIPop3IncomingServer> curFolderPop3MailServer;
+ nsCOMArray<nsIPop3IncomingServer> pop3Servers; // servers with msgs deleted...
+
+ nsCOMPtr<nsIMsgIncomingServer> incomingServer;
+ nsresult rv = GetServer(getter_AddRefs(incomingServer));
+ NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
+
+ nsCOMPtr <nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // I wonder if we should run through the pop3 accounts and see if any of them have
+ // leave on server set. If not, we could short-circuit some of this.
+
+ curFolderPop3MailServer = do_QueryInterface(incomingServer, &rv);
+ rv = GetFolderScanState(&folderScanState);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ uint32_t srcCount;
+ aMessages->GetLength(&srcCount);
+
+ // Filter delete requests are always honored, others are subject
+ // to the deleteMailLeftOnServer preference.
+ int32_t mark;
+ mark = (aMark == POP3_FORCE_DEL) ? POP3_DELETE : aMark;
+
+ for (uint32_t i = 0; i < srcCount; i++)
+ {
+ /* get uidl for this message */
+ nsCOMPtr<nsIMsgDBHdr> msgDBHdr (do_QueryElementAt(aMessages, i, &rv));
+
+ uint32_t flags = 0;
+
+ if (msgDBHdr)
+ {
+ msgDBHdr->GetFlags(&flags);
+ nsCOMPtr <nsIPop3IncomingServer> msgPop3Server = curFolderPop3MailServer;
+ bool leaveOnServer = false;
+ bool deleteMailLeftOnServer = false;
+ // set up defaults, in case there's no x-mozilla-account header
+ if (curFolderPop3MailServer)
+ {
+ curFolderPop3MailServer->GetDeleteMailLeftOnServer(&deleteMailLeftOnServer);
+ curFolderPop3MailServer->GetLeaveMessagesOnServer(&leaveOnServer);
+ }
+
+ rv = GetUidlFromFolder(&folderScanState, msgDBHdr);
+ if (!NS_SUCCEEDED(rv))
+ continue;
+
+ if (folderScanState.m_uidl)
+ {
+ nsCOMPtr <nsIMsgAccount> account;
+ rv = accountManager->GetAccount(folderScanState.m_accountKey, getter_AddRefs(account));
+ if (NS_SUCCEEDED(rv) && account)
+ {
+ account->GetIncomingServer(getter_AddRefs(incomingServer));
+ nsCOMPtr<nsIPop3IncomingServer> curMsgPop3MailServer = do_QueryInterface(incomingServer);
+ if (curMsgPop3MailServer)
+ {
+ msgPop3Server = curMsgPop3MailServer;
+ msgPop3Server->GetDeleteMailLeftOnServer(&deleteMailLeftOnServer);
+ msgPop3Server->GetLeaveMessagesOnServer(&leaveOnServer);
+ }
+ }
+ }
+ // ignore this header if not partial and leaveOnServer not set...
+ // or if we can't find the pop3 server.
+ if (!msgPop3Server || (! (flags & nsMsgMessageFlags::Partial) && !leaveOnServer))
+ continue;
+ // if marking deleted, ignore header if we're not deleting from
+ // server when deleting locally.
+ if (aMark == POP3_DELETE && leaveOnServer && !deleteMailLeftOnServer)
+ continue;
+ if (folderScanState.m_uidl)
+ {
+ msgPop3Server->AddUidlToMark(folderScanState.m_uidl, mark);
+ // remember this pop server in list of servers with msgs deleted
+ if (pop3Servers.IndexOfObject(msgPop3Server) == -1)
+ pop3Servers.AppendObject(msgPop3Server);
+ }
+ }
+ }
+ if (folderScanState.m_inputStream)
+ folderScanState.m_inputStream->Close();
+ // need to do this for all pop3 mail servers that had messages deleted.
+ uint32_t serverCount = pop3Servers.Count();
+ for (uint32_t index = 0; index < serverCount; index++)
+ pop3Servers[index]->MarkMessages();
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::DeleteDownloadMsg(nsIMsgDBHdr *aMsgHdr, bool *aDoSelect)
+{
+ uint32_t numMsgs;
+ char *newMsgId;
+
+ // This method is only invoked thru DownloadMessagesForOffline()
+ if (mDownloadState != DOWNLOAD_STATE_NONE)
+ {
+ // We only remember the first key, no matter how many
+ // messages were originally selected.
+ if (mDownloadState == DOWNLOAD_STATE_INITED)
+ {
+ aMsgHdr->GetMessageKey(&mDownloadSelectKey);
+ mDownloadState = DOWNLOAD_STATE_GOTMSG;
+ }
+
+ aMsgHdr->GetMessageId(&newMsgId);
+
+ // Walk through all the selected headers, looking for a matching
+ // Message-ID.
+ numMsgs = mDownloadMessages.Length();
+ for (uint32_t i = 0; i < numMsgs; i++)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIMsgDBHdr> msgDBHdr = mDownloadMessages[i];
+ char *oldMsgId = nullptr;
+ msgDBHdr->GetMessageId(&oldMsgId);
+
+ // Delete the first match and remove it from the array
+ if (!PL_strcmp(newMsgId, oldMsgId))
+ {
+ rv = GetDatabase();
+ if (!mDatabase)
+ return rv;
+
+ UpdateNewMsgHdr(msgDBHdr, aMsgHdr);
+
+#if DOWNLOAD_NOTIFY_STYLE == DOWNLOAD_NOTIFY_LAST
+ msgDBHdr->GetMessageKey(&mDownloadOldKey);
+ msgDBHdr->GetThreadParent(&mDownloadOldParent);
+ msgDBHdr->GetFlags(&mDownloadOldFlags);
+ mDatabase->DeleteHeader(msgDBHdr, nullptr, false, false);
+ // Tell caller we want to select this message
+ if (aDoSelect)
+ *aDoSelect = true;
+#else
+ mDatabase->DeleteHeader(msgDBHdr, nullptr, false, true);
+ // Tell caller we want to select this message
+ if (aDoSelect && mDownloadState == DOWNLOAD_STATE_GOTMSG)
+ *aDoSelect = true;
+#endif
+ mDownloadMessages.RemoveElementAt(i);
+ break;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::SelectDownloadMsg()
+{
+#if DOWNLOAD_NOTIFY_STYLE == DOWNLOAD_NOTIFY_LAST
+ if (mDownloadState >= DOWNLOAD_STATE_GOTMSG)
+ {
+ nsresult rv = GetDatabase();
+ if (!mDatabase)
+ return rv;
+ }
+ mDatabase->NotifyKeyDeletedAll(mDownloadOldKey, mDownloadOldParent, mDownloadOldFlags, nullptr);
+ }
+#endif
+
+ if (mDownloadState == DOWNLOAD_STATE_GOTMSG && mDownloadWindow)
+ {
+ nsAutoCString newuri;
+ nsBuildLocalMessageURI(mBaseMessageURI.get(), mDownloadSelectKey, newuri);
+ nsCOMPtr<nsIMsgWindowCommands> windowCommands;
+ mDownloadWindow->GetWindowCommands(getter_AddRefs(windowCommands));
+ if (windowCommands)
+ windowCommands->SelectMessage(newuri);
+ mDownloadState = DOWNLOAD_STATE_DIDSEL;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::DownloadMessagesForOffline(nsIArray *aMessages, nsIMsgWindow *aWindow)
+{
+ if (mDownloadState != DOWNLOAD_STATE_NONE)
+ return NS_ERROR_FAILURE; // already has a download in progress
+
+ // We're starting a download...
+ mDownloadState = DOWNLOAD_STATE_INITED;
+
+ MarkMsgsOnPop3Server(aMessages, POP3_FETCH_BODY);
+
+ // Pull out all the PARTIAL messages into a new array
+ uint32_t srcCount;
+ aMessages->GetLength(&srcCount);
+
+ nsresult rv;
+ for (uint32_t i = 0; i < srcCount; i++)
+ {
+ nsCOMPtr<nsIMsgDBHdr> msgDBHdr (do_QueryElementAt(aMessages, i, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ uint32_t flags = 0;
+ msgDBHdr->GetFlags(&flags);
+ if (flags & nsMsgMessageFlags::Partial)
+ mDownloadMessages.AppendElement(msgDBHdr);
+ }
+ }
+ mDownloadWindow = aWindow;
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
+
+ nsCOMPtr<nsILocalMailIncomingServer> localMailServer = do_QueryInterface(server, &rv);
+ NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
+ return localMailServer->GetNewMail(aWindow, this, this, nullptr);
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::NotifyDelete()
+{
+ NotifyFolderEvent(mDeleteOrMoveMsgCompletedAtom);
+ return NS_OK;
+}
+
+// TODO: once we move certain code into the IncomingServer (search for TODO)
+// this method will go away.
+// sometimes this gets called when we don't have the server yet, so
+// that's why we're not calling GetServer()
+NS_IMETHODIMP
+nsMsgLocalMailFolder::GetIncomingServerType(nsACString& aServerType)
+{
+ nsresult rv;
+ if (mType.IsEmpty())
+ {
+ nsCOMPtr<nsIURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = url->SetSpec(mURI);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ // try "none" first
+ url->SetScheme(NS_LITERAL_CSTRING("none"));
+ rv = accountManager->FindServerByURI(url, false, getter_AddRefs(server));
+ if (NS_SUCCEEDED(rv) && server)
+ mType.AssignLiteral("none");
+ else
+ {
+ // next try "pop3"
+ url->SetScheme(NS_LITERAL_CSTRING("pop3"));
+ rv = accountManager->FindServerByURI(url, false, getter_AddRefs(server));
+ if (NS_SUCCEEDED(rv) && server)
+ mType.AssignLiteral("pop3");
+ else
+ {
+ // next try "rss"
+ url->SetScheme(NS_LITERAL_CSTRING("rss"));
+ rv = accountManager->FindServerByURI(url, false, getter_AddRefs(server));
+ if (NS_SUCCEEDED(rv) && server)
+ mType.AssignLiteral("rss");
+ else
+ {
+#ifdef HAVE_MOVEMAIL
+ // next try "movemail"
+ url->SetScheme(NS_LITERAL_CSTRING("movemail"));
+ rv = accountManager->FindServerByURI(url, false, getter_AddRefs(server));
+ if (NS_SUCCEEDED(rv) && server)
+ mType.AssignLiteral("movemail");
+#endif /* HAVE_MOVEMAIL */
+ }
+ }
+ }
+ }
+ aServerType = mType;
+ return NS_OK;
+}
+
+nsresult nsMsgLocalMailFolder::CreateBaseMessageURI(const nsACString& aURI)
+{
+ return nsCreateLocalBaseMessageURI(aURI, mBaseMessageURI);
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::OnStartRunningUrl(nsIURI * aUrl)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPop3URL> popurl = do_QueryInterface(aUrl, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString aSpec;
+ rv = aUrl->GetSpec(aSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (strstr(aSpec.get(), "uidl="))
+ {
+ nsCOMPtr<nsIPop3Sink> popsink;
+ rv = popurl->GetPop3Sink(getter_AddRefs(popsink));
+ if (NS_SUCCEEDED(rv))
+ {
+ popsink->SetBaseMessageUri(mBaseMessageURI.get());
+ nsCString messageuri;
+ popurl->GetMessageUri(getter_Copies(messageuri));
+ popsink->SetOrigMessageUri(messageuri);
+ }
+ }
+ }
+ return nsMsgDBFolder::OnStartRunningUrl(aUrl);
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::OnStopRunningUrl(nsIURI * aUrl, nsresult aExitCode)
+{
+ // If we just finished a DownloadMessages call, reset...
+ if (mDownloadState != DOWNLOAD_STATE_NONE)
+ {
+ mDownloadState = DOWNLOAD_STATE_NONE;
+ mDownloadMessages.Clear();
+ mDownloadWindow = nullptr;
+ return nsMsgDBFolder::OnStopRunningUrl(aUrl, aExitCode);
+ }
+
+ nsresult rv;
+ if (NS_SUCCEEDED(aExitCode))
+ {
+ nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
+ nsAutoCString aSpec;
+ if (aUrl) {
+ rv = aUrl->GetSpec(aSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ if (strstr(aSpec.get(), "uidl="))
+ {
+ nsCOMPtr<nsIPop3URL> popurl = do_QueryInterface(aUrl, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCString messageuri;
+ rv = popurl->GetMessageUri(getter_Copies(messageuri));
+ if (NS_SUCCEEDED(rv))
+ {
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIMsgDBHdr> msgDBHdr;
+ rv = GetMsgDBHdrFromURI(messageuri.get(), getter_AddRefs(msgDBHdr));
+ if (NS_SUCCEEDED(rv))
+ {
+ GetDatabase();
+ if (mDatabase)
+ mDatabase->DeleteHeader(msgDBHdr, nullptr, true, true);
+ }
+
+ nsCOMPtr<nsIPop3Sink> pop3sink;
+ nsCString newMessageUri;
+ rv = popurl->GetPop3Sink(getter_AddRefs(pop3sink));
+ if (NS_SUCCEEDED(rv))
+ {
+ pop3sink->GetMessageUri(getter_Copies(newMessageUri));
+ if (msgWindow)
+ {
+ nsCOMPtr<nsIMsgWindowCommands> windowCommands;
+ msgWindow->GetWindowCommands(getter_AddRefs(windowCommands));
+ if (windowCommands)
+ windowCommands->SelectMessage(newMessageUri);
+ }
+ }
+ }
+ }
+ }
+
+ if (mFlags & nsMsgFolderFlags::Inbox)
+ {
+ if (mDatabase && mCheckForNewMessagesAfterParsing)
+ {
+ bool valid = false; // GetSummaryValid may return without setting valid.
+ mDatabase->GetSummaryValid(&valid);
+ if (valid && msgWindow)
+ rv = GetNewMessages(msgWindow, nullptr);
+ mCheckForNewMessagesAfterParsing = false;
+ }
+ }
+ }
+
+ if (m_parsingFolder)
+ {
+ // Clear this before calling OnStopRunningUrl, in case the url listener
+ // tries to get the database.
+ m_parsingFolder = false;
+
+ // TODO: Updating the size should be pushed down into the msg store backend
+ // so that the size is recalculated as part of parsing the folder data
+ // (important for maildir), once GetSizeOnDisk is pushed into the msgStores
+ // (bug 1032360).
+ (void)RefreshSizeOnDisk();
+
+ // Update the summary totals so the front end will
+ // show the right thing.
+ UpdateSummaryTotals(true);
+
+ if (mReparseListener)
+ {
+ nsCOMPtr<nsIUrlListener> saveReparseListener = mReparseListener;
+ mReparseListener = nullptr;
+ saveReparseListener->OnStopRunningUrl(aUrl, aExitCode);
+ }
+ }
+ if (mFlags & nsMsgFolderFlags::Inbox)
+ {
+ // if we are the inbox and running pop url
+ nsCOMPtr<nsIPop3URL> popurl = do_QueryInterface(aUrl, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ GetServer(getter_AddRefs(server));
+ // this is the deferred to account, in the global inbox case
+ if (server)
+ server->SetPerformingBiff(false); //biff is over
+ }
+ }
+ return nsMsgDBFolder::OnStopRunningUrl(aUrl, aExitCode);
+}
+
+nsresult nsMsgLocalMailFolder::DisplayMoveCopyStatusMsg()
+{
+ nsresult rv = NS_OK;
+ if (mCopyState)
+ {
+ if (!mCopyState->m_statusFeedback)
+ {
+ // get msgWindow from undo txn
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ if (mCopyState->m_undoMsgTxn)
+ mCopyState->m_undoMsgTxn->GetMsgWindow(getter_AddRefs(msgWindow));
+ if (!msgWindow)
+ return NS_OK; // not a fatal error.
+
+ msgWindow->GetStatusFeedback(getter_AddRefs(mCopyState->m_statusFeedback));
+ }
+
+ if (!mCopyState->m_stringBundle)
+ {
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ rv = bundleService->CreateBundle("chrome://messenger/locale/localMsgs.properties", getter_AddRefs(mCopyState->m_stringBundle));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ if (mCopyState->m_statusFeedback && mCopyState->m_stringBundle)
+ {
+ nsString folderName;
+ GetName(folderName);
+ nsAutoString numMsgSoFarString;
+ numMsgSoFarString.AppendInt((mCopyState->m_copyingMultipleMessages) ? mCopyState->m_curCopyIndex : 1);
+
+ nsAutoString totalMessagesString;
+ totalMessagesString.AppendInt(mCopyState->m_totalMsgCount);
+ nsString finalString;
+ const char16_t * stringArray[] = { numMsgSoFarString.get(), totalMessagesString.get(), folderName.get() };
+ rv = mCopyState->m_stringBundle->FormatStringFromName(
+ (mCopyState->m_isMove) ?
+ u"movingMessagesStatus" :
+ u"copyingMessagesStatus",
+ stringArray, 3, getter_Copies(finalString));
+ int64_t nowMS = PR_IntervalToMilliseconds(PR_IntervalNow());
+
+ // only update status/progress every half second
+ if (nowMS - mCopyState->m_lastProgressTime < 500 &&
+ mCopyState->m_curCopyIndex < mCopyState->m_totalMsgCount)
+ return NS_OK;
+
+ mCopyState->m_lastProgressTime = nowMS;
+ mCopyState->m_statusFeedback->ShowStatusString(finalString);
+ mCopyState->m_statusFeedback->ShowProgress(mCopyState->m_curCopyIndex * 100 / mCopyState->m_totalMsgCount);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::SetFlagsOnDefaultMailboxes(uint32_t flags)
+{
+ if (flags & nsMsgFolderFlags::Inbox)
+ setSubfolderFlag(NS_LITERAL_STRING("Inbox"), nsMsgFolderFlags::Inbox);
+
+ if (flags & nsMsgFolderFlags::SentMail)
+ setSubfolderFlag(NS_LITERAL_STRING("Sent"), nsMsgFolderFlags::SentMail);
+
+ if (flags & nsMsgFolderFlags::Drafts)
+ setSubfolderFlag(NS_LITERAL_STRING("Drafts"), nsMsgFolderFlags::Drafts);
+
+ if (flags & nsMsgFolderFlags::Templates)
+ setSubfolderFlag(NS_LITERAL_STRING("Templates"), nsMsgFolderFlags::Templates);
+
+ if (flags & nsMsgFolderFlags::Trash)
+ setSubfolderFlag(NS_LITERAL_STRING("Trash"), nsMsgFolderFlags::Trash);
+
+ if (flags & nsMsgFolderFlags::Queue)
+ setSubfolderFlag(NS_LITERAL_STRING("Unsent Messages"), nsMsgFolderFlags::Queue);
+
+ if (flags & nsMsgFolderFlags::Junk)
+ setSubfolderFlag(NS_LITERAL_STRING("Junk"), nsMsgFolderFlags::Junk);
+
+ if (flags & nsMsgFolderFlags::Archive)
+ setSubfolderFlag(NS_LITERAL_STRING("Archives"), nsMsgFolderFlags::Archive);
+
+ return NS_OK;
+}
+
+nsresult
+nsMsgLocalMailFolder::setSubfolderFlag(const nsAString& aFolderName, uint32_t flags)
+{
+ // FindSubFolder() expects the folder name to be escaped
+ // see bug #192043
+ nsAutoCString escapedFolderName;
+ nsresult rv = NS_MsgEscapeEncodeURLPath(aFolderName, escapedFolderName);
+ NS_ENSURE_SUCCESS(rv,rv);
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+ rv = FindSubFolder(escapedFolderName, getter_AddRefs(msgFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // we only want to do this if the folder *really* exists,
+ // so check if it has a parent. Otherwise, we'll create the
+ // .msf file when we don't want to.
+ nsCOMPtr <nsIMsgFolder> parent;
+ msgFolder->GetParent(getter_AddRefs(parent));
+ if (!parent)
+ return NS_ERROR_FAILURE;
+
+ rv = msgFolder->SetFlag(flags);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return msgFolder->SetPrettyName(aFolderName);
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::GetCheckForNewMessagesAfterParsing(bool *aCheckForNewMessagesAfterParsing)
+{
+ NS_ENSURE_ARG_POINTER(aCheckForNewMessagesAfterParsing);
+ *aCheckForNewMessagesAfterParsing = mCheckForNewMessagesAfterParsing;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::SetCheckForNewMessagesAfterParsing(bool aCheckForNewMessagesAfterParsing)
+{
+ mCheckForNewMessagesAfterParsing = aCheckForNewMessagesAfterParsing;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::NotifyCompactCompleted()
+{
+ mExpungedBytes = 0;
+ m_newMsgs.Clear(); // if compacted, m_newMsgs probably aren't valid.
+ // if compacted, processing flags probably also aren't valid.
+ ClearProcessingFlags();
+ (void) RefreshSizeOnDisk();
+ (void) CloseDBIfFolderNotOpen();
+ nsCOMPtr <nsIAtom> compactCompletedAtom;
+ compactCompletedAtom = MsgGetAtom("CompactCompleted");
+ NotifyFolderEvent(compactCompletedAtom);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::Shutdown(bool shutdownChildren)
+{
+ mInitialized = false;
+ return nsMsgDBFolder::Shutdown(shutdownChildren);
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::OnMessageClassified(const char *aMsgURI,
+ nsMsgJunkStatus aClassification,
+ uint32_t aJunkPercent)
+
+{
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ nsresult rv = GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISpamSettings> spamSettings;
+ rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString spamFolderURI;
+ rv = spamSettings->GetSpamFolderURI(getter_Copies(spamFolderURI));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ if (aMsgURI) // not end of batch
+ {
+ nsCOMPtr <nsIMsgDBHdr> msgHdr;
+ rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsMsgKey msgKey;
+ rv = msgHdr->GetMessageKey(&msgKey);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // check if this message needs junk classification
+ uint32_t processingFlags;
+ GetProcessingFlags(msgKey, &processingFlags);
+
+ if (processingFlags & nsMsgProcessingFlags::ClassifyJunk)
+ {
+ nsMsgDBFolder::OnMessageClassified(aMsgURI, aClassification, aJunkPercent);
+
+ if (aClassification == nsIJunkMailPlugin::JUNK)
+ {
+ bool willMoveMessage = false;
+
+ // don't do the move when we are opening up
+ // the junk mail folder or the trash folder
+ // or when manually classifying messages in those folders
+ if (!(mFlags & nsMsgFolderFlags::Junk || mFlags & nsMsgFolderFlags::Trash))
+ {
+ bool moveOnSpam = false;
+ rv = spamSettings->GetMoveOnSpam(&moveOnSpam);
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (moveOnSpam)
+ {
+ nsCOMPtr<nsIMsgFolder> folder;
+ rv = GetExistingFolder(spamFolderURI, getter_AddRefs(folder));
+ if (NS_SUCCEEDED(rv) && folder)
+ {
+ rv = folder->SetFlag(nsMsgFolderFlags::Junk);
+ NS_ENSURE_SUCCESS(rv,rv);
+ mSpamKeysToMove.AppendElement(msgKey);
+ willMoveMessage = true;
+ }
+ else
+ {
+ // XXX TODO
+ // JUNK MAIL RELATED
+ // the listener should do
+ // rv = folder->SetFlag(nsMsgFolderFlags::Junk);
+ // NS_ENSURE_SUCCESS(rv,rv);
+ // mSpamKeysToMove.AppendElement(msgKey);
+ // willMoveMessage = true;
+ rv = GetOrCreateFolder(spamFolderURI, nullptr /* aListener */);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "GetOrCreateFolder failed");
+ }
+ }
+ }
+ rv = spamSettings->LogJunkHit(msgHdr, willMoveMessage);
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ }
+ }
+
+ else // end of batch
+ {
+ // Parent will apply post bayes filters.
+ nsMsgDBFolder::OnMessageClassified(nullptr, nsIJunkMailPlugin::UNCLASSIFIED, 0);
+ nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ if (!mSpamKeysToMove.IsEmpty())
+ {
+ nsCOMPtr<nsIMsgFolder> folder;
+ if (!spamFolderURI.IsEmpty())
+ rv = GetExistingFolder(spamFolderURI, getter_AddRefs(folder));
+ for (uint32_t keyIndex = 0; keyIndex < mSpamKeysToMove.Length(); keyIndex++)
+ {
+ // If an upstream filter moved this message, don't move it here.
+ nsMsgKey msgKey = mSpamKeysToMove.ElementAt(keyIndex);
+ nsMsgProcessingFlagType processingFlags;
+ GetProcessingFlags(msgKey, &processingFlags);
+ if (folder && !(processingFlags & nsMsgProcessingFlags::FilterToMove))
+ {
+ nsCOMPtr<nsIMsgDBHdr> mailHdr;
+ rv = GetMessageHeader(msgKey, getter_AddRefs(mailHdr));
+ if (NS_SUCCEEDED(rv) && mailHdr)
+ messages->AppendElement(mailHdr, false);
+ }
+ else
+ {
+ // We don't need the processing flag any more.
+ AndProcessingFlags(msgKey, ~nsMsgProcessingFlags::FilterToMove);
+ }
+ }
+
+ if (folder)
+ {
+ nsCOMPtr<nsIMsgCopyService> copySvc = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = copySvc->CopyMessages(this, messages, folder, true,
+ /*nsIMsgCopyServiceListener* listener*/ nullptr, nullptr, false /*allowUndo*/);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "CopyMessages failed");
+ if (NS_FAILED(rv))
+ {
+ nsAutoCString logMsg("failed to copy junk messages to junk folder rv = ");
+ logMsg.AppendInt(static_cast<uint32_t>(rv), 16);
+ spamSettings->LogJunkString(logMsg.get());
+ }
+ }
+ }
+ int32_t numNewMessages;
+ GetNumNewMessages(false, &numNewMessages);
+ uint32_t length;
+ messages->GetLength(&length);
+ SetNumNewMessages(numNewMessages - length);
+ mSpamKeysToMove.Clear();
+ // check if this is the inbox first...
+ if (mFlags & nsMsgFolderFlags::Inbox)
+ PerformBiffNotifications();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::GetFolderScanState(nsLocalFolderScanState *aState)
+{
+ NS_ENSURE_ARG_POINTER(aState);
+
+ nsresult rv = GetMsgStore(getter_AddRefs(aState->m_msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ aState->m_uidl = nullptr;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::GetUidlFromFolder(nsLocalFolderScanState *aState, nsIMsgDBHdr *aMsgDBHdr)
+{
+ bool more = false;
+ uint32_t size = 0, len = 0;
+ const char *accountKey = nullptr;
+ nsresult rv = GetMsgInputStream(aMsgDBHdr, &aState->m_streamReusable,
+ getter_AddRefs(aState->m_inputStream));
+ aState->m_seekableStream = do_QueryInterface(aState->m_inputStream);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsAutoPtr<nsLineBuffer<char> > lineBuffer(new nsLineBuffer<char>);
+ NS_ENSURE_TRUE(lineBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ aState->m_uidl = nullptr;
+
+ aMsgDBHdr->GetMessageSize(&len);
+ while (len > 0)
+ {
+ rv = NS_ReadLine(aState->m_inputStream.get(), lineBuffer.get(), aState->m_header, &more);
+ if (NS_SUCCEEDED(rv))
+ {
+ size = aState->m_header.Length();
+ if (!size)
+ break;
+ // this isn't quite right - need to account for line endings
+ len -= size;
+ // account key header will always be before X_UIDL header
+ if (!accountKey)
+ {
+ accountKey = strstr(aState->m_header.get(), HEADER_X_MOZILLA_ACCOUNT_KEY);
+ if (accountKey)
+ {
+ accountKey += strlen(HEADER_X_MOZILLA_ACCOUNT_KEY) + 2;
+ aState->m_accountKey = accountKey;
+ }
+ }
+ else
+ {
+ aState->m_uidl = strstr(aState->m_header.get(), X_UIDL);
+ if (aState->m_uidl)
+ {
+ aState->m_uidl += X_UIDL_LEN + 2; // skip UIDL: header
+ break;
+ }
+ }
+ }
+ }
+ if (!aState->m_streamReusable)
+ {
+ aState->m_inputStream->Close();
+ aState->m_inputStream = nullptr;
+ }
+ lineBuffer = nullptr;
+ return rv;
+}
+
+/**
+ * Adds a message to the end of the folder, parsing it as it goes, and
+ * applying filters, if applicable.
+ */
+NS_IMETHODIMP
+nsMsgLocalMailFolder::AddMessage(const char *aMessage, nsIMsgDBHdr **aHdr)
+{
+ const char *aMessages[] = {aMessage};
+ nsCOMPtr<nsIArray> hdrs;
+ nsresult rv = AddMessageBatch(1, aMessages, getter_AddRefs(hdrs));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgDBHdr> hdr(do_QueryElementAt(hdrs, 0, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ hdr.forget(aHdr);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::AddMessageBatch(uint32_t aMessageCount,
+ const char **aMessages,
+ nsIArray **aHdrArray)
+{
+ NS_ENSURE_ARG_POINTER(aHdrArray);
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ nsresult rv = GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ nsCOMPtr <nsIOutputStream> outFileStream;
+ nsCOMPtr<nsIMsgDBHdr> newHdr;
+
+ rv = server->GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ rv = GetRootFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isLocked;
+
+ GetLocked(&isLocked);
+ if (isLocked)
+ return NS_MSG_FOLDER_BUSY;
+
+ AcquireSemaphore(static_cast<nsIMsgLocalMailFolder*>(this));
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMutableArray> hdrArray =
+ do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ for (uint32_t i = 0; i < aMessageCount; i++)
+ {
+ RefPtr<nsParseNewMailState> newMailParser = new nsParseNewMailState;
+ NS_ENSURE_TRUE(newMailParser, NS_ERROR_OUT_OF_MEMORY);
+ if (!mGettingNewMessages)
+ newMailParser->DisableFilters();
+ bool reusable;
+ rv = msgStore->GetNewMsgOutputStream(this, getter_AddRefs(newHdr),
+ &reusable,
+ getter_AddRefs(outFileStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get a msgWindow. Proceed without one, but filter actions to imap folders
+ // will silently fail if not signed in and no window for a prompt.
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
+
+ rv = newMailParser->Init(rootFolder, this,
+ msgWindow, newHdr, outFileStream);
+
+ uint32_t bytesWritten, messageLen = strlen(aMessages[i]);
+ outFileStream->Write(aMessages[i], messageLen, &bytesWritten);
+ newMailParser->BufferInput(aMessages[i], messageLen);
+
+ msgStore->FinishNewMessage(outFileStream, newHdr);
+ outFileStream->Close();
+ outFileStream = nullptr;
+ newMailParser->OnStopRequest(nullptr, nullptr, NS_OK);
+ newMailParser->EndMsgDownload();
+ hdrArray->AppendElement(newHdr, false);
+ }
+ NS_ADDREF(*aHdrArray = hdrArray);
+ }
+ ReleaseSemaphore(static_cast<nsIMsgLocalMailFolder*>(this));
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgLocalMailFolder::WarnIfLocalFileTooBig(nsIMsgWindow *aWindow,
+ int64_t aSpaceRequested,
+ bool *aTooBig)
+{
+ NS_ENSURE_ARG_POINTER(aTooBig);
+
+ *aTooBig = true;
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ nsresult rv = GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool spaceAvailable = false;
+ // check if we have a reasonable amount of space left
+ rv = msgStore->HasSpaceAvailable(this, aSpaceRequested, &spaceAvailable);
+ if (NS_SUCCEEDED(rv) && spaceAvailable) {
+ *aTooBig = false;
+ } else if (rv == NS_ERROR_FILE_TOO_BIG) {
+ ThrowAlertMsg("mailboxTooLarge", aWindow);
+ } else {
+ ThrowAlertMsg("outOfDiskSpace", aWindow);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::FetchMsgPreviewText(nsMsgKey *aKeysToFetch, uint32_t aNumKeys,
+ bool aLocalOnly, nsIUrlListener *aUrlListener,
+ bool *aAsyncResults)
+{
+ NS_ENSURE_ARG_POINTER(aKeysToFetch);
+ NS_ENSURE_ARG_POINTER(aAsyncResults);
+
+ *aAsyncResults = false;
+ nsCOMPtr <nsIInputStream> inputStream;
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ nsresult rv = GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (uint32_t i = 0; i < aNumKeys; i++)
+ {
+ nsCOMPtr <nsIMsgDBHdr> msgHdr;
+ nsCString prevBody;
+ rv = GetMessageHeader(aKeysToFetch[i], getter_AddRefs(msgHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ // ignore messages that already have a preview body.
+ msgHdr->GetStringProperty("preview", getter_Copies(prevBody));
+ if (!prevBody.IsEmpty())
+ continue;
+
+ bool reusable;
+ rv = GetMsgInputStream(msgHdr, &reusable, getter_AddRefs(inputStream));
+ NS_ENSURE_SUCCESS(rv,rv);
+ rv = GetMsgPreviewTextFromStream(msgHdr, inputStream);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::AddKeywordsToMessages(nsIArray *aMessages, const nsACString& aKeywords)
+{
+ return ChangeKeywordForMessages(aMessages, aKeywords, true /* add */);
+}
+nsresult nsMsgLocalMailFolder::ChangeKeywordForMessages(nsIArray *aMessages, const nsACString& aKeywords, bool add)
+{
+ nsresult rv = (add) ? nsMsgDBFolder::AddKeywordsToMessages(aMessages, aKeywords)
+ : nsMsgDBFolder::RemoveKeywordsFromMessages(aMessages, aKeywords);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return msgStore->ChangeKeywords(aMessages, aKeywords, add);
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::RemoveKeywordsFromMessages(nsIArray *aMessages, const nsACString& aKeywords)
+{
+ return ChangeKeywordForMessages(aMessages, aKeywords, false /* remove */);
+}
+
+NS_IMETHODIMP nsMsgLocalMailFolder::UpdateNewMsgHdr(nsIMsgDBHdr* aOldHdr, nsIMsgDBHdr* aNewHdr)
+{
+ NS_ENSURE_ARG_POINTER(aOldHdr);
+ NS_ENSURE_ARG_POINTER(aNewHdr);
+ // Preserve any properties set on the message.
+ CopyPropertiesToMsgHdr(aNewHdr, aOldHdr, true);
+
+ // Preserve keywords manually, since they are set as don't preserve.
+ nsCString keywordString;
+ aOldHdr->GetStringProperty("keywords", getter_Copies(keywordString));
+ aNewHdr->SetStringProperty("keywords", keywordString.get());
+
+ // If the junk score was set by the plugin, remove junkscore to force a new
+ // junk analysis, this time using the body.
+ nsCString junkScoreOrigin;
+ aOldHdr->GetStringProperty("junkscoreorigin", getter_Copies(junkScoreOrigin));
+ if (junkScoreOrigin.EqualsLiteral("plugin"))
+ aNewHdr->SetStringProperty("junkscore", "");
+
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsLocalMailFolder.h b/mailnews/local/src/nsLocalMailFolder.h
new file mode 100644
index 000000000..e8c395342
--- /dev/null
+++ b/mailnews/local/src/nsLocalMailFolder.h
@@ -0,0 +1,276 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ Interface for representing Local Mail folders.
+*/
+
+#ifndef nsMsgLocalMailFolder_h__
+#define nsMsgLocalMailFolder_h__
+
+#include "mozilla/Attributes.h"
+#include "nsMsgDBFolder.h" /* include the interface we are going to support */
+#include "nsAutoPtr.h"
+#include "nsICopyMessageListener.h"
+#include "nsIFileStreams.h"
+#include "nsIPop3IncomingServer.h" // need this for an interface ID
+#include "nsMsgTxn.h"
+#include "nsIMsgMessageService.h"
+#include "nsIMsgParseMailMsgState.h"
+#include "nsITransactionManager.h"
+#include "nsIMsgLocalMailFolder.h"
+#include "nsISeekableStream.h"
+#include "nsIMutableArray.h"
+#include "nsLocalUndoTxn.h"
+
+#define COPY_BUFFER_SIZE 16384
+
+class nsParseMailMessageState;
+
+struct nsLocalMailCopyState
+{
+ nsLocalMailCopyState();
+ virtual ~nsLocalMailCopyState();
+
+ nsCOMPtr <nsIOutputStream> m_fileStream;
+ nsCOMPtr<nsIMsgPluggableStore> m_msgStore;
+ nsCOMPtr<nsISupports> m_srcSupport;
+ /// Source nsIMsgDBHdr instances.
+ nsCOMPtr<nsIArray> m_messages;
+ /// Destination nsIMsgDBHdr instances.
+ nsCOMPtr<nsIMutableArray> m_destMessages;
+ RefPtr<nsLocalMoveCopyMsgTxn> m_undoMsgTxn;
+ nsCOMPtr<nsIMsgDBHdr> m_message; // current copy message
+ nsMsgMessageFlagType m_flags; // current copy message flags
+ RefPtr<nsParseMailMessageState> m_parseMsgState;
+ nsCOMPtr<nsIMsgCopyServiceListener> m_listener;
+ nsCOMPtr<nsIMsgWindow> m_msgWindow;
+ nsCOMPtr<nsIMsgDatabase> m_destDB;
+
+ // for displaying status;
+ nsCOMPtr <nsIMsgStatusFeedback> m_statusFeedback;
+ nsCOMPtr <nsIStringBundle> m_stringBundle;
+ int64_t m_lastProgressTime;
+
+ nsMsgKey m_curDstKey;
+ uint32_t m_curCopyIndex;
+ nsCOMPtr <nsIMsgMessageService> m_messageService;
+ /// The number of messages in m_messages.
+ uint32_t m_totalMsgCount;
+ char *m_dataBuffer;
+ uint32_t m_dataBufferSize;
+ uint32_t m_leftOver;
+ bool m_isMove;
+ bool m_isFolder; // isFolder move/copy
+ bool m_dummyEnvelopeNeeded;
+ bool m_copyingMultipleMessages;
+ bool m_fromLineSeen;
+ bool m_allowUndo;
+ bool m_writeFailed;
+ bool m_notifyFolderLoaded;
+ bool m_wholeMsgInStream;
+ nsCString m_newMsgKeywords;
+ nsCOMPtr <nsIMsgDBHdr> m_newHdr;
+};
+
+struct nsLocalFolderScanState
+{
+ nsLocalFolderScanState();
+ ~nsLocalFolderScanState();
+
+ nsCOMPtr<nsIInputStream> m_inputStream;
+ nsCOMPtr<nsISeekableStream> m_seekableStream;
+ nsCOMPtr<nsIMsgPluggableStore> m_msgStore;
+ nsCString m_header;
+ nsCString m_accountKey;
+ const char *m_uidl; // memory is owned by m_header
+ // false if we need a new input stream for each message
+ bool m_streamReusable;
+};
+
+class nsMsgLocalMailFolder : public nsMsgDBFolder,
+ public nsIMsgLocalMailFolder,
+ public nsICopyMessageListener
+{
+public:
+ nsMsgLocalMailFolder(void);
+ NS_DECL_NSICOPYMESSAGELISTENER
+ NS_DECL_NSIMSGLOCALMAILFOLDER
+ NS_DECL_NSIJUNKMAILCLASSIFICATIONLISTENER
+ NS_DECL_ISUPPORTS_INHERITED
+ // nsIRDFResource methods:
+ NS_IMETHOD Init(const char *aURI) override;
+
+ // nsIUrlListener methods
+ NS_IMETHOD OnStartRunningUrl(nsIURI * aUrl) override;
+ NS_IMETHOD OnStopRunningUrl(nsIURI * aUrl, nsresult aExitCode) override;
+
+ // nsIMsgFolder methods:
+ NS_IMETHOD GetSubFolders(nsISimpleEnumerator* *aResult) override;
+ NS_IMETHOD GetMsgDatabase(nsIMsgDatabase **aMsgDatabase) override;
+
+ NS_IMETHOD OnAnnouncerGoingAway(nsIDBChangeAnnouncer *instigator) override;
+ NS_IMETHOD GetMessages(nsISimpleEnumerator **result) override;
+ NS_IMETHOD UpdateFolder(nsIMsgWindow *aWindow) override;
+
+ NS_IMETHOD CreateSubfolder(const nsAString& folderName ,nsIMsgWindow *msgWindow) override;
+
+ NS_IMETHOD Compact(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow) override;
+ NS_IMETHOD CompactAll(nsIUrlListener *aListener, nsIMsgWindow *aMsgWindow, bool aCompactOfflineAlso) override;
+ NS_IMETHOD EmptyTrash(nsIMsgWindow *msgWindow, nsIUrlListener *aListener) override;
+ NS_IMETHOD Delete () override;
+ NS_IMETHOD DeleteSubFolders(nsIArray *folders, nsIMsgWindow *msgWindow) override;
+ NS_IMETHOD CreateStorageIfMissing(nsIUrlListener* urlListener) override;
+ NS_IMETHOD Rename (const nsAString& aNewName, nsIMsgWindow *msgWindow) override;
+ NS_IMETHOD RenameSubFolders (nsIMsgWindow *msgWindow, nsIMsgFolder *oldFolder) override;
+
+ NS_IMETHOD GetPrettyName(nsAString& prettyName) override; // Override of the base, for top-level mail folder
+ NS_IMETHOD SetPrettyName(const nsAString& aName) override;
+
+ NS_IMETHOD GetFolderURL(nsACString& url) override;
+
+ NS_IMETHOD GetManyHeadersToDownload(bool *retval) override;
+
+ NS_IMETHOD GetDeletable (bool *deletable) override;
+ NS_IMETHOD GetSizeOnDisk(int64_t *size) override;
+
+ NS_IMETHOD GetDBFolderInfoAndDB(nsIDBFolderInfo **folderInfo, nsIMsgDatabase **db) override;
+
+ NS_IMETHOD DeleteMessages(nsIArray *messages,
+ nsIMsgWindow *msgWindow, bool
+ deleteStorage, bool isMove,
+ nsIMsgCopyServiceListener* listener, bool allowUndo) override;
+ NS_IMETHOD CopyMessages(nsIMsgFolder *srcFolder, nsIArray* messages,
+ bool isMove, nsIMsgWindow *msgWindow,
+ nsIMsgCopyServiceListener* listener, bool isFolder, bool allowUndo) override;
+ NS_IMETHOD CopyFolder(nsIMsgFolder *srcFolder, bool isMoveFolder, nsIMsgWindow *msgWindow,
+ nsIMsgCopyServiceListener* listener) override;
+ NS_IMETHOD CopyFileMessage(nsIFile* aFile, nsIMsgDBHdr* msgToReplace,
+ bool isDraftOrTemplate,
+ uint32_t newMsgFlags,
+ const nsACString &aNewMsgKeywords,
+ nsIMsgWindow *msgWindow,
+ nsIMsgCopyServiceListener* listener) override;
+
+ NS_IMETHOD AddMessageDispositionState(nsIMsgDBHdr *aMessage, nsMsgDispositionState aDispositionFlag) override;
+ NS_IMETHOD MarkMessagesRead(nsIArray *aMessages, bool aMarkRead) override;
+ NS_IMETHOD MarkMessagesFlagged(nsIArray *aMessages, bool aMarkFlagged) override;
+ NS_IMETHOD MarkAllMessagesRead(nsIMsgWindow *aMsgWindow) override;
+ NS_IMETHOD MarkThreadRead(nsIMsgThread *thread) override;
+ NS_IMETHOD GetNewMessages(nsIMsgWindow *aWindow, nsIUrlListener *aListener) override;
+ NS_IMETHOD NotifyCompactCompleted() override;
+ NS_IMETHOD Shutdown(bool shutdownChildren) override;
+
+ NS_IMETHOD WriteToFolderCacheElem(nsIMsgFolderCacheElement *element) override;
+ NS_IMETHOD ReadFromFolderCacheElem(nsIMsgFolderCacheElement *element) override;
+
+ NS_IMETHOD GetName(nsAString& aName) override;
+
+ // Used when headers_only is TRUE
+ NS_IMETHOD DownloadMessagesForOffline(nsIArray *aMessages, nsIMsgWindow *aWindow) override;
+ NS_IMETHOD FetchMsgPreviewText(nsMsgKey *aKeysToFetch, uint32_t aNumKeys,
+ bool aLocalOnly, nsIUrlListener *aUrlListener,
+ bool *aAsyncResults) override;
+ NS_IMETHOD AddKeywordsToMessages(nsIArray *aMessages, const nsACString& aKeywords) override;
+ NS_IMETHOD RemoveKeywordsFromMessages(nsIArray *aMessages, const nsACString& aKeywords) override;
+ NS_IMETHOD GetIncomingServerType(nsACString& serverType) override;
+
+protected:
+ virtual ~nsMsgLocalMailFolder();
+ nsresult CreateChildFromURI(const nsCString &uri, nsIMsgFolder **folder) override;
+ nsresult CopyFolderAcrossServer(nsIMsgFolder *srcFolder, nsIMsgWindow *msgWindow,nsIMsgCopyServiceListener* listener);
+
+ nsresult CreateSubFolders(nsIFile *path);
+ nsresult GetTrashFolder(nsIMsgFolder** trashFolder);
+ nsresult WriteStartOfNewMessage();
+
+ // CreateSubfolder, but without the nsIMsgFolderListener notification
+ nsresult CreateSubfolderInternal(const nsAString& folderName, nsIMsgWindow *msgWindow,
+ nsIMsgFolder **aNewFolder);
+
+ nsresult IsChildOfTrash(bool *result);
+ nsresult RecursiveSetDeleteIsMoveTrash(bool bVal);
+ nsresult ConfirmFolderDeletion(nsIMsgWindow *aMsgWindow, nsIMsgFolder *aFolder, bool *aResult);
+
+ nsresult DeleteMessage(nsISupports *message, nsIMsgWindow *msgWindow,
+ bool deleteStorage, bool commit);
+ nsresult GetDatabase() override;
+ // this will set mDatabase, if successful. It will also create a .msf file
+ // for an empty local mail folder. It will leave invalid DBs in place, and
+ // return an error.
+ nsresult OpenDatabase();
+
+ // copy message helper
+ nsresult DisplayMoveCopyStatusMsg();
+
+ nsresult CopyMessageTo(nsISupports *message, nsIMsgFolder *dstFolder,
+ nsIMsgWindow *msgWindow, bool isMove);
+
+ /**
+ * Checks if there's room in the target folder to copy message(s) into.
+ * If not, handles alerting the user, and sending the copy notifications.
+ */
+ bool CheckIfSpaceForCopy(nsIMsgWindow *msgWindow,
+ nsIMsgFolder *srcFolder,
+ nsISupports *srcSupports,
+ bool isMove,
+ int64_t totalMsgSize);
+
+ // copy multiple messages at a time from this folder
+ nsresult CopyMessagesTo(nsIArray *messages, nsTArray<nsMsgKey> &keyArray,
+ nsIMsgWindow *aMsgWindow,
+ nsIMsgFolder *dstFolder,
+ bool isMove);
+ nsresult InitCopyState(nsISupports* aSupport, nsIArray* messages,
+ bool isMove, nsIMsgCopyServiceListener* listener, nsIMsgWindow *msgWindow, bool isMoveFolder, bool allowUndo);
+ nsresult InitCopyMsgHdrAndFileStream();
+ // preserve message metadata when moving or copying messages
+ void CopyPropertiesToMsgHdr(nsIMsgDBHdr *destHdr, nsIMsgDBHdr *srcHdr, bool isMove);
+ virtual nsresult CreateBaseMessageURI(const nsACString& aURI) override;
+ nsresult ChangeKeywordForMessages(nsIArray *aMessages, const nsACString& aKeyword, bool add);
+ bool GetDeleteFromServerOnMove();
+ void CopyHdrPropertiesWithSkipList(nsIMsgDBHdr *destHdr,
+ nsIMsgDBHdr *srcHdr,
+ const nsCString &skipList);
+
+protected:
+ nsLocalMailCopyState *mCopyState; //We only allow one of these at a time
+ nsCString mType;
+ bool mHaveReadNameFromDB;
+ bool mInitialized;
+ bool mCheckForNewMessagesAfterParsing;
+ bool m_parsingFolder;
+ nsCOMPtr<nsIUrlListener> mReparseListener;
+ nsTArray<nsMsgKey> mSpamKeysToMove;
+ nsresult setSubfolderFlag(const nsAString& aFolderName, uint32_t flags);
+
+ // state variables for DownloadMessagesForOffline
+
+ // Do we notify the owning window of Delete's before or after
+ // Adding the new msg?
+#define DOWNLOAD_NOTIFY_FIRST 1
+#define DOWNLOAD_NOTIFY_LAST 2
+
+#ifndef DOWNLOAD_NOTIFY_STYLE
+#define DOWNLOAD_NOTIFY_STYLE DOWNLOAD_NOTIFY_FIRST
+#endif
+
+ nsCOMArray<nsIMsgDBHdr> mDownloadMessages;
+ nsCOMPtr<nsIMsgWindow> mDownloadWindow;
+ nsMsgKey mDownloadSelectKey;
+ uint32_t mDownloadState;
+#define DOWNLOAD_STATE_NONE 0
+#define DOWNLOAD_STATE_INITED 1
+#define DOWNLOAD_STATE_GOTMSG 2
+#define DOWNLOAD_STATE_DIDSEL 3
+
+#if DOWNLOAD_NOTIFY_STYLE == DOWNLOAD_NOTIFY_LAST
+ nsMsgKey mDownloadOldKey;
+ nsMsgKey mDownloadOldParent;
+ uint32_t mDownloadOldFlags;
+#endif
+};
+
+#endif // nsMsgLocalMailFolder_h__
diff --git a/mailnews/local/src/nsLocalUndoTxn.cpp b/mailnews/local/src/nsLocalUndoTxn.cpp
new file mode 100644
index 000000000..a53ac992e
--- /dev/null
+++ b/mailnews/local/src/nsLocalUndoTxn.cpp
@@ -0,0 +1,560 @@
+/* -*- Mode: C++; tab-width: 4; 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"
+#include "nsIMsgHdr.h"
+#include "nsLocalUndoTxn.h"
+#include "nsImapCore.h"
+#include "nsMsgImapCID.h"
+#include "nsIImapService.h"
+#include "nsIUrlListener.h"
+#include "nsIMsgLocalMailFolder.h"
+#include "nsIMsgMailSession.h"
+#include "nsIMsgFolderNotificationService.h"
+#include "nsThreadUtils.h"
+#include "nsIMsgDatabase.h"
+#include "nsIMutableArray.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsMsgUtils.h"
+
+NS_IMPL_ISUPPORTS_INHERITED(nsLocalMoveCopyMsgTxn, nsMsgTxn, nsIFolderListener)
+
+nsLocalMoveCopyMsgTxn::nsLocalMoveCopyMsgTxn() : m_srcIsImap4(false),
+ m_canUndelete(false)
+{
+}
+
+nsLocalMoveCopyMsgTxn::~nsLocalMoveCopyMsgTxn()
+{
+}
+
+nsresult
+nsLocalMoveCopyMsgTxn::Init(nsIMsgFolder* srcFolder, nsIMsgFolder* dstFolder,
+ bool isMove)
+{
+ nsresult rv;
+ rv = SetSrcFolder(srcFolder);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = SetDstFolder(dstFolder);
+ NS_ENSURE_SUCCESS(rv, rv);
+ m_isMove = isMove;
+
+ mUndoFolderListener = nullptr;
+
+ nsCString protocolType;
+ rv = srcFolder->GetURI(protocolType);
+ protocolType.SetLength(protocolType.FindChar(':'));
+ if (MsgLowerCaseEqualsLiteral(protocolType, "imap"))
+ m_srcIsImap4 = true;
+ return nsMsgTxn::Init();
+}
+nsresult
+nsLocalMoveCopyMsgTxn::GetSrcIsImap(bool *isImap)
+{
+ *isImap = m_srcIsImap4;
+ return NS_OK;
+}
+nsresult
+nsLocalMoveCopyMsgTxn::SetSrcFolder(nsIMsgFolder* srcFolder)
+{
+ nsresult rv = NS_ERROR_NULL_POINTER;
+ if (srcFolder)
+ m_srcFolder = do_GetWeakReference(srcFolder, &rv);
+ return rv;
+}
+
+nsresult
+nsLocalMoveCopyMsgTxn::SetDstFolder(nsIMsgFolder* dstFolder)
+{
+ nsresult rv = NS_ERROR_NULL_POINTER;
+ if (dstFolder)
+ m_dstFolder = do_GetWeakReference(dstFolder, &rv);
+ return rv;
+}
+
+nsresult
+nsLocalMoveCopyMsgTxn::AddSrcKey(nsMsgKey aKey)
+{
+ m_srcKeyArray.AppendElement(aKey);
+ return NS_OK;
+}
+
+nsresult
+nsLocalMoveCopyMsgTxn::AddSrcStatusOffset(uint32_t aStatusOffset)
+{
+ m_srcStatusOffsetArray.AppendElement(aStatusOffset);
+ return NS_OK;
+}
+
+
+nsresult
+nsLocalMoveCopyMsgTxn::AddDstKey(nsMsgKey aKey)
+{
+ m_dstKeyArray.AppendElement(aKey);
+ return NS_OK;
+}
+
+nsresult
+nsLocalMoveCopyMsgTxn::AddDstMsgSize(uint32_t msgSize)
+{
+ m_dstSizeArray.AppendElement(msgSize);
+ return NS_OK;
+}
+
+nsresult
+nsLocalMoveCopyMsgTxn::UndoImapDeleteFlag(nsIMsgFolder* folder,
+ nsTArray<nsMsgKey>& keyArray,
+ bool deleteFlag)
+{
+ nsresult rv = NS_ERROR_FAILURE;
+ if (m_srcIsImap4)
+ {
+ nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIUrlListener> urlListener;
+ nsCString msgIds;
+ uint32_t i, count = keyArray.Length();
+ urlListener = do_QueryInterface(folder, &rv);
+ for (i=0; i < count; i++)
+ {
+ if (!msgIds.IsEmpty())
+ msgIds.Append(',');
+ msgIds.AppendInt((int32_t) keyArray[i]);
+ }
+ // This is to make sure that we are in the selected state
+ // when executing the imap url; we don't want to load the
+ // folder so use lite select to do the trick
+ rv = imapService->LiteSelectFolder(folder,
+ urlListener, nullptr, nullptr);
+ if (!deleteFlag)
+ rv =imapService->AddMessageFlags(folder,
+ urlListener, nullptr,
+ msgIds,
+ kImapMsgDeletedFlag,
+ true);
+ else
+ rv = imapService->SubtractMessageFlags(folder,
+ urlListener, nullptr,
+ msgIds,
+ kImapMsgDeletedFlag,
+ true);
+ if (NS_SUCCEEDED(rv) && m_msgWindow)
+ folder->UpdateFolder(m_msgWindow);
+ rv = NS_OK; // always return NS_OK to indicate that the src is imap
+ }
+ else
+ rv = NS_ERROR_FAILURE;
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalMoveCopyMsgTxn::UndoTransaction()
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgDatabase> dstDB;
+
+ nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgLocalMailFolder> dstlocalMailFolder = do_QueryReferent(m_dstFolder, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ dstlocalMailFolder->GetDatabaseWOReparse(getter_AddRefs(dstDB));
+
+ if (!dstDB)
+ {
+ // This will listen for the db reparse finishing, and the corresponding
+ // FolderLoadedNotification. When it gets that, it will then call
+ // UndoTransactionInternal.
+ mUndoFolderListener = new nsLocalUndoFolderListener(this, dstFolder);
+ if (!mUndoFolderListener)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(mUndoFolderListener);
+
+ nsCOMPtr<nsIMsgMailSession> mailSession =
+ do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = mailSession->AddFolderListener(mUndoFolderListener, nsIFolderListener::event);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = dstFolder->GetMsgDatabase(getter_AddRefs(dstDB));
+ NS_ENSURE_SUCCESS(rv,rv);
+ }
+ else
+ rv = UndoTransactionInternal();
+ return rv;
+}
+
+nsresult
+nsLocalMoveCopyMsgTxn::UndoTransactionInternal()
+{
+ nsresult rv = NS_ERROR_FAILURE;
+
+ if (mUndoFolderListener)
+ {
+ nsCOMPtr<nsIMsgMailSession> mailSession =
+ do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = mailSession->RemoveFolderListener(mUndoFolderListener);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ NS_RELEASE(mUndoFolderListener);
+ mUndoFolderListener = nullptr;
+ }
+
+ nsCOMPtr<nsIMsgDatabase> srcDB;
+ nsCOMPtr<nsIMsgDatabase> dstDB;
+ nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
+ if(NS_FAILED(rv)) return rv;
+
+ rv = dstFolder->GetMsgDatabase(getter_AddRefs(dstDB));
+ if (NS_FAILED(rv)) return rv;
+
+ uint32_t count = m_srcKeyArray.Length();
+ uint32_t i;
+ nsCOMPtr<nsIMsgDBHdr> oldHdr;
+ nsCOMPtr<nsIMsgDBHdr> newHdr;
+
+ // protect against a bogus undo txn without any source keys
+ // see bug #179856 for details
+ NS_ASSERTION(count, "no source keys");
+ if (!count)
+ return NS_ERROR_UNEXPECTED;
+
+ if (m_isMove)
+ {
+ if (m_srcIsImap4)
+ {
+ bool deleteFlag = true; //message has been deleted -we are trying to undo it
+ CheckForToggleDelete(srcFolder, m_srcKeyArray[0], &deleteFlag); //there could have been a toggle.
+ rv = UndoImapDeleteFlag(srcFolder, m_srcKeyArray, deleteFlag);
+ }
+ else if (m_canUndelete)
+ {
+ nsCOMPtr<nsIMutableArray> srcMessages =
+ do_CreateInstance(NS_ARRAY_CONTRACTID);
+ nsCOMPtr<nsIMutableArray> dstMessages =
+ do_CreateInstance(NS_ARRAY_CONTRACTID);
+
+ srcDB->StartBatch();
+ for (i = 0; i < count; i++)
+ {
+ rv = dstDB->GetMsgHdrForKey(m_dstKeyArray[i],
+ getter_AddRefs(oldHdr));
+ NS_ASSERTION(oldHdr, "fatal ... cannot get old msg header\n");
+ if (NS_SUCCEEDED(rv) && oldHdr)
+ {
+ rv = srcDB->CopyHdrFromExistingHdr(m_srcKeyArray[i],
+ oldHdr, true,
+ getter_AddRefs(newHdr));
+ NS_ASSERTION(newHdr,
+ "fatal ... cannot create new msg header\n");
+ if (NS_SUCCEEDED(rv) && newHdr)
+ {
+ newHdr->SetStatusOffset(m_srcStatusOffsetArray[i]);
+ srcDB->UndoDelete(newHdr);
+ srcMessages->AppendElement(newHdr, false);
+ // (we want to keep these two lists in sync)
+ dstMessages->AppendElement(oldHdr, false);
+ }
+ }
+ }
+ srcDB->EndBatch();
+
+ nsCOMPtr<nsIMsgFolderNotificationService>
+ notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ if (notifier)
+ {
+ // Remember that we're actually moving things back from the destination
+ // to the source!
+ notifier->NotifyMsgsMoveCopyCompleted(true, dstMessages,
+ srcFolder, srcMessages);
+ }
+
+ nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(srcFolder);
+ if (localFolder)
+ localFolder->MarkMsgsOnPop3Server(srcMessages, POP3_NONE /*deleteMsgs*/);
+ }
+ else // undoing a move means moving the messages back.
+ {
+ nsCOMPtr<nsIMutableArray> dstMessages =
+ do_CreateInstance(NS_ARRAY_CONTRACTID);
+ m_numHdrsCopied = 0;
+ m_srcKeyArray.Clear();
+ for (i = 0; i < count; i++)
+ {
+ // GetMsgHdrForKey is not a test for whether the key exists, so check.
+ bool hasKey = false;
+ dstDB->ContainsKey(m_dstKeyArray[i], &hasKey);
+ nsCOMPtr<nsIMsgDBHdr> dstHdr;
+ if (hasKey)
+ dstDB->GetMsgHdrForKey(m_dstKeyArray[i], getter_AddRefs(dstHdr));
+ if (dstHdr)
+ {
+ nsCString messageId;
+ dstHdr->GetMessageId(getter_Copies(messageId));
+ dstMessages->AppendElement(dstHdr, false);
+ m_copiedMsgIds.AppendElement(messageId);
+ }
+ else
+ {
+ NS_WARNING("Cannot get old msg header");
+ }
+ }
+ if (m_copiedMsgIds.Length())
+ {
+ srcFolder->AddFolderListener(this);
+ m_undoing = true;
+ return srcFolder->CopyMessages(dstFolder, dstMessages,
+ true, nullptr, nullptr, false,
+ false);
+ }
+ else
+ {
+ // Nothing to do, probably because original messages were deleted.
+ NS_WARNING("Undo did not find any messages to move");
+ }
+ }
+ srcDB->SetSummaryValid(true);
+ }
+
+ dstDB->DeleteMessages(m_dstKeyArray.Length(), m_dstKeyArray.Elements(), nullptr);
+ dstDB->SetSummaryValid(true);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsLocalMoveCopyMsgTxn::RedoTransaction()
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgDatabase> srcDB;
+ nsCOMPtr<nsIMsgDatabase> dstDB;
+
+ nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
+ if(NS_FAILED(rv)) return rv;
+ rv = dstFolder->GetMsgDatabase(getter_AddRefs(dstDB));
+ if (NS_FAILED(rv)) return rv;
+
+ uint32_t count = m_srcKeyArray.Length();
+ uint32_t i;
+ nsCOMPtr<nsIMsgDBHdr> oldHdr;
+ nsCOMPtr<nsIMsgDBHdr> newHdr;
+
+ nsCOMPtr<nsIMutableArray> srcMessages = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ nsCOMPtr <nsISupports> msgSupports;
+
+ for (i=0; i<count; i++)
+ {
+ rv = srcDB->GetMsgHdrForKey(m_srcKeyArray[i],
+ getter_AddRefs(oldHdr));
+ NS_ASSERTION(oldHdr, "fatal ... cannot get old msg header\n");
+
+ if (NS_SUCCEEDED(rv) && oldHdr)
+ {
+ msgSupports =do_QueryInterface(oldHdr);
+ srcMessages->AppendElement(msgSupports, false);
+
+ if (m_canUndelete)
+ {
+ rv = dstDB->CopyHdrFromExistingHdr(m_dstKeyArray[i],
+ oldHdr, true,
+ getter_AddRefs(newHdr));
+ NS_ASSERTION(newHdr, "fatal ... cannot get new msg header\n");
+ if (NS_SUCCEEDED(rv) && newHdr)
+ {
+ if (i < m_dstSizeArray.Length())
+ rv = newHdr->SetMessageSize(m_dstSizeArray[i]);
+ dstDB->UndoDelete(newHdr);
+ }
+ }
+ }
+ }
+ dstDB->SetSummaryValid(true);
+
+ if (m_isMove)
+ {
+ if (m_srcIsImap4)
+ {
+ // protect against a bogus undo txn without any source keys
+ // see bug #179856 for details
+ NS_ASSERTION(!m_srcKeyArray.IsEmpty(), "no source keys");
+ if (m_srcKeyArray.IsEmpty())
+ return NS_ERROR_UNEXPECTED;
+
+ bool deleteFlag = false; //message is un-deleted- we are trying to redo
+ CheckForToggleDelete(srcFolder, m_srcKeyArray[0], &deleteFlag); // there could have been a toggle
+ rv = UndoImapDeleteFlag(srcFolder, m_srcKeyArray, deleteFlag);
+ }
+ else if (m_canUndelete)
+ {
+ nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(srcFolder);
+ if (localFolder)
+ localFolder->MarkMsgsOnPop3Server(srcMessages, POP3_DELETE /*deleteMsgs*/);
+
+ rv = srcDB->DeleteMessages(m_srcKeyArray.Length(), m_srcKeyArray.Elements(), nullptr);
+ srcDB->SetSummaryValid(true);
+ }
+ else
+ {
+ nsCOMPtr<nsIMsgDBHdr> srcHdr;
+ m_numHdrsCopied = 0;
+ m_dstKeyArray.Clear();
+ for (i = 0; i < count; i++)
+ {
+ srcDB->GetMsgHdrForKey(m_srcKeyArray[i], getter_AddRefs(srcHdr));
+ NS_ASSERTION(srcHdr, "fatal ... cannot get old msg header\n");
+ if (srcHdr)
+ {
+ nsCString messageId;
+ srcHdr->GetMessageId(getter_Copies(messageId));
+ m_copiedMsgIds.AppendElement(messageId);
+ }
+ }
+ dstFolder->AddFolderListener(this);
+ m_undoing = false;
+ return dstFolder->CopyMessages(srcFolder, srcMessages, true, nullptr,
+ nullptr, false, false);
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemAdded(nsIMsgFolder *parentItem, nsISupports *item)
+{
+ nsCOMPtr<nsIMsgDBHdr> msgHdr(do_QueryInterface(item));
+ if (msgHdr)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(m_undoing ? m_srcFolder :
+ m_dstFolder, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ nsCString messageId;
+ msgHdr->GetMessageId(getter_Copies(messageId));
+ if (m_copiedMsgIds.Contains(messageId))
+ {
+ nsMsgKey msgKey;
+ msgHdr->GetMessageKey(&msgKey);
+ if (m_undoing)
+ m_srcKeyArray.AppendElement(msgKey);
+ else
+ m_dstKeyArray.AppendElement(msgKey);
+ if (++m_numHdrsCopied == m_copiedMsgIds.Length())
+ {
+ folder->RemoveFolderListener(this);
+ m_copiedMsgIds.Clear();
+ }
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemRemoved(nsIMsgFolder *parentItem, nsISupports *item)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemPropertyChanged(nsIMsgFolder *item, nsIAtom *property, const char *oldValue, const char *newValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemIntPropertyChanged(nsIMsgFolder *item, nsIAtom *property, int64_t oldValue, int64_t newValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemBoolPropertyChanged(nsIMsgFolder *item, nsIAtom *property, bool oldValue, bool newValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemUnicharPropertyChanged(nsIMsgFolder *item, nsIAtom *property, const char16_t *oldValue, const char16_t *newValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property, uint32_t oldFlag, uint32_t newFlag)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemEvent(nsIMsgFolder *aItem, nsIAtom *aEvent)
+{
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsLocalUndoFolderListener, nsIFolderListener)
+
+nsLocalUndoFolderListener::nsLocalUndoFolderListener(nsLocalMoveCopyMsgTxn *aTxn, nsIMsgFolder *aFolder)
+{
+ mTxn = aTxn;
+ mFolder = aFolder;
+}
+
+nsLocalUndoFolderListener::~nsLocalUndoFolderListener()
+{
+}
+
+NS_IMETHODIMP nsLocalUndoFolderListener::OnItemAdded(nsIMsgFolder *parentItem, nsISupports *item)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalUndoFolderListener::OnItemRemoved(nsIMsgFolder *parentItem, nsISupports *item)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalUndoFolderListener::OnItemPropertyChanged(nsIMsgFolder *item, nsIAtom *property, const char *oldValue, const char *newValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalUndoFolderListener::OnItemIntPropertyChanged(nsIMsgFolder *item, nsIAtom *property, int64_t oldValue, int64_t newValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalUndoFolderListener::OnItemBoolPropertyChanged(nsIMsgFolder *item, nsIAtom *property, bool oldValue, bool newValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalUndoFolderListener::OnItemUnicharPropertyChanged(nsIMsgFolder *item, nsIAtom *property, const char16_t *oldValue, const char16_t *newValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalUndoFolderListener::OnItemPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property, uint32_t oldFlag, uint32_t newFlag)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsLocalUndoFolderListener::OnItemEvent(nsIMsgFolder *aItem, nsIAtom *aEvent)
+{
+ if (mTxn && mFolder && aItem == mFolder) {
+ bool isEqual = false;
+ aEvent->ScriptableEquals(NS_LITERAL_STRING("FolderLoaded"), &isEqual);
+ if (isEqual)
+ return mTxn->UndoTransactionInternal();
+ }
+
+ return NS_ERROR_FAILURE;
+}
diff --git a/mailnews/local/src/nsLocalUndoTxn.h b/mailnews/local/src/nsLocalUndoTxn.h
new file mode 100644
index 000000000..62f93f20a
--- /dev/null
+++ b/mailnews/local/src/nsLocalUndoTxn.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef nsLocalUndoTxn_h__
+#define nsLocalUndoTxn_h__
+
+#include "mozilla/Attributes.h"
+#include "msgCore.h"
+#include "nsIMsgFolder.h"
+#include "nsMailboxService.h"
+#include "nsMsgTxn.h"
+#include "MailNewsTypes.h"
+#include "nsTArray.h"
+#include "nsCOMPtr.h"
+#include "nsIUrlListener.h"
+#include "nsIWeakReference.h"
+#include "nsIWeakReferenceUtils.h"
+
+class nsLocalUndoFolderListener;
+
+class nsLocalMoveCopyMsgTxn : public nsIFolderListener, public nsMsgTxn
+{
+public:
+ nsLocalMoveCopyMsgTxn();
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIFOLDERLISTENER
+
+ // overloading nsITransaction methods
+ NS_IMETHOD UndoTransaction(void) override;
+ NS_IMETHOD RedoTransaction(void) override;
+
+ // helper
+ nsresult AddSrcKey(nsMsgKey aKey);
+ nsresult AddSrcStatusOffset(uint32_t statusOffset);
+ nsresult AddDstKey(nsMsgKey aKey);
+ nsresult AddDstMsgSize(uint32_t msgSize);
+ nsresult SetSrcFolder(nsIMsgFolder* srcFolder);
+ nsresult GetSrcIsImap(bool *isImap);
+ nsresult SetDstFolder(nsIMsgFolder* dstFolder);
+ nsresult Init(nsIMsgFolder* srcFolder,
+ nsIMsgFolder* dstFolder, bool isMove);
+ nsresult UndoImapDeleteFlag(nsIMsgFolder* aFolder,
+ nsTArray<nsMsgKey>& aKeyArray,
+ bool deleteFlag);
+ nsresult UndoTransactionInternal();
+ // If the store using this undo transaction can "undelete" a message,
+ // it will call this function on the transaction; This makes undo/redo
+ // easy because message keys don't change after undo/redo. Otherwise,
+ // we need to adjust the src or dst keys after every undo/redo action
+ // to note the new keys.
+ void SetCanUndelete(bool canUndelete) {m_canUndelete = canUndelete;}
+
+private:
+ virtual ~nsLocalMoveCopyMsgTxn();
+ nsWeakPtr m_srcFolder;
+ nsTArray<nsMsgKey> m_srcKeyArray; // used when src is local or imap
+ nsTArray<uint32_t> m_srcStatusOffsetArray; // used when src is local
+ nsWeakPtr m_dstFolder;
+ nsTArray<nsMsgKey> m_dstKeyArray;
+ bool m_isMove;
+ bool m_srcIsImap4;
+ bool m_canUndelete;
+ nsTArray<uint32_t> m_dstSizeArray;
+ bool m_undoing; // if false, re-doing
+ uint32_t m_numHdrsCopied;
+ nsTArray<nsCString> m_copiedMsgIds;
+ nsLocalUndoFolderListener *mUndoFolderListener;
+};
+
+class nsLocalUndoFolderListener : public nsIFolderListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIFOLDERLISTENER
+
+ nsLocalUndoFolderListener(nsLocalMoveCopyMsgTxn *aTxn, nsIMsgFolder *aFolder);
+
+private:
+ virtual ~nsLocalUndoFolderListener();
+ nsLocalMoveCopyMsgTxn *mTxn;
+ nsIMsgFolder *mFolder;
+};
+
+#endif
diff --git a/mailnews/local/src/nsLocalUtils.cpp b/mailnews/local/src/nsLocalUtils.cpp
new file mode 100644
index 000000000..14a6a2f21
--- /dev/null
+++ b/mailnews/local/src/nsLocalUtils.cpp
@@ -0,0 +1,244 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "msgCore.h"
+#include "nsLocalUtils.h"
+#include "nsIServiceManager.h"
+#include "prsystem.h"
+#include "nsCOMPtr.h"
+#include "prmem.h"
+// stuff for temporary root folder hack
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsIPop3IncomingServer.h"
+#include "nsINoIncomingServer.h"
+#include "nsMsgBaseCID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+
+#include "nsMsgUtils.h"
+#include "nsNetCID.h"
+
+// it would be really cool to:
+// - cache the last hostname->path match
+// - if no such server exists, behave like an old-style mailbox URL
+// (i.e. return the mail.directory preference or something)
+static nsresult
+nsGetMailboxServer(const char *uriStr, nsIMsgIncomingServer** aResult)
+{
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIURL> aUrl = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = aUrl->SetSpec(nsDependentCString(uriStr));
+ if (NS_FAILED(rv)) return rv;
+
+ // retrieve the AccountManager
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ // find all local mail "no servers" matching the given hostname
+ nsCOMPtr<nsIMsgIncomingServer> none_server;
+ aUrl->SetScheme(NS_LITERAL_CSTRING("none"));
+ // No unescaping of username or hostname done here.
+ // The unescaping is done inside of FindServerByURI
+ rv = accountManager->FindServerByURI(aUrl, false,
+ getter_AddRefs(none_server));
+ if (NS_SUCCEEDED(rv)) {
+ NS_ADDREF(*aResult = none_server);
+ return rv;
+ }
+
+ // if that fails, look for the rss hosts matching the given hostname
+ nsCOMPtr<nsIMsgIncomingServer> rss_server;
+ aUrl->SetScheme(NS_LITERAL_CSTRING("rss"));
+ rv = accountManager->FindServerByURI(aUrl, false,
+ getter_AddRefs(rss_server));
+ if (NS_SUCCEEDED(rv))
+ {
+ NS_ADDREF(*aResult = rss_server);
+ return rv;
+ }
+#ifdef HAVE_MOVEMAIL
+ // find all movemail "servers" matching the given hostname
+ nsCOMPtr<nsIMsgIncomingServer> movemail_server;
+ aUrl->SetScheme(NS_LITERAL_CSTRING("movemail"));
+ rv = accountManager->FindServerByURI(aUrl, false,
+ getter_AddRefs(movemail_server));
+ if (NS_SUCCEEDED(rv)) {
+ NS_ADDREF(*aResult = movemail_server);
+ return rv;
+ }
+#endif /* HAVE_MOVEMAIL */
+
+ // if that fails, look for the pop hosts matching the given hostname
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ if (NS_FAILED(rv))
+ {
+ aUrl->SetScheme(NS_LITERAL_CSTRING("pop3"));
+ rv = accountManager->FindServerByURI(aUrl, false,
+ getter_AddRefs(server));
+
+ // if we can't find a pop server, maybe it's a local message
+ // in an imap hierarchy. look for an imap server.
+ if (NS_FAILED(rv))
+ {
+ aUrl->SetScheme(NS_LITERAL_CSTRING("imap"));
+ rv = accountManager->FindServerByURI(aUrl, false,
+ getter_AddRefs(server));
+ }
+ }
+ if (NS_SUCCEEDED(rv))
+ {
+ NS_ADDREF(*aResult = server);
+ return rv;
+ }
+
+// if you fail after looking at all "pop3", "movemail" and "none" servers, you fail.
+return rv;
+}
+
+static nsresult
+nsLocalURI2Server(const char* uriStr,
+ nsIMsgIncomingServer ** aResult)
+{
+ nsresult rv;
+
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = nsGetMailboxServer(uriStr, getter_AddRefs(server));
+
+ NS_IF_ADDREF(*aResult = server);
+
+ return rv;
+}
+
+// given rootURI and rootURI##folder, return on-disk path of folder
+nsresult
+nsLocalURI2Path(const char* rootURI, const char* uriStr,
+ nsCString& pathResult)
+{
+ nsresult rv;
+
+ // verify that rootURI starts with "mailbox:/" or "mailbox-message:/"
+ if ((PL_strcmp(rootURI, kMailboxRootURI) != 0) &&
+ (PL_strcmp(rootURI, kMailboxMessageRootURI) != 0)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // verify that uristr starts with rooturi
+ nsAutoCString uri(uriStr);
+ if (uri.Find(rootURI) != 0)
+ return NS_ERROR_FAILURE;
+
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = nsLocalURI2Server(uriStr, getter_AddRefs(server));
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ // now ask the server what it's root is
+ // and begin pathResult with the mailbox root
+ nsCOMPtr<nsIFile> localPath;
+ rv = server->GetLocalPath(getter_AddRefs(localPath));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString localNativePath;
+
+ localPath->GetNativePath(localNativePath);
+ nsEscapeNativePath(localNativePath);
+ pathResult = localNativePath.get();
+ const char *curPos = uriStr + PL_strlen(rootURI);
+ if (curPos) {
+ // advance past hostname
+ while ((*curPos)=='/') curPos++;
+ while (*curPos && (*curPos)!='/') curPos++;
+
+ nsAutoCString newPath("");
+
+ // Unescape folder name
+ if (curPos) {
+ nsCString unescapedStr;
+ MsgUnescapeString(nsDependentCString(curPos), 0, unescapedStr);
+ NS_MsgCreatePathStringFromFolderURI(unescapedStr.get(), newPath, NS_LITERAL_CSTRING("none"));
+ } else
+ NS_MsgCreatePathStringFromFolderURI(curPos, newPath, NS_LITERAL_CSTRING("none"));
+
+ pathResult.Append('/');
+ pathResult.Append(newPath);
+ }
+
+ return NS_OK;
+}
+
+/* parses LocalMessageURI
+ * mailbox-message://folder1/folder2#123?header=none or
+ * mailbox-message://folder1/folder2#1234&part=1.2
+ *
+ * puts folder URI in folderURI (mailbox://folder1/folder2)
+ * message key number in key
+ */
+nsresult nsParseLocalMessageURI(const char* uri,
+ nsCString& folderURI,
+ nsMsgKey *key)
+{
+ if(!key)
+ return NS_ERROR_NULL_POINTER;
+
+ nsAutoCString uriStr(uri);
+ int32_t keySeparator = uriStr.FindChar('#');
+ if(keySeparator != -1)
+ {
+ int32_t keyEndSeparator = MsgFindCharInSet(uriStr, "?&", keySeparator);
+ folderURI = StringHead(uriStr, keySeparator);
+ folderURI.Cut(7, 8); // cut out the -message part of mailbox-message:
+
+ nsAutoCString keyStr;
+ if (keyEndSeparator != -1)
+ keyStr = Substring(uriStr, keySeparator + 1,
+ keyEndSeparator - (keySeparator + 1));
+ else
+ keyStr = StringTail(uriStr, uriStr.Length() - (keySeparator + 1));
+
+ *key = msgKeyFromInt(ParseUint64Str(keyStr.get()));
+ return NS_OK;
+ }
+ return NS_ERROR_FAILURE;
+
+}
+
+nsresult nsBuildLocalMessageURI(const char *baseURI, nsMsgKey key, nsCString& uri)
+{
+ // need to convert mailbox://hostname/.. to mailbox-message://hostname/..
+ uri.Append(baseURI);
+ uri.Append('#');
+ uri.AppendInt(key);
+ return NS_OK;
+}
+
+nsresult nsCreateLocalBaseMessageURI(const nsACString& baseURI, nsCString &baseMessageURI)
+{
+ nsAutoCString tailURI(baseURI);
+
+ // chop off mailbox:/
+ if (tailURI.Find(kMailboxRootURI) == 0)
+ tailURI.Cut(0, PL_strlen(kMailboxRootURI));
+
+ baseMessageURI = kMailboxMessageRootURI;
+ baseMessageURI += tailURI;
+
+ return NS_OK;
+}
+
+void nsEscapeNativePath(nsCString& nativePath)
+{
+#if defined(XP_WIN)
+ nativePath.Insert('/', 0);
+ MsgReplaceChar(nativePath, '\\', '/');
+#endif
+}
diff --git a/mailnews/local/src/nsLocalUtils.h b/mailnews/local/src/nsLocalUtils.h
new file mode 100644
index 000000000..4419e6baa
--- /dev/null
+++ b/mailnews/local/src/nsLocalUtils.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef NS_LOCALUTILS_H
+#define NS_LOCALUTILS_H
+
+#include "nsStringGlue.h"
+#include "nsIMsgIncomingServer.h"
+
+static const char kMailboxRootURI[] = "mailbox:/";
+static const char kMailboxMessageRootURI[] = "mailbox-message:/";
+
+nsresult
+nsLocalURI2Path(const char* rootURI, const char* uriStr, nsCString& pathResult);
+
+nsresult
+nsParseLocalMessageURI(const char* uri, nsCString& folderURI, nsMsgKey *key);
+
+nsresult
+nsBuildLocalMessageURI(const char* baseURI, nsMsgKey key, nsCString& uri);
+
+nsresult
+nsCreateLocalBaseMessageURI(const nsACString& baseURI, nsCString &baseMessageURI);
+
+void
+nsEscapeNativePath(nsCString& nativePath);
+
+#endif //NS_LOCALUTILS_H
diff --git a/mailnews/local/src/nsMailboxProtocol.cpp b/mailnews/local/src/nsMailboxProtocol.cpp
new file mode 100644
index 000000000..fad77aa93
--- /dev/null
+++ b/mailnews/local/src/nsMailboxProtocol.cpp
@@ -0,0 +1,725 @@
+/* -*- Mode: C++; tab-width: 4; 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"
+
+#include "nsMailboxProtocol.h"
+#include "nscore.h"
+#include "nsIOutputStream.h"
+#include "nsIInputStreamPump.h"
+#include "nsIMsgDatabase.h"
+#include "nsIMsgHdr.h"
+#include "nsMsgLineBuffer.h"
+#include "nsMsgDBCID.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsICopyMsgStreamListener.h"
+#include "nsMsgMessageFlags.h"
+#include "prtime.h"
+#include "mozilla/Logging.h"
+#include "prerror.h"
+#include "prprf.h"
+#include "nspr.h"
+
+PRLogModuleInfo *MAILBOX;
+#include "nsIFileStreams.h"
+#include "nsIStreamTransportService.h"
+#include "nsIStreamConverterService.h"
+#include "nsIIOService.h"
+#include "nsNetUtil.h"
+#include "nsMsgUtils.h"
+#include "nsIMsgWindow.h"
+#include "nsIMimeHeaders.h"
+#include "nsIMsgPluggableStore.h"
+#include "nsISeekableStream.h"
+
+#include "nsIMsgMdnGenerator.h"
+
+/* the output_buffer_size must be larger than the largest possible line
+ * 2000 seems good for news
+ *
+ * jwz: I increased this to 4k since it must be big enough to hold the
+ * entire button-bar HTML, and with the new "mailto" format, that can
+ * contain arbitrarily long header fields like "references".
+ *
+ * fortezza: proxy auth is huge, buffer increased to 8k (sigh).
+ */
+#define OUTPUT_BUFFER_SIZE (4096*2)
+
+nsMailboxProtocol::nsMailboxProtocol(nsIURI * aURI)
+ : nsMsgProtocol(aURI)
+{
+ m_lineStreamBuffer =nullptr;
+
+ // initialize the pr log if it hasn't been initialiezed already
+ if (!MAILBOX)
+ MAILBOX = PR_NewLogModule("MAILBOX");
+}
+
+nsMailboxProtocol::~nsMailboxProtocol()
+{
+ // free our local state
+ delete m_lineStreamBuffer;
+}
+
+nsresult nsMailboxProtocol::OpenMultipleMsgTransport(uint64_t offset, int32_t size)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIStreamTransportService> serv =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // XXX 64-bit
+ rv = serv->CreateInputTransport(m_multipleMsgMoveCopyStream, int64_t(offset),
+ int64_t(size), false,
+ getter_AddRefs(m_transport));
+
+ return rv;
+}
+
+nsresult nsMailboxProtocol::Initialize(nsIURI * aURL)
+{
+ NS_PRECONDITION(aURL, "invalid URL passed into MAILBOX Protocol");
+ nsresult rv = NS_OK;
+ if (aURL)
+ {
+ rv = aURL->QueryInterface(NS_GET_IID(nsIMailboxUrl), (void **) getter_AddRefs(m_runningUrl));
+ if (NS_SUCCEEDED(rv) && m_runningUrl)
+ {
+ nsCOMPtr <nsIMsgWindow> window;
+ rv = m_runningUrl->GetMailboxAction(&m_mailboxAction);
+ // clear stopped flag on msg window, because we care.
+ nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
+ if (mailnewsUrl)
+ {
+ mailnewsUrl->GetMsgWindow(getter_AddRefs(window));
+ if (window)
+ window->SetStopped(false);
+ }
+
+ if (m_mailboxAction == nsIMailboxUrl::ActionParseMailbox)
+ {
+ // Set the length of the file equal to the max progress
+ nsCOMPtr<nsIFile> file;
+ GetFileFromURL(aURL, getter_AddRefs(file));
+ if (file)
+ {
+ int64_t fileSize = 0;
+ file->GetFileSize(&fileSize);
+ mailnewsUrl->SetMaxProgress(fileSize);
+ }
+
+ rv = OpenFileSocket(aURL, 0, -1 /* read in all the bytes in the file */);
+ }
+ else
+ {
+ // we need to specify a byte range to read in so we read in JUST the message we want.
+ rv = SetupMessageExtraction();
+ if (NS_FAILED(rv)) return rv;
+ uint32_t aMsgSize = 0;
+ rv = m_runningUrl->GetMessageSize(&aMsgSize);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "oops....i messed something up");
+ SetContentLength(aMsgSize);
+ mailnewsUrl->SetMaxProgress(aMsgSize);
+
+ if (RunningMultipleMsgUrl())
+ {
+ // if we're running multiple msg url, we clear the event sink because the multiple
+ // msg urls will handle setting the progress.
+ mProgressEventSink = nullptr;
+ }
+
+ nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(m_runningUrl, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ rv = msgUrl->GetMessageHeader(getter_AddRefs(msgHdr));
+ if (NS_SUCCEEDED(rv) && msgHdr)
+ {
+ rv = msgHdr->GetFolder(getter_AddRefs(folder));
+ if (NS_SUCCEEDED(rv) && folder)
+ {
+ nsCOMPtr<nsIInputStream> stream;
+ int64_t offset = 0;
+ bool reusable = false;
+
+ rv = folder->GetMsgInputStream(msgHdr, &reusable, getter_AddRefs(stream));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsISeekableStream> seekableStream(do_QueryInterface(stream, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ seekableStream->Tell(&offset);
+ // create input stream transport
+ nsCOMPtr<nsIStreamTransportService> sts =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+ m_readCount = aMsgSize;
+ // Save the stream for reuse, but only for multiple URLs.
+ if (reusable && RunningMultipleMsgUrl())
+ m_multipleMsgMoveCopyStream = stream;
+ else
+ reusable = false;
+ rv = sts->CreateInputTransport(stream, offset,
+ int64_t(aMsgSize), !reusable,
+ getter_AddRefs(m_transport));
+
+ m_socketIsOpen = false;
+ }
+ }
+ if (!folder) // must be a .eml file
+ rv = OpenFileSocket(aURL, 0, aMsgSize);
+ }
+ NS_ASSERTION(NS_SUCCEEDED(rv), "oops....i messed something up");
+ }
+ }
+ }
+
+ m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, true);
+
+ m_nextState = MAILBOX_READ_FOLDER;
+ m_initialState = MAILBOX_READ_FOLDER;
+ mCurrentProgress = 0;
+
+ // do we really need both?
+ m_tempMessageFile = m_tempMsgFile;
+ return rv;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+// we suppport the nsIStreamListener interface
+////////////////////////////////////////////////////////////////////////////////////////////
+
+NS_IMETHODIMP nsMailboxProtocol::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
+{
+ // extract the appropriate event sinks from the url and initialize them in our protocol data
+ // the URL should be queried for a nsINewsURL. If it doesn't support a news URL interface then
+ // we have an error.
+ if (m_nextState == MAILBOX_READ_FOLDER && m_mailboxParser)
+ {
+ // we need to inform our mailbox parser that it's time to start...
+ m_mailboxParser->OnStartRequest(request, ctxt);
+ }
+ return nsMsgProtocol::OnStartRequest(request, ctxt);
+}
+
+bool nsMailboxProtocol::RunningMultipleMsgUrl()
+{
+ if (m_mailboxAction == nsIMailboxUrl::ActionCopyMessage || m_mailboxAction == nsIMailboxUrl::ActionMoveMessage)
+ {
+ uint32_t numMoveCopyMsgs;
+ nsresult rv = m_runningUrl->GetNumMoveCopyMsgs(&numMoveCopyMsgs);
+ if (NS_SUCCEEDED(rv) && numMoveCopyMsgs > 1)
+ return true;
+ }
+ return false;
+}
+
+// stop binding is a "notification" informing us that the stream associated with aURL is going away.
+NS_IMETHODIMP nsMailboxProtocol::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult aStatus)
+{
+ nsresult rv;
+ if (m_nextState == MAILBOX_READ_FOLDER && m_mailboxParser)
+ {
+ // we need to inform our mailbox parser that there is no more incoming data...
+ m_mailboxParser->OnStopRequest(request, ctxt, aStatus);
+ }
+ else if (m_nextState == MAILBOX_READ_MESSAGE)
+ {
+ DoneReadingMessage();
+ }
+ // I'm not getting cancel status - maybe the load group still has the status.
+ bool stopped = false;
+ if (m_runningUrl)
+ {
+ nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_runningUrl);
+ if (mailnewsUrl)
+ {
+ nsCOMPtr <nsIMsgWindow> window;
+ mailnewsUrl->GetMsgWindow(getter_AddRefs(window));
+ if (window)
+ window->GetStopped(&stopped);
+ }
+
+ if (!stopped && NS_SUCCEEDED(aStatus) && (m_mailboxAction == nsIMailboxUrl::ActionCopyMessage || m_mailboxAction == nsIMailboxUrl::ActionMoveMessage))
+ {
+ uint32_t numMoveCopyMsgs;
+ uint32_t curMoveCopyMsgIndex;
+ rv = m_runningUrl->GetNumMoveCopyMsgs(&numMoveCopyMsgs);
+ if (NS_SUCCEEDED(rv) && numMoveCopyMsgs > 0)
+ {
+ m_runningUrl->GetCurMoveCopyMsgIndex(&curMoveCopyMsgIndex);
+ if (++curMoveCopyMsgIndex < numMoveCopyMsgs)
+ {
+ if (!mSuppressListenerNotifications && m_channelListener)
+ {
+ nsCOMPtr<nsICopyMessageStreamListener> listener = do_QueryInterface(m_channelListener, &rv);
+ if (listener)
+ {
+ listener->EndCopy(ctxt, aStatus);
+ listener->StartMessage(); // start next message.
+ }
+ }
+ m_runningUrl->SetCurMoveCopyMsgIndex(curMoveCopyMsgIndex);
+ nsCOMPtr <nsIMsgDBHdr> nextMsg;
+ rv = m_runningUrl->GetMoveCopyMsgHdrForIndex(curMoveCopyMsgIndex, getter_AddRefs(nextMsg));
+ if (NS_SUCCEEDED(rv) && nextMsg)
+ {
+ uint32_t msgSize = 0;
+ nsCOMPtr <nsIMsgFolder> msgFolder;
+ nextMsg->GetFolder(getter_AddRefs(msgFolder));
+ NS_ASSERTION(msgFolder, "couldn't get folder for next msg in multiple msg local copy");
+ if (msgFolder)
+ {
+ nsCString uri;
+ msgFolder->GetUriForMsg(nextMsg, uri);
+ nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(m_runningUrl);
+ if (msgUrl)
+ {
+ msgUrl->SetOriginalSpec(uri.get());
+ msgUrl->SetUri(uri.get());
+
+ uint64_t msgOffset;
+ nextMsg->GetMessageOffset(&msgOffset);
+ nextMsg->GetMessageSize(&msgSize);
+ // now we have to seek to the right position in the file and
+ // basically re-initialize the transport with the correct message size.
+ // then, we have to make sure the url keeps running somehow.
+ nsCOMPtr<nsISupports> urlSupports = do_QueryInterface(m_runningUrl);
+ //
+ // put us in a state where we are always notified of incoming data
+ //
+
+ m_transport = nullptr; // open new stream transport
+ m_inputStream = nullptr;
+ m_outputStream = nullptr;
+
+ if (m_multipleMsgMoveCopyStream)
+ {
+ rv = OpenMultipleMsgTransport(msgOffset, msgSize);
+ }
+ else
+ {
+ nsCOMPtr<nsIInputStream> stream;
+ bool reusable = false;
+ rv = msgFolder->GetMsgInputStream(nextMsg, &reusable,
+ getter_AddRefs(stream));
+ NS_ASSERTION(!reusable, "We thought streams were not reusable!");
+
+ if (NS_SUCCEEDED(rv))
+ {
+ // create input stream transport
+ nsCOMPtr<nsIStreamTransportService> sts =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ m_readCount = msgSize;
+ rv = sts->CreateInputTransport(stream, msgOffset,
+ int64_t(msgSize), true,
+ getter_AddRefs(m_transport));
+ }
+ }
+ }
+
+ if (NS_SUCCEEDED(rv))
+ {
+ if (!m_inputStream)
+ rv = m_transport->OpenInputStream(0, 0, 0, getter_AddRefs(m_inputStream));
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIInputStreamPump> pump;
+ rv = NS_NewInputStreamPump(getter_AddRefs(pump), m_inputStream);
+ if (NS_SUCCEEDED(rv)) {
+ rv = pump->AsyncRead(this, urlSupports);
+ if (NS_SUCCEEDED(rv))
+ m_request = pump;
+ }
+ }
+ }
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncRead failed");
+ if (m_loadGroup)
+ m_loadGroup->RemoveRequest(static_cast<nsIRequest *>(this), nullptr, aStatus);
+ m_socketIsOpen = true; // mark the channel as open
+ return aStatus;
+ }
+ }
+ }
+ }
+ else
+ {
+ }
+ }
+ }
+ }
+ // and we want to mark ourselves for deletion or some how inform our protocol manager that we are
+ // available for another url if there is one.
+
+ // mscott --> maybe we should set our state to done because we don't run multiple urls in a mailbox
+ // protocol connection....
+ m_nextState = MAILBOX_DONE;
+
+ // the following is for smoke test purposes. QA is looking at this "Mailbox Done" string which
+ // is printed out to the console and determining if the mail app loaded up correctly...obviously
+ // this solution is not very good so we should look at something better, but don't remove this
+ // line before talking to me (mscott) and mailnews QA....
+
+ MOZ_LOG(MAILBOX, mozilla::LogLevel::Info, ("Mailbox Done\n"));
+
+ // when on stop binding is called, we as the protocol are done...let's close down the connection
+ // releasing all of our interfaces. It's important to remember that this on stop binding call
+ // is coming from netlib so they are never going to ping us again with on data available. This means
+ // we'll never be going through the Process loop...
+
+ if (m_multipleMsgMoveCopyStream)
+ {
+ m_multipleMsgMoveCopyStream->Close();
+ m_multipleMsgMoveCopyStream = nullptr;
+ }
+ nsMsgProtocol::OnStopRequest(request, ctxt, aStatus);
+ return CloseSocket();
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+// End of nsIStreamListenerSupport
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+nsresult nsMailboxProtocol::DoneReadingMessage()
+{
+ nsresult rv = NS_OK;
+ // and close the article file if it was open....
+
+ if (m_mailboxAction == nsIMailboxUrl::ActionSaveMessageToDisk && m_msgFileOutputStream)
+ rv = m_msgFileOutputStream->Close();
+
+ return rv;
+}
+
+nsresult nsMailboxProtocol::SetupMessageExtraction()
+{
+ // Determine the number of bytes we are going to need to read out of the
+ // mailbox url....
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ nsresult rv = NS_OK;
+
+ NS_ASSERTION(m_runningUrl, "Not running a url");
+ if (m_runningUrl)
+ {
+ uint32_t messageSize = 0;
+ m_runningUrl->GetMessageSize(&messageSize);
+ if (!messageSize)
+ {
+ nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(m_runningUrl, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ rv = msgUrl->GetMessageHeader(getter_AddRefs(msgHdr));
+ if (NS_SUCCEEDED(rv) && msgHdr)
+ {
+ msgHdr->GetMessageSize(&messageSize);
+ m_runningUrl->SetMessageSize(messageSize);
+ msgHdr->GetMessageOffset(&m_msgOffset);
+ }
+ else
+ NS_ASSERTION(false, "couldn't get message header");
+ }
+ }
+ return rv;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////
+// Begin protocol state machine functions...
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+nsresult nsMailboxProtocol::LoadUrl(nsIURI * aURL, nsISupports * aConsumer)
+{
+ nsresult rv = NS_OK;
+ // if we were already initialized with a consumer, use it...
+ nsCOMPtr<nsIStreamListener> consumer = do_QueryInterface(aConsumer);
+ if (consumer)
+ m_channelListener = consumer;
+
+ if (aURL)
+ {
+ m_runningUrl = do_QueryInterface(aURL);
+ if (m_runningUrl)
+ {
+ // find out from the url what action we are supposed to perform...
+ rv = m_runningUrl->GetMailboxAction(&m_mailboxAction);
+
+ bool convertData = false;
+
+ // need to check if we're fetching an rfc822 part in order to
+ // quote a message.
+ if (m_mailboxAction == nsIMailboxUrl::ActionFetchMessage)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> msgUrl = do_QueryInterface(m_runningUrl, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsAutoCString queryStr;
+ rv = msgUrl->GetQuery(queryStr);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ // check if this is a filter plugin requesting the message.
+ // in that case, set up a text converter
+ convertData = (queryStr.Find("header=filter") != -1 ||
+ queryStr.Find("header=attach") != -1);
+ }
+ else if (m_mailboxAction == nsIMailboxUrl::ActionFetchPart)
+ {
+ // when fetching a part, we need to insert a converter into the listener chain order to
+ // force just the part out of the message. Our channel listener is the consumer we'll
+ // pass in to AsyncConvertData.
+ convertData = true;
+ consumer = m_channelListener;
+ }
+ if (convertData)
+ {
+ nsCOMPtr<nsIStreamConverterService> streamConverter = do_GetService("@mozilla.org/streamConverters;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIStreamListener> conversionListener;
+ nsCOMPtr<nsIChannel> channel;
+ QueryInterface(NS_GET_IID(nsIChannel), getter_AddRefs(channel));
+
+ rv = streamConverter->AsyncConvertData("message/rfc822",
+ "*/*",
+ consumer, channel, getter_AddRefs(m_channelListener));
+ }
+
+ if (NS_SUCCEEDED(rv))
+ {
+ switch (m_mailboxAction)
+ {
+ case nsIMailboxUrl::ActionParseMailbox:
+ // extract the mailbox parser..
+ rv = m_runningUrl->GetMailboxParser(getter_AddRefs(m_mailboxParser));
+ m_nextState = MAILBOX_READ_FOLDER;
+ break;
+ case nsIMailboxUrl::ActionSaveMessageToDisk:
+ // ohhh, display message already writes a msg to disk (as part of a hack)
+ // so we can piggy back off of that!! We just need to change m_tempMessageFile
+ // to be the name of our save message to disk file. Since save message to disk
+ // urls are run without a docshell to display the msg into, we won't be trying
+ // to display the message after we write it to disk...
+ {
+ nsCOMPtr<nsIMsgMessageUrl> messageUrl = do_QueryInterface(m_runningUrl, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ messageUrl->GetMessageFile(getter_AddRefs(m_tempMessageFile));
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(m_msgFileOutputStream), m_tempMessageFile, -1, 00600);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool addDummyEnvelope = false;
+ messageUrl->GetAddDummyEnvelope(&addDummyEnvelope);
+ if (addDummyEnvelope)
+ SetFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
+ else
+ ClearFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
+ }
+ }
+ m_nextState = MAILBOX_READ_MESSAGE;
+ break;
+ case nsIMailboxUrl::ActionCopyMessage:
+ case nsIMailboxUrl::ActionMoveMessage:
+ case nsIMailboxUrl::ActionFetchMessage:
+ ClearFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
+ m_nextState = MAILBOX_READ_MESSAGE;
+ break;
+ case nsIMailboxUrl::ActionFetchPart:
+ m_nextState = MAILBOX_READ_MESSAGE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ rv = nsMsgProtocol::LoadUrl(aURL, m_channelListener);
+
+ } // if we received an MAILBOX url...
+ } // if we received a url!
+
+ return rv;
+}
+
+int32_t nsMailboxProtocol::ReadFolderResponse(nsIInputStream * inputStream, uint64_t sourceOffset, uint32_t length)
+{
+ // okay we are doing a folder read in 8K chunks of a mail folder....
+ // this is almost too easy....we can just forward the data in this stream on to our
+ // folder parser object!!!
+
+ nsresult rv = NS_OK;
+ mCurrentProgress += length;
+
+ if (m_mailboxParser)
+ {
+ nsCOMPtr <nsIURI> url = do_QueryInterface(m_runningUrl);
+ rv = m_mailboxParser->OnDataAvailable(nullptr, url, inputStream, sourceOffset, length); // let the parser deal with it...
+ }
+ if (NS_FAILED(rv))
+ {
+ m_nextState = MAILBOX_ERROR_DONE; // drop out of the loop....
+ return -1;
+ }
+
+ // now wait for the next 8K chunk to come in.....
+ SetFlag(MAILBOX_PAUSE_FOR_READ);
+
+ // leave our state alone so when the next chunk of the mailbox comes in we jump to this state
+ // and repeat....how does this process end? Well when the file is done being read in, core net lib
+ // will issue an ::OnStopRequest to us...we'll use that as our sign to drop out of this state and to
+ // close the protocol instance...
+
+ return 0;
+}
+
+int32_t nsMailboxProtocol::ReadMessageResponse(nsIInputStream * inputStream, uint64_t sourceOffset, uint32_t length)
+{
+ char *line = nullptr;
+ uint32_t status = 0;
+ nsresult rv = NS_OK;
+ mCurrentProgress += length;
+
+ // if we are doing a move or a copy, forward the data onto the copy handler...
+ // if we want to display the message then parse the incoming data...
+
+ if (m_channelListener)
+ {
+ // just forward the data we read in to the listener...
+ rv = m_channelListener->OnDataAvailable(this, m_channelContext, inputStream, sourceOffset, length);
+ }
+ else
+ {
+ bool pauseForMoreData = false;
+ bool canonicalLineEnding = false;
+ nsCOMPtr<nsIMsgMessageUrl> msgurl = do_QueryInterface(m_runningUrl);
+
+ if (msgurl)
+ msgurl->GetCanonicalLineEnding(&canonicalLineEnding);
+
+ while ((line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData)) &&
+ !pauseForMoreData)
+ {
+ /* When we're sending this line to a converter (ie,
+ it's a message/rfc822) use the local line termination
+ convention, not CRLF. This makes text articles get
+ saved with the local line terminators. Since SMTP
+ and NNTP mandate the use of CRLF, it is expected that
+ the local system will convert that to the local line
+ terminator as it is read.
+ */
+ // mscott - the firstline hack is aimed at making sure we don't write
+ // out the dummy header when we are trying to display the message.
+ // The dummy header is the From line with the date tag on it.
+ if (m_msgFileOutputStream && TestFlag(MAILBOX_MSG_PARSE_FIRST_LINE))
+ {
+ uint32_t count = 0;
+ rv = m_msgFileOutputStream->Write(line, PL_strlen(line), &count);
+ if (NS_FAILED(rv))
+ break;
+
+ if (canonicalLineEnding)
+ rv = m_msgFileOutputStream->Write(CRLF, 2, &count);
+ else
+ rv = m_msgFileOutputStream->Write(MSG_LINEBREAK,
+ MSG_LINEBREAK_LEN, &count);
+
+ if (NS_FAILED(rv))
+ break;
+ }
+ else
+ SetFlag(MAILBOX_MSG_PARSE_FIRST_LINE);
+ PR_Free(line);
+ }
+ PR_Free(line);
+ }
+
+ SetFlag(MAILBOX_PAUSE_FOR_READ); // wait for more data to become available...
+ if (mProgressEventSink && m_runningUrl)
+ {
+ int64_t maxProgress;
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl(do_QueryInterface(m_runningUrl));
+ mailnewsUrl->GetMaxProgress(&maxProgress);
+ mProgressEventSink->OnProgress(this, m_channelContext,
+ mCurrentProgress,
+ maxProgress);
+ }
+
+ if (NS_FAILED(rv)) return -1;
+
+ return 0;
+}
+
+
+/*
+ * returns negative if the transfer is finished or error'd out
+ *
+ * returns zero or more if the transfer needs to be continued.
+ */
+nsresult nsMailboxProtocol::ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream, uint64_t offset, uint32_t length)
+{
+ nsresult rv = NS_OK;
+ int32_t status = 0;
+ ClearFlag(MAILBOX_PAUSE_FOR_READ); /* already paused; reset */
+
+ while(!TestFlag(MAILBOX_PAUSE_FOR_READ))
+ {
+
+ switch(m_nextState)
+ {
+ case MAILBOX_READ_MESSAGE:
+ if (inputStream == nullptr)
+ SetFlag(MAILBOX_PAUSE_FOR_READ);
+ else
+ status = ReadMessageResponse(inputStream, offset, length);
+ break;
+ case MAILBOX_READ_FOLDER:
+ if (inputStream == nullptr)
+ SetFlag(MAILBOX_PAUSE_FOR_READ); // wait for file socket to read in the next chunk...
+ else
+ status = ReadFolderResponse(inputStream, offset, length);
+ break;
+ case MAILBOX_DONE:
+ case MAILBOX_ERROR_DONE:
+ {
+ nsCOMPtr <nsIMsgMailNewsUrl> anotherUrl = do_QueryInterface(m_runningUrl);
+ rv = m_nextState == MAILBOX_DONE ? NS_OK : NS_ERROR_FAILURE;
+ anotherUrl->SetUrlState(false, rv);
+ m_nextState = MAILBOX_FREE;
+ }
+ break;
+
+ case MAILBOX_FREE:
+ // MAILBOX is a one time use connection so kill it if we get here...
+ CloseSocket();
+ return rv; /* final end */
+
+ default: /* should never happen !!! */
+ m_nextState = MAILBOX_ERROR_DONE;
+ break;
+ }
+
+ /* check for errors during load and call error
+ * state if found
+ */
+ if(status < 0 && m_nextState != MAILBOX_FREE)
+ {
+ m_nextState = MAILBOX_ERROR_DONE;
+ /* don't exit! loop around again and do the free case */
+ ClearFlag(MAILBOX_PAUSE_FOR_READ);
+ }
+ } /* while(!MAILBOX_PAUSE_FOR_READ) */
+
+ return rv;
+}
+
+nsresult nsMailboxProtocol::CloseSocket()
+{
+ // how do you force a release when closing the connection??
+ nsMsgProtocol::CloseSocket();
+ m_runningUrl = nullptr;
+ m_mailboxParser = nullptr;
+ return NS_OK;
+}
+
+// vim: ts=2 sw=2
diff --git a/mailnews/local/src/nsMailboxProtocol.h b/mailnews/local/src/nsMailboxProtocol.h
new file mode 100644
index 000000000..53c65b40f
--- /dev/null
+++ b/mailnews/local/src/nsMailboxProtocol.h
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef nsMailboxProtocol_h___
+#define nsMailboxProtocol_h___
+
+#include "mozilla/Attributes.h"
+#include "nsMsgProtocol.h"
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "nsIChannel.h"
+#include "nsIInputStreamPump.h"
+#include "nsIOutputStream.h"
+#include "nsIMailboxUrl.h"
+// State Flags (Note, I use the word state in terms of storing
+// state information about the connection (authentication, have we sent
+// commands, etc. I do not intend it to refer to protocol state)
+
+#define MAILBOX_PAUSE_FOR_READ 0x00000001 /* should we pause for the next read */
+#define MAILBOX_MSG_PARSE_FIRST_LINE 0x00000002 /* have we read in the first line of the msg */
+
+/* states of the machine
+ */
+typedef enum _MailboxStatesEnum {
+ MAILBOX_READ_FOLDER,
+ MAILBOX_FINISH_OPEN_FOLDER,
+ MAILBOX_OPEN_MESSAGE,
+ MAILBOX_OPEN_STREAM,
+ MAILBOX_READ_MESSAGE,
+ MAILBOX_COMPRESS_FOLDER,
+ MAILBOX_FINISH_COMPRESS_FOLDER,
+ MAILBOX_BACKGROUND,
+ MAILBOX_NULL,
+ MAILBOX_NULL2,
+ MAILBOX_DELIVER_QUEUED,
+ MAILBOX_FINISH_DELIVER_QUEUED,
+ MAILBOX_DONE,
+ MAILBOX_ERROR_DONE,
+ MAILBOX_FREE,
+ MAILBOX_COPY_MESSAGES,
+ MAILBOX_FINISH_COPY_MESSAGES
+} MailboxStatesEnum;
+
+class nsMsgLineStreamBuffer;
+
+class nsMailboxProtocol : public nsMsgProtocol
+{
+public:
+ // Creating a protocol instance requires the URL which needs to be run AND it requires
+ // a transport layer.
+ nsMailboxProtocol(nsIURI * aURL);
+ virtual ~nsMailboxProtocol();
+
+ // initialization function given a new url and transport layer
+ nsresult Initialize(nsIURI * aURL);
+
+ // the consumer of the url might be something like an nsIDocShell....
+ virtual nsresult LoadUrl(nsIURI * aURL, nsISupports * aConsumer) override;
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // we suppport the nsIStreamListener interface
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ NS_IMETHOD OnStartRequest(nsIRequest *request, nsISupports *ctxt) override;
+ NS_IMETHOD OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult aStatus) override;
+
+private:
+ nsCOMPtr<nsIMailboxUrl> m_runningUrl; // the nsIMailboxURL that is currently running
+ nsMailboxAction m_mailboxAction; // current mailbox action associated with this connnection...
+ uint64_t m_msgOffset;
+ // Event sink handles
+ nsCOMPtr<nsIStreamListener> m_mailboxParser;
+
+ // Local state for the current operation
+ nsMsgLineStreamBuffer * m_lineStreamBuffer; // used to efficiently extract lines from the incoming data stream
+
+ // Generic state information -- What state are we in? What state do we want to go to
+ // after the next response? What was the last response code? etc.
+ MailboxStatesEnum m_nextState;
+ MailboxStatesEnum m_initialState;
+
+ int64_t mCurrentProgress;
+
+ // can we just use the base class m_tempMsgFile?
+ nsCOMPtr<nsIFile> m_tempMessageFile;
+ nsCOMPtr<nsIOutputStream> m_msgFileOutputStream;
+
+ // this is used to hold the source mailbox file open when move/copying
+ // multiple messages.
+ nsCOMPtr<nsIInputStream> m_multipleMsgMoveCopyStream;
+
+ virtual nsresult ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream,
+ uint64_t sourceOffset, uint32_t length) override;
+ virtual nsresult CloseSocket() override;
+
+ nsresult SetupMessageExtraction();
+ nsresult OpenMultipleMsgTransport(uint64_t offset, int32_t size);
+ bool RunningMultipleMsgUrl();
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // Protocol Methods --> This protocol is state driven so each protocol method is
+ // designed to re-act to the current "state". I've attempted to
+ // group them together based on functionality.
+ ////////////////////////////////////////////////////////////////////////////////////////
+
+ // When parsing a mailbox folder in chunks, this protocol state reads in the current chunk
+ // and forwards it to the mailbox parser.
+ int32_t ReadFolderResponse(nsIInputStream * inputStream, uint64_t sourceOffset, uint32_t length);
+ int32_t ReadMessageResponse(nsIInputStream * inputStream, uint64_t sourceOffset, uint32_t length);
+ nsresult DoneReadingMessage();
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // End of Protocol Methods
+ ////////////////////////////////////////////////////////////////////////////////////////
+};
+
+#endif // nsMailboxProtocol_h___
+
+
+
+
+
+
diff --git a/mailnews/local/src/nsMailboxServer.cpp b/mailnews/local/src/nsMailboxServer.cpp
new file mode 100644
index 000000000..005204f0d
--- /dev/null
+++ b/mailnews/local/src/nsMailboxServer.cpp
@@ -0,0 +1,33 @@
+/**
+ * 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 "nsMailboxServer.h"
+#include "nsLocalMailFolder.h"
+
+NS_IMETHODIMP
+nsMailboxServer::GetLocalStoreType(nsACString& type)
+{
+ type.AssignLiteral("mailbox");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMailboxServer::GetLocalDatabaseType(nsACString& type)
+{
+ type.AssignLiteral("mailbox");
+ return NS_OK;
+}
+
+nsresult
+nsMailboxServer::CreateRootFolderFromUri(const nsCString &serverUri,
+ nsIMsgFolder **rootFolder)
+{
+ nsMsgLocalMailFolder *newRootFolder = new nsMsgLocalMailFolder;
+ if (!newRootFolder)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(*rootFolder = newRootFolder);
+ newRootFolder->Init(serverUri.get());
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsMailboxServer.h b/mailnews/local/src/nsMailboxServer.h
new file mode 100644
index 000000000..70e914f52
--- /dev/null
+++ b/mailnews/local/src/nsMailboxServer.h
@@ -0,0 +1,22 @@
+/**
+ * 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/. */
+
+#ifndef nsMailboxServer_h__
+#define nsMailboxServer_h__
+
+#include "mozilla/Attributes.h"
+#include "nsMsgIncomingServer.h"
+
+class nsMailboxServer : public nsMsgIncomingServer
+{
+public:
+ NS_IMETHOD GetLocalStoreType(nsACString& type) override;
+ NS_IMETHOD GetLocalDatabaseType(nsACString& type) override;
+protected:
+ virtual nsresult CreateRootFolderFromUri(const nsCString &serverUri,
+ nsIMsgFolder **rootFolder) override;
+};
+
+#endif
diff --git a/mailnews/local/src/nsMailboxService.cpp b/mailnews/local/src/nsMailboxService.cpp
new file mode 100644
index 000000000..00a0d87c8
--- /dev/null
+++ b/mailnews/local/src/nsMailboxService.cpp
@@ -0,0 +1,677 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsCOMPtr.h"
+
+#include "nsMailboxService.h"
+#include "nsMailboxUrl.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsMailboxProtocol.h"
+#include "nsIMsgDatabase.h"
+#include "nsMsgDBCID.h"
+#include "MailNewsTypes.h"
+#include "nsTArray.h"
+#include "nsLocalUtils.h"
+#include "nsMsgLocalCID.h"
+#include "nsMsgBaseCID.h"
+#include "nsIDocShell.h"
+#include "nsIPop3Service.h"
+#include "nsMsgUtils.h"
+#include "nsNetUtil.h"
+#include "nsIDocShellLoadInfo.h"
+#include "nsIWebNavigation.h"
+#include "prprf.h"
+#include "nsIMsgHdr.h"
+#include "nsIFileURL.h"
+#include "mozilla/RefPtr.h"
+
+nsMailboxService::nsMailboxService()
+{
+ mPrintingOperation = false;
+}
+
+nsMailboxService::~nsMailboxService()
+{}
+
+NS_IMPL_ISUPPORTS(nsMailboxService, nsIMailboxService, nsIMsgMessageService, nsIProtocolHandler, nsIMsgMessageFetchPartService)
+
+nsresult nsMailboxService::ParseMailbox(nsIMsgWindow *aMsgWindow, nsIFile *aMailboxPath, nsIStreamListener *aMailboxParser,
+ nsIUrlListener * aUrlListener, nsIURI ** aURL)
+{
+ NS_ENSURE_ARG_POINTER(aMailboxPath);
+
+ nsresult rv;
+ nsCOMPtr<nsIMailboxUrl> mailboxurl =
+ do_CreateInstance(NS_MAILBOXURL_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && mailboxurl)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> url = do_QueryInterface(mailboxurl);
+ // okay now generate the url string
+ nsCString mailboxPath;
+
+ aMailboxPath->GetNativePath(mailboxPath);
+ nsAutoCString buf;
+ MsgEscapeURL(mailboxPath,
+ nsINetUtil::ESCAPE_URL_MINIMAL | nsINetUtil::ESCAPE_URL_FORCED, buf);
+ nsEscapeNativePath(buf);
+ url->SetUpdatingFolder(true);
+ url->SetMsgWindow(aMsgWindow);
+ nsAutoCString uriSpec("mailbox://");
+ uriSpec.Append(buf);
+ rv = url->SetSpec(uriSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mailboxurl->SetMailboxParser(aMailboxParser);
+ if (aUrlListener)
+ url->RegisterListener(aUrlListener);
+
+ rv = RunMailboxUrl(url, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aURL)
+ {
+ *aURL = url;
+ NS_IF_ADDREF(*aURL);
+ }
+ }
+
+ return rv;
+}
+
+nsresult nsMailboxService::CopyMessage(const char * aSrcMailboxURI,
+ nsIStreamListener * aMailboxCopyHandler,
+ bool moveMessage,
+ nsIUrlListener * aUrlListener,
+ nsIMsgWindow *aMsgWindow,
+ nsIURI **aURL)
+{
+ nsMailboxAction mailboxAction = nsIMailboxUrl::ActionMoveMessage;
+ if (!moveMessage)
+ mailboxAction = nsIMailboxUrl::ActionCopyMessage;
+ return FetchMessage(aSrcMailboxURI, aMailboxCopyHandler, aMsgWindow, aUrlListener, nullptr, mailboxAction, nullptr, aURL);
+}
+
+nsresult nsMailboxService::CopyMessages(uint32_t aNumKeys,
+ nsMsgKey* aMsgKeys,
+ nsIMsgFolder *srcFolder,
+ nsIStreamListener * aMailboxCopyHandler,
+ bool moveMessage,
+ nsIUrlListener * aUrlListener,
+ nsIMsgWindow *aMsgWindow,
+ nsIURI **aURL)
+{
+ nsresult rv = NS_OK;
+ NS_ENSURE_ARG(srcFolder);
+ NS_ENSURE_ARG(aMsgKeys);
+ nsCOMPtr<nsIMailboxUrl> mailboxurl;
+
+ nsMailboxAction actionToUse = nsIMailboxUrl::ActionMoveMessage;
+ if (!moveMessage)
+ actionToUse = nsIMailboxUrl::ActionCopyMessage;
+
+ nsCOMPtr <nsIMsgDBHdr> msgHdr;
+ nsCOMPtr <nsIMsgDatabase> db;
+ srcFolder->GetMsgDatabase(getter_AddRefs(db));
+ if (db)
+ {
+ db->GetMsgHdrForKey(aMsgKeys[0], getter_AddRefs(msgHdr));
+ if (msgHdr)
+ {
+ nsCString uri;
+ srcFolder->GetUriForMsg(msgHdr, uri);
+ rv = PrepareMessageUrl(uri.get(), aUrlListener, actionToUse , getter_AddRefs(mailboxurl), aMsgWindow);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIURI> url = do_QueryInterface(mailboxurl);
+ nsCOMPtr<nsIMsgMailNewsUrl> msgUrl (do_QueryInterface(url));
+ nsCOMPtr<nsIMailboxUrl> mailboxUrl (do_QueryInterface(url));
+ msgUrl->SetMsgWindow(aMsgWindow);
+
+ mailboxUrl->SetMoveCopyMsgKeys(aMsgKeys, aNumKeys);
+ rv = RunMailboxUrl(url, aMailboxCopyHandler);
+ }
+ }
+ }
+ if (aURL && mailboxurl)
+ CallQueryInterface(mailboxurl, aURL);
+
+ return rv;
+}
+
+nsresult nsMailboxService::FetchMessage(const char* aMessageURI,
+ nsISupports * aDisplayConsumer,
+ nsIMsgWindow * aMsgWindow,
+ nsIUrlListener * aUrlListener,
+ const char * aFileName, /* only used by open attachment... */
+ nsMailboxAction mailboxAction,
+ const char * aCharsetOverride,
+ nsIURI ** aURL)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMailboxUrl> mailboxurl;
+ nsMailboxAction actionToUse = mailboxAction;
+ nsCOMPtr<nsIURI> url;
+ nsCOMPtr<nsIMsgMailNewsUrl> msgUrl;
+ nsAutoCString uriString(aMessageURI);
+
+ if (!strncmp(aMessageURI, "file:", 5))
+ {
+ int64_t fileSize;
+ nsCOMPtr<nsIURI> fileUri;
+ rv = NS_NewURI(getter_AddRefs(fileUri), aMessageURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIFileURL> fileUrl = do_QueryInterface(fileUri, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIFile> file;
+ rv = fileUrl->GetFile(getter_AddRefs(file));
+ NS_ENSURE_SUCCESS(rv, rv);
+ file->GetFileSize(&fileSize);
+ uriString.Replace(0, 5, NS_LITERAL_CSTRING("mailbox:"));
+ uriString.Append(NS_LITERAL_CSTRING("&number=0"));
+ rv = NS_NewURI(getter_AddRefs(url), uriString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ msgUrl = do_QueryInterface(url);
+ if (msgUrl)
+ {
+ msgUrl->SetMsgWindow(aMsgWindow);
+ nsCOMPtr <nsIMailboxUrl> mailboxUrl = do_QueryInterface(msgUrl, &rv);
+ mailboxUrl->SetMessageSize((uint32_t) fileSize);
+ nsCOMPtr <nsIMsgHeaderSink> headerSink;
+ // need to tell the header sink to capture some headers to create a fake db header
+ // so we can do reply to a .eml file or a rfc822 msg attachment.
+ if (aMsgWindow)
+ aMsgWindow->GetMsgHeaderSink(getter_AddRefs(headerSink));
+ if (headerSink)
+ {
+ nsCOMPtr <nsIMsgDBHdr> dummyHeader;
+ headerSink->GetDummyMsgHeader(getter_AddRefs(dummyHeader));
+ if (dummyHeader)
+ dummyHeader->SetMessageSize((uint32_t) fileSize);
+ }
+ }
+ }
+ else
+ {
+ // this happens with forward inline of message/rfc822 attachment
+ // opened in a stand-alone msg window.
+ int32_t typeIndex = uriString.Find("&type=application/x-message-display");
+ if (typeIndex != -1)
+ {
+ uriString.Cut(typeIndex, sizeof("&type=application/x-message-display") - 1);
+ rv = NS_NewURI(getter_AddRefs(url), uriString.get());
+ mailboxurl = do_QueryInterface(url);
+ }
+ else
+ rv = PrepareMessageUrl(aMessageURI, aUrlListener, actionToUse , getter_AddRefs(mailboxurl), aMsgWindow);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ url = do_QueryInterface(mailboxurl);
+ msgUrl = do_QueryInterface(url);
+ msgUrl->SetMsgWindow(aMsgWindow);
+ if (aFileName)
+ msgUrl->SetFileName(nsDependentCString(aFileName));
+ }
+ }
+
+ nsCOMPtr<nsIMsgI18NUrl> i18nurl(do_QueryInterface(msgUrl));
+ if (i18nurl)
+ i18nurl->SetCharsetOverRide(aCharsetOverride);
+
+ // instead of running the mailbox url like we used to, let's try to run the url in the docshell...
+ nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aDisplayConsumer, &rv));
+ // if we were given a docShell, run the url in the docshell..otherwise just run it normally.
+ if (NS_SUCCEEDED(rv) && docShell)
+ {
+ nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+ // DIRTY LITTLE HACK --> if we are opening an attachment we want the docshell to
+ // treat this load as if it were a user click event. Then the dispatching stuff will be much
+ // happier.
+ if (mailboxAction == nsIMailboxUrl::ActionFetchPart)
+ {
+ docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
+ loadInfo->SetLoadType(nsIDocShellLoadInfo::loadLink);
+ }
+ rv = docShell->LoadURI(url, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, false);
+ }
+ else
+ rv = RunMailboxUrl(url, aDisplayConsumer);
+
+ if (aURL && mailboxurl)
+ CallQueryInterface(mailboxurl, aURL);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMailboxService::FetchMimePart(nsIURI *aURI, const char *aMessageURI, nsISupports *aDisplayConsumer, nsIMsgWindow *aMsgWindow, nsIUrlListener *aUrlListener, nsIURI **aURL)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgMailNewsUrl> msgUrl (do_QueryInterface(aURI, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ msgUrl->SetMsgWindow(aMsgWindow);
+
+ // set up the url listener
+ if (aUrlListener)
+ msgUrl->RegisterListener(aUrlListener);
+
+ return RunMailboxUrl(msgUrl, aDisplayConsumer);
+}
+
+NS_IMETHODIMP nsMailboxService::DisplayMessage(const char* aMessageURI,
+ nsISupports * aDisplayConsumer,
+ nsIMsgWindow * aMsgWindow,
+ nsIUrlListener * aUrlListener,
+ const char * aCharsetOveride,
+ nsIURI ** aURL)
+{
+ return FetchMessage(aMessageURI, aDisplayConsumer,
+ aMsgWindow,aUrlListener, nullptr,
+ nsIMailboxUrl::ActionFetchMessage, aCharsetOveride, aURL);
+}
+
+NS_IMETHODIMP
+nsMailboxService::StreamMessage(const char *aMessageURI,
+ nsISupports *aConsumer,
+ nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aUrlListener,
+ bool /* aConvertData */,
+ const nsACString &aAdditionalHeader,
+ bool aLocalOnly,
+ nsIURI **aURL)
+{
+ // The mailbox protocol object will look for "header=filter" or
+ // "header=attach" to decide if it wants to convert the data instead of
+ // using aConvertData. It turns out to be way too hard to pass aConvertData
+ // all the way over to the mailbox protocol object.
+ nsAutoCString aURIString(aMessageURI);
+ if (!aAdditionalHeader.IsEmpty())
+ {
+ aURIString.FindChar('?') == -1 ? aURIString += "?" : aURIString += "&";
+ aURIString += "header=";
+ aURIString += aAdditionalHeader;
+ }
+
+ return FetchMessage(aURIString.get(), aConsumer, aMsgWindow, aUrlListener, nullptr,
+ nsIMailboxUrl::ActionFetchMessage, nullptr, aURL);
+}
+
+NS_IMETHODIMP nsMailboxService::StreamHeaders(const char *aMessageURI,
+ nsIStreamListener *aConsumer,
+ nsIUrlListener *aUrlListener,
+ bool aLocalOnly,
+ nsIURI **aURL)
+{
+ NS_ENSURE_ARG_POINTER(aMessageURI);
+ NS_ENSURE_ARG_POINTER(aConsumer);
+ nsAutoCString folderURI;
+ nsMsgKey msgKey;
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsresult rv = DecomposeMailboxURI(aMessageURI, getter_AddRefs(folder), &msgKey);
+ if (msgKey == nsMsgKey_None)
+ return NS_MSG_MESSAGE_NOT_FOUND;
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ int64_t messageOffset;
+ uint32_t messageSize;
+ rv = folder->GetOfflineFileStream(msgKey, &messageOffset, &messageSize, getter_AddRefs(inputStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // GetOfflineFileStream returns NS_OK but null inputStream when there is an error getting the database
+ if (!inputStream)
+ return NS_ERROR_FAILURE;
+ return MsgStreamMsgHeaders(inputStream, aConsumer);
+}
+
+
+NS_IMETHODIMP nsMailboxService::IsMsgInMemCache(nsIURI *aUrl,
+ nsIMsgFolder *aFolder,
+ bool *aResult)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsMailboxService::OpenAttachment(const char *aContentType,
+ const char *aFileName,
+ const char *aUrl,
+ const char *aMessageUri,
+ nsISupports *aDisplayConsumer,
+ nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aUrlListener)
+{
+ nsCOMPtr <nsIURI> URL;
+ nsAutoCString urlString(aUrl);
+ urlString += "&type=";
+ urlString += aContentType;
+ urlString += "&filename=";
+ urlString += aFileName;
+ CreateStartupUrl(urlString.get(), getter_AddRefs(URL));
+ nsresult rv;
+
+ // try to run the url in the docshell...
+ nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aDisplayConsumer, &rv));
+ // if we were given a docShell, run the url in the docshell..otherwise just run it normally.
+ if (NS_SUCCEEDED(rv) && docShell)
+ {
+ nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
+ // DIRTY LITTLE HACK --> since we are opening an attachment we want the docshell to
+ // treat this load as if it were a user click event. Then the dispatching stuff will be much
+ // happier.
+ docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
+ loadInfo->SetLoadType(nsIDocShellLoadInfo::loadLink);
+ return docShell->LoadURI(URL, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, false);
+ }
+ return RunMailboxUrl(URL, aDisplayConsumer);
+
+}
+
+
+NS_IMETHODIMP
+nsMailboxService::SaveMessageToDisk(const char *aMessageURI,
+ nsIFile *aFile,
+ bool aAddDummyEnvelope,
+ nsIUrlListener *aUrlListener,
+ nsIURI **aURL,
+ bool canonicalLineEnding,
+ nsIMsgWindow *aMsgWindow)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMailboxUrl> mailboxurl;
+
+ rv = PrepareMessageUrl(aMessageURI, aUrlListener, nsIMailboxUrl::ActionSaveMessageToDisk, getter_AddRefs(mailboxurl), aMsgWindow);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(mailboxurl);
+ if (msgUrl)
+ {
+ msgUrl->SetMessageFile(aFile);
+ msgUrl->SetAddDummyEnvelope(aAddDummyEnvelope);
+ msgUrl->SetCanonicalLineEnding(canonicalLineEnding);
+ }
+
+ nsCOMPtr<nsIURI> url = do_QueryInterface(mailboxurl);
+ rv = RunMailboxUrl(url);
+ }
+
+ if (aURL && mailboxurl)
+ CallQueryInterface(mailboxurl, aURL);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMailboxService::GetUrlForUri(const char *aMessageURI, nsIURI **aURL, nsIMsgWindow *aMsgWindow)
+{
+ NS_ENSURE_ARG_POINTER(aURL);
+ if (!strncmp(aMessageURI, "file:", 5) || PL_strstr(aMessageURI, "type=application/x-message-display")
+ || !strncmp(aMessageURI, "mailbox:", 8))
+ return NS_NewURI(aURL, aMessageURI);
+
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMailboxUrl> mailboxurl;
+ rv = PrepareMessageUrl(aMessageURI, nullptr, nsIMailboxUrl::ActionFetchMessage, getter_AddRefs(mailboxurl), aMsgWindow);
+ if (NS_SUCCEEDED(rv) && mailboxurl)
+ rv = CallQueryInterface(mailboxurl, aURL);
+ return rv;
+}
+
+// Takes a mailbox url, this method creates a protocol instance and loads the url
+// into the protocol instance.
+nsresult nsMailboxService::RunMailboxUrl(nsIURI * aMailboxUrl, nsISupports * aDisplayConsumer)
+{
+ // create a protocol instance to run the url..
+ nsresult rv = NS_OK;
+ nsMailboxProtocol * protocol = new nsMailboxProtocol(aMailboxUrl);
+
+ if (protocol)
+ {
+ rv = protocol->Initialize(aMailboxUrl);
+ if (NS_FAILED(rv))
+ {
+ delete protocol;
+ return rv;
+ }
+ NS_ADDREF(protocol);
+ rv = protocol->LoadUrl(aMailboxUrl, aDisplayConsumer);
+ NS_RELEASE(protocol); // after loading, someone else will have a ref cnt on the mailbox
+ }
+
+ return rv;
+}
+
+// This function takes a message uri, converts it into a file path & msgKey
+// pair. It then turns that into a mailbox url object. It also registers a url
+// listener if appropriate. AND it can take in a mailbox action and set that field
+// on the returned url as well.
+nsresult nsMailboxService::PrepareMessageUrl(const char * aSrcMsgMailboxURI, nsIUrlListener * aUrlListener,
+ nsMailboxAction aMailboxAction, nsIMailboxUrl ** aMailboxUrl,
+ nsIMsgWindow *msgWindow)
+{
+ nsresult rv = CallCreateInstance(NS_MAILBOXURL_CONTRACTID, aMailboxUrl);
+ if (NS_SUCCEEDED(rv) && aMailboxUrl && *aMailboxUrl)
+ {
+ // okay now generate the url string
+ char * urlSpec;
+ nsAutoCString folderURI;
+ nsMsgKey msgKey;
+ nsCString folderPath;
+ const char *part = PL_strstr(aSrcMsgMailboxURI, "part=");
+ const char *header = PL_strstr(aSrcMsgMailboxURI, "header=");
+ rv = nsParseLocalMessageURI(aSrcMsgMailboxURI, folderURI, &msgKey);
+ NS_ENSURE_SUCCESS(rv,rv);
+ rv = nsLocalURI2Path(kMailboxRootURI, folderURI.get(), folderPath);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ // set up the url spec and initialize the url with it.
+ nsAutoCString buf;
+ MsgEscapeURL(folderPath,
+ nsINetUtil::ESCAPE_URL_DIRECTORY | nsINetUtil::ESCAPE_URL_FORCED, buf);
+ if (mPrintingOperation)
+ urlSpec = PR_smprintf("mailbox://%s?number=%lu&header=print", buf.get(), msgKey);
+ else if (part)
+ urlSpec = PR_smprintf("mailbox://%s?number=%lu&%s", buf.get(), msgKey, part);
+ else if (header)
+ urlSpec = PR_smprintf("mailbox://%s?number=%lu&%s", buf.get(), msgKey, header);
+ else
+ urlSpec = PR_smprintf("mailbox://%s?number=%lu", buf.get(), msgKey);
+
+ nsCOMPtr <nsIMsgMailNewsUrl> url = do_QueryInterface(*aMailboxUrl);
+ rv = url->SetSpec(nsDependentCString(urlSpec));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ PR_smprintf_free(urlSpec);
+
+ (*aMailboxUrl)->SetMailboxAction(aMailboxAction);
+
+ // set up the url listener
+ if (aUrlListener)
+ rv = url->RegisterListener(aUrlListener);
+
+ url->SetMsgWindow(msgWindow);
+ nsCOMPtr<nsIMsgMessageUrl> msgUrl = do_QueryInterface(url);
+ if (msgUrl)
+ {
+ msgUrl->SetOriginalSpec(aSrcMsgMailboxURI);
+ msgUrl->SetUri(aSrcMsgMailboxURI);
+ }
+
+ } // if we got a url
+ } // if we got a url
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMailboxService::GetScheme(nsACString &aScheme)
+{
+ aScheme = "mailbox";
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxService::GetDefaultPort(int32_t *aDefaultPort)
+{
+ NS_ENSURE_ARG_POINTER(aDefaultPort);
+ *aDefaultPort = -1; // mailbox doesn't use a port!!!!!
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxService::AllowPort(int32_t port, const char *scheme, bool *_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ // don't override anything.
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxService::GetProtocolFlags(uint32_t *result)
+{
+ NS_ENSURE_ARG_POINTER(result);
+ *result = URI_STD | URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT |
+ URI_DANGEROUS_TO_LOAD | URI_FORBIDS_COOKIE_ACCESS
+#ifdef IS_ORIGIN_IS_FULL_SPEC_DEFINED
+ | ORIGIN_IS_FULL_SPEC
+#endif
+ ;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxService::NewURI(const nsACString &aSpec,
+ const char *aOriginCharset,
+ nsIURI *aBaseURI,
+ nsIURI **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = 0;
+ nsresult rv;
+ nsCOMPtr<nsIURI> aMsgUri = do_CreateInstance(NS_MAILBOXURL_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // SetSpec calls below may fail if the mailbox url is of the form
+ // mailbox://<account>/<mailbox name>?... instead of
+ // mailbox://<path to folder>?.... This is the case for pop3 download urls.
+ // We know this, and the failure is harmless.
+ if (aBaseURI)
+ {
+ nsAutoCString newSpec;
+ rv = aBaseURI->Resolve(aSpec, newSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ (void) aMsgUri->SetSpec(newSpec);
+ }
+ else
+ {
+ (void) aMsgUri->SetSpec(aSpec);
+ }
+ aMsgUri.swap(*_retval);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMailboxService::NewChannel(nsIURI *aURI, nsIChannel **_retval)
+{
+ return NewChannel2(aURI, nullptr, _retval);
+}
+
+NS_IMETHODIMP nsMailboxService::NewChannel2(nsIURI *aURI,
+ nsILoadInfo *aLoadInfo,
+ nsIChannel **_retval)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+ NS_ENSURE_ARG_POINTER(_retval);
+ nsresult rv = NS_OK;
+ nsAutoCString spec;
+ rv = aURI->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (spec.Find("?uidl=") >= 0 || spec.Find("&uidl=") >= 0)
+ {
+ nsCOMPtr<nsIProtocolHandler> handler =
+ do_GetService(NS_POP3SERVICE_CONTRACTID1, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr <nsIURI> pop3Uri;
+
+ rv = handler->NewURI(spec, "" /* ignored */, aURI, getter_AddRefs(pop3Uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return handler->NewChannel2(pop3Uri, aLoadInfo, _retval);
+ }
+ }
+
+ RefPtr<nsMailboxProtocol> protocol = new nsMailboxProtocol(aURI);
+ if (!protocol) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ rv = protocol->Initialize(aURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = protocol->SetLoadInfo(aLoadInfo);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CallQueryInterface(protocol, _retval);
+}
+
+nsresult nsMailboxService::DisplayMessageForPrinting(const char* aMessageURI,
+ nsISupports * aDisplayConsumer,
+ nsIMsgWindow * aMsgWindow,
+ nsIUrlListener * aUrlListener,
+ nsIURI ** aURL)
+{
+ mPrintingOperation = true;
+ nsresult rv = FetchMessage(aMessageURI, aDisplayConsumer, aMsgWindow,aUrlListener, nullptr,
+ nsIMailboxUrl::ActionFetchMessage, nullptr, aURL);
+ mPrintingOperation = false;
+ return rv;
+}
+
+NS_IMETHODIMP nsMailboxService::Search(nsIMsgSearchSession *aSearchSession, nsIMsgWindow *aMsgWindow, nsIMsgFolder *aMsgFolder, const char *aMessageUri)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsMailboxService::DecomposeMailboxURI(const char * aMessageURI, nsIMsgFolder ** aFolder, nsMsgKey *aMsgKey)
+{
+ NS_ENSURE_ARG_POINTER(aMessageURI);
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aMsgKey);
+
+ nsresult rv = NS_OK;
+ nsAutoCString folderURI;
+ rv = nsParseLocalMessageURI(aMessageURI, folderURI, aMsgKey);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr <nsIRDFService> rdf = do_GetService("@mozilla.org/rdf/rdf-service;1",&rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIRDFResource> res;
+ rv = rdf->GetResource(folderURI, getter_AddRefs(res));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = res->QueryInterface(NS_GET_IID(nsIMsgFolder), (void **) aFolder);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMailboxService::MessageURIToMsgHdr(const char *uri, nsIMsgDBHdr **_retval)
+{
+ NS_ENSURE_ARG_POINTER(uri);
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsMsgKey msgKey;
+
+ rv = DecomposeMailboxURI(uri, getter_AddRefs(folder), &msgKey);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = folder->GetMessageHeader(msgKey, _retval);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsMailboxService.h b/mailnews/local/src/nsMailboxService.h
new file mode 100644
index 000000000..c7fc759ee
--- /dev/null
+++ b/mailnews/local/src/nsMailboxService.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMailboxService_h___
+#define nsMailboxService_h___
+
+#include "nscore.h"
+#include "nsISupports.h"
+
+#include "nsIMailboxService.h"
+#include "nsIMsgMessageService.h"
+#include "nsIMailboxUrl.h"
+#include "nsIURL.h"
+#include "nsIUrlListener.h"
+#include "nsIStreamListener.h"
+#include "nsIFile.h"
+#include "nsIProtocolHandler.h"
+#include "nsIRDFService.h"
+
+class nsMailboxService : public nsIMailboxService, public nsIMsgMessageService, public nsIMsgMessageFetchPartService, public nsIProtocolHandler
+{
+public:
+
+ nsMailboxService();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMAILBOXSERVICE
+ NS_DECL_NSIMSGMESSAGESERVICE
+ NS_DECL_NSIMSGMESSAGEFETCHPARTSERVICE
+ NS_DECL_NSIPROTOCOLHANDLER
+
+protected:
+ virtual ~nsMailboxService();
+ bool mPrintingOperation;
+
+ // helper functions used by the service
+ nsresult PrepareMessageUrl(const char * aSrcMsgMailboxURI, nsIUrlListener * aUrlListener,
+ nsMailboxAction aMailboxAction, nsIMailboxUrl ** aMailboxUrl,
+ nsIMsgWindow *msgWindow);
+
+ nsresult RunMailboxUrl(nsIURI * aMailboxUrl, nsISupports * aDisplayConsumer = nullptr);
+
+ nsresult FetchMessage(const char* aMessageURI,
+ nsISupports * aDisplayConsumer,
+ nsIMsgWindow * aMsgWindow,
+ nsIUrlListener * aUrlListener,
+ const char * aFileName, /* only used by open attachment */
+ nsMailboxAction mailboxAction,
+ const char * aCharsetOverride,
+ nsIURI ** aURL);
+
+ nsresult DecomposeMailboxURI(const char * aMessageURI, nsIMsgFolder ** aFolder, nsMsgKey *aMsgKey);
+};
+
+#endif /* nsMailboxService_h___ */
diff --git a/mailnews/local/src/nsMailboxUrl.cpp b/mailnews/local/src/nsMailboxUrl.cpp
new file mode 100644
index 000000000..25fe4f35f
--- /dev/null
+++ b/mailnews/local/src/nsMailboxUrl.cpp
@@ -0,0 +1,556 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsIURI.h"
+#include "nsIMailboxUrl.h"
+#include "nsMailboxUrl.h"
+
+#include "nsStringGlue.h"
+#include "nsLocalUtils.h"
+#include "nsIMsgDatabase.h"
+#include "nsMsgDBCID.h"
+#include "nsMsgBaseCID.h"
+#include "nsIMsgHdr.h"
+
+#include "nsIMsgFolder.h"
+#include "prprf.h"
+#include "prmem.h"
+#include "nsIMsgMailSession.h"
+#include "nsNetUtil.h"
+#include "nsIFileURL.h"
+
+// this is totally lame and MUST be removed by M6
+// the real fix is to attach the URI to the URL as it runs through netlib
+// then grab it and use it on the other side
+#include "nsCOMPtr.h"
+#include "nsMsgBaseCID.h"
+#include "nsIMsgAccountManager.h"
+#include "nsMsgUtils.h"
+#include "mozilla/Services.h"
+
+// helper function for parsing the search field of a url
+char * extractAttributeValue(const char * searchString, const char * attributeName);
+
+nsMailboxUrl::nsMailboxUrl()
+{
+ m_mailboxAction = nsIMailboxUrl::ActionParseMailbox;
+ m_filePath = nullptr;
+ m_messageID = nullptr;
+ m_messageKey = nsMsgKey_None;
+ m_messageSize = 0;
+ m_messageFile = nullptr;
+ m_addDummyEnvelope = false;
+ m_canonicalLineEnding = false;
+ m_curMsgIndex = 0;
+}
+
+nsMailboxUrl::~nsMailboxUrl()
+{
+ PR_Free(m_messageID);
+}
+
+NS_IMPL_ADDREF_INHERITED(nsMailboxUrl, nsMsgMailNewsUrl)
+NS_IMPL_RELEASE_INHERITED(nsMailboxUrl, nsMsgMailNewsUrl)
+
+NS_INTERFACE_MAP_BEGIN(nsMailboxUrl)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMailboxUrl)
+ NS_INTERFACE_MAP_ENTRY(nsIMailboxUrl)
+ NS_INTERFACE_MAP_ENTRY(nsIMsgMessageUrl)
+ NS_INTERFACE_MAP_ENTRY(nsIMsgI18NUrl)
+NS_INTERFACE_MAP_END_INHERITING(nsMsgMailNewsUrl)
+
+////////////////////////////////////////////////////////////////////////////////////
+// Begin nsIMailboxUrl specific support
+////////////////////////////////////////////////////////////////////////////////////
+nsresult nsMailboxUrl::SetMailboxParser(nsIStreamListener * aMailboxParser)
+{
+ if (aMailboxParser)
+ m_mailboxParser = aMailboxParser;
+ return NS_OK;
+}
+
+nsresult nsMailboxUrl::GetMailboxParser(nsIStreamListener ** aConsumer)
+{
+ NS_ENSURE_ARG_POINTER(aConsumer);
+
+ NS_IF_ADDREF(*aConsumer = m_mailboxParser);
+ return NS_OK;
+}
+
+nsresult nsMailboxUrl::SetMailboxCopyHandler(nsIStreamListener * aMailboxCopyHandler)
+{
+ if (aMailboxCopyHandler)
+ m_mailboxCopyHandler = aMailboxCopyHandler;
+ return NS_OK;
+}
+
+nsresult nsMailboxUrl::GetMailboxCopyHandler(nsIStreamListener ** aMailboxCopyHandler)
+{
+ NS_ENSURE_ARG_POINTER(aMailboxCopyHandler);
+
+ if (aMailboxCopyHandler)
+ {
+ *aMailboxCopyHandler = m_mailboxCopyHandler;
+ NS_IF_ADDREF(*aMailboxCopyHandler);
+ }
+
+ return NS_OK;
+}
+
+nsresult nsMailboxUrl::GetMessageKey(nsMsgKey* aMessageKey)
+{
+ *aMessageKey = m_messageKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::GetMessageSize(uint32_t * aMessageSize)
+{
+ if (aMessageSize)
+ {
+ *aMessageSize = m_messageSize;
+ return NS_OK;
+ }
+ else
+ return NS_ERROR_NULL_POINTER;
+}
+
+nsresult nsMailboxUrl::SetMessageSize(uint32_t aMessageSize)
+{
+ m_messageSize = aMessageSize;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::GetPrincipalSpec(nsACString& aPrincipalSpec)
+{
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsURL;
+ QueryInterface(NS_GET_IID(nsIMsgMailNewsUrl), getter_AddRefs(mailnewsURL));
+
+ nsAutoCString spec;
+ mailnewsURL->GetSpecIgnoringRef(spec);
+
+ // mailbox: URLs contain a lot of query parts. We want need a normalised form:
+ // mailbox:///path/to/folder?number=nn.
+ // We also need to translate the second form mailbox://user@domain@server/folder?number=nn.
+
+ char* messageKey = extractAttributeValue(spec.get(), "number=");
+
+ // Strip any query part beginning with ? or /;
+ int32_t ind = spec.Find("/;");
+ if (ind != kNotFound)
+ spec.SetLength(ind);
+
+ ind = spec.FindChar('?');
+ if (ind != kNotFound)
+ spec.SetLength(ind);
+
+ // Check for format lacking absolute path.
+ if (spec.Find("///") == kNotFound) {
+ nsCString folderPath;
+ nsresult rv = nsLocalURI2Path(kMailboxRootURI, spec.get(), folderPath);
+ if (NS_SUCCEEDED (rv)) {
+ nsAutoCString buf;
+ MsgEscapeURL(folderPath,
+ nsINetUtil::ESCAPE_URL_DIRECTORY | nsINetUtil::ESCAPE_URL_FORCED, buf);
+ spec = NS_LITERAL_CSTRING("mailbox://") + buf;
+ }
+ }
+
+ spec += NS_LITERAL_CSTRING("?number=");
+ spec.Append(messageKey);
+ PR_Free(messageKey);
+
+ aPrincipalSpec.Assign(spec);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::SetUri(const char * aURI)
+{
+ mURI= aURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::CloneInternal(uint32_t aRefHandlingMode,
+ const nsACString& newRef,
+ nsIURI **_retval)
+{
+ nsresult rv = nsMsgMailNewsUrl::CloneInternal(aRefHandlingMode,
+ newRef, _retval);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // also clone the mURI member, because GetUri below won't work if
+ // mURI isn't set due to nsIFile fun.
+ nsCOMPtr <nsIMsgMessageUrl> clonedUrl = do_QueryInterface(*_retval);
+ if (clonedUrl)
+ clonedUrl->SetUri(mURI.get());
+ return rv;
+}
+
+NS_IMETHODIMP nsMailboxUrl::GetUri(char ** aURI)
+{
+ // if we have been given a uri to associate with this url, then use it
+ // otherwise try to reconstruct a URI on the fly....
+
+ if (!mURI.IsEmpty())
+ *aURI = ToNewCString(mURI);
+ else
+ {
+ if (m_filePath)
+ {
+ nsAutoCString baseUri;
+ nsresult rv;
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // we blow off errors here so that we can open attachments
+ // in .eml files.
+ (void) accountManager->FolderUriForPath(m_filePath, baseUri);
+ if (baseUri.IsEmpty()) {
+ rv = m_baseURL->GetSpec(baseUri);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ nsCString baseMessageURI;
+ nsCreateLocalBaseMessageURI(baseUri, baseMessageURI);
+ nsAutoCString uriStr;
+ nsBuildLocalMessageURI(baseMessageURI.get(), m_messageKey, uriStr);
+ *aURI = ToNewCString(uriStr);
+ }
+ else
+ *aURI = nullptr;
+ }
+
+ return NS_OK;
+}
+
+nsresult nsMailboxUrl::GetMsgHdrForKey(nsMsgKey msgKey, nsIMsgDBHdr ** aMsgHdr)
+{
+ nsresult rv = NS_OK;
+ if (aMsgHdr && m_filePath)
+ {
+ nsCOMPtr<nsIMsgDatabase> mailDBFactory;
+ nsCOMPtr<nsIMsgDatabase> mailDB;
+ nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+
+ if (msgDBService)
+ rv = msgDBService->OpenMailDBFromFile(m_filePath, nullptr, false,
+ false, getter_AddRefs(mailDB));
+ if (NS_SUCCEEDED(rv) && mailDB) // did we get a db back?
+ rv = mailDB->GetMsgHdrForKey(msgKey, aMsgHdr);
+ else
+ {
+ nsCOMPtr<nsIMsgWindow> msgWindow(do_QueryReferent(m_msgWindowWeak));
+ if (!msgWindow)
+ {
+ nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
+ }
+
+ // maybe this is .eml file we're trying to read. See if we can get a header from the header sink.
+ if (msgWindow)
+ {
+ nsCOMPtr<nsIMsgHeaderSink> headerSink;
+ msgWindow->GetMsgHeaderSink(getter_AddRefs(headerSink));
+ if (headerSink)
+ {
+ rv = headerSink->GetDummyMsgHeader(aMsgHdr);
+ if (NS_SUCCEEDED(rv))
+ {
+ int64_t fileSize = 0;
+ m_filePath->GetFileSize(&fileSize);
+ (*aMsgHdr)->SetMessageSize(fileSize);
+ }
+ }
+ }
+ }
+ }
+ else
+ rv = NS_ERROR_NULL_POINTER;
+
+ return rv;
+}
+
+NS_IMETHODIMP nsMailboxUrl::GetMessageHeader(nsIMsgDBHdr ** aMsgHdr)
+{
+ if (m_dummyHdr)
+ {
+ NS_IF_ADDREF(*aMsgHdr = m_dummyHdr);
+ return NS_OK;
+ }
+ return GetMsgHdrForKey(m_messageKey, aMsgHdr);
+}
+
+NS_IMETHODIMP nsMailboxUrl::SetMessageHeader(nsIMsgDBHdr *aMsgHdr)
+{
+ m_dummyHdr = aMsgHdr;
+ return NS_OK;
+}
+
+NS_IMPL_GETSET(nsMailboxUrl, AddDummyEnvelope, bool, m_addDummyEnvelope)
+NS_IMPL_GETSET(nsMailboxUrl, CanonicalLineEnding, bool, m_canonicalLineEnding)
+
+NS_IMETHODIMP
+nsMailboxUrl::GetOriginalSpec(char **aSpec)
+{
+ if (!aSpec || m_originalSpec.IsEmpty())
+ return NS_ERROR_NULL_POINTER;
+ *aSpec = ToNewCString(m_originalSpec);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMailboxUrl::SetOriginalSpec(const char *aSpec)
+{
+ m_originalSpec = aSpec;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::SetMessageFile(nsIFile * aFile)
+{
+ m_messageFile = aFile;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::GetMessageFile(nsIFile ** aFile)
+{
+ // why don't we return an error for null aFile?
+ if (aFile)
+ NS_IF_ADDREF(*aFile = m_messageFile);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::IsUrlType(uint32_t type, bool *isType)
+{
+ NS_ENSURE_ARG(isType);
+
+ switch(type)
+ {
+ case nsIMsgMailNewsUrl::eCopy:
+ *isType = (m_mailboxAction == nsIMailboxUrl::ActionCopyMessage);
+ break;
+ case nsIMsgMailNewsUrl::eMove:
+ *isType = (m_mailboxAction == nsIMailboxUrl::ActionMoveMessage);
+ break;
+ case nsIMsgMailNewsUrl::eDisplay:
+ *isType = (m_mailboxAction == nsIMailboxUrl::ActionFetchMessage ||
+ m_mailboxAction == nsIMailboxUrl::ActionFetchPart);
+ break;
+ default:
+ *isType = false;
+ };
+
+ return NS_OK;
+
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+// End nsIMailboxUrl specific support
+////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// possible search part phrases include: MessageID=id&number=MessageKey
+
+nsresult nsMailboxUrl::ParseSearchPart()
+{
+ nsAutoCString searchPart;
+ nsresult rv = GetQuery(searchPart);
+ // add code to this function to decompose everything past the '?'.....
+ if (NS_SUCCEEDED(rv) && !searchPart.IsEmpty())
+ {
+ // the action for this mailbox must be a display message...
+ char * msgPart = extractAttributeValue(searchPart.get(), "part=");
+ if (msgPart) // if we have a part in the url then we must be fetching just the part.
+ m_mailboxAction = nsIMailboxUrl::ActionFetchPart;
+ else
+ m_mailboxAction = nsIMailboxUrl::ActionFetchMessage;
+
+ char * messageKey = extractAttributeValue(searchPart.get(), "number=");
+ m_messageID = extractAttributeValue(searchPart.get(),"messageid=");
+ if (messageKey)
+ m_messageKey = (nsMsgKey) ParseUint64Str(messageKey); // convert to a uint32_t...
+
+ PR_Free(msgPart);
+ PR_Free(messageKey);
+ }
+ else
+ m_mailboxAction = nsIMailboxUrl::ActionParseMailbox;
+
+ return rv;
+}
+
+// warning: don't assume when parsing the url that the protocol part is "news"...
+nsresult nsMailboxUrl::ParseUrl()
+{
+ GetFilePath(m_file);
+
+ ParseSearchPart();
+ // ### fix me.
+ // this hack is to avoid asserting on every local message loaded because the security manager
+ // is creating an empty "mailbox://" uri for every message.
+ if (m_file.Length() < 2)
+ m_filePath = nullptr;
+ else
+ {
+ nsCString fileUri("file://");
+ fileUri.Append(m_file);
+ nsresult rv;
+ nsCOMPtr<nsIIOService> ioService =
+ mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED);
+ nsCOMPtr <nsIURI> uri;
+ rv = ioService->NewURI(fileUri, nullptr, nullptr, getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIFileURL> fileURL = do_QueryInterface(uri);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIFile> fileURLFile;
+ fileURL->GetFile(getter_AddRefs(fileURLFile));
+ m_filePath = do_QueryInterface(fileURLFile, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ GetPath(m_file);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::SetSpec(const nsACString &aSpec)
+{
+ nsresult rv = nsMsgMailNewsUrl::SetSpec(aSpec);
+ if (NS_SUCCEEDED(rv))
+ rv = ParseUrl();
+ return rv;
+}
+
+NS_IMETHODIMP nsMailboxUrl::SetQuery(const nsACString &aQuery)
+{
+ nsresult rv = nsMsgMailNewsUrl::SetQuery(aQuery);
+ if (NS_SUCCEEDED(rv))
+ rv = ParseUrl();
+ return rv;
+}
+
+// takes a string like ?messageID=fooo&number=MsgKey and returns a new string
+// containing just the attribute value. i.e you could pass in this string with
+// an attribute name of messageID and I'll return fooo. Use PR_Free to delete
+// this string...
+
+// Assumption: attribute pairs in the string are separated by '&'.
+char * extractAttributeValue(const char * searchString, const char * attributeName)
+{
+ char * attributeValue = nullptr;
+
+ if (searchString && attributeName)
+ {
+ // search the string for attributeName
+ uint32_t attributeNameSize = PL_strlen(attributeName);
+ char * startOfAttribute = PL_strcasestr(searchString, attributeName);
+ if (startOfAttribute)
+ {
+ startOfAttribute += attributeNameSize; // skip over the attributeName
+ if (startOfAttribute) // is there something after the attribute name
+ {
+ char * endOfAttribute = startOfAttribute ? PL_strchr(startOfAttribute, '&') : nullptr;
+ nsDependentCString attributeValueStr;
+ if (startOfAttribute && endOfAttribute) // is there text after attribute value
+ attributeValueStr.Assign(startOfAttribute, endOfAttribute - startOfAttribute);
+ else // there is nothing left so eat up rest of line.
+ attributeValueStr.Assign(startOfAttribute);
+
+ // now unescape the string...
+ nsCString unescapedValue;
+ MsgUnescapeString(attributeValueStr, 0, unescapedValue);
+ attributeValue = PL_strdup(unescapedValue.get());
+ } // if we have a attribute value
+
+ } // if we have a attribute name
+ } // if we got non-null search string and attribute name values
+
+ return attributeValue;
+}
+
+// nsIMsgI18NUrl support
+
+nsresult nsMailboxUrl::GetFolder(nsIMsgFolder **msgFolder)
+{
+ // if we have a RDF URI, then try to get the folder for that URI and then ask the folder
+ // for it's charset....
+ nsCString uri;
+ GetUri(getter_Copies(uri));
+ NS_ENSURE_TRUE(!uri.IsEmpty(), NS_ERROR_FAILURE);
+ nsCOMPtr<nsIMsgDBHdr> msg;
+ GetMsgDBHdrFromURI(uri.get(), getter_AddRefs(msg));
+ if (!msg)
+ return NS_ERROR_FAILURE;
+ return msg->GetFolder(msgFolder);
+}
+
+NS_IMETHODIMP nsMailboxUrl::GetFolderCharset(char ** aCharacterSet)
+{
+ NS_ENSURE_ARG_POINTER(aCharacterSet);
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsresult rv = GetFolder(getter_AddRefs(folder));
+
+ // In cases where a file is not associated with a folder, for
+ // example standalone .eml files, failure is normal.
+ if (NS_FAILED(rv))
+ return rv;
+ nsCString tmpStr;
+ folder->GetCharset(tmpStr);
+ *aCharacterSet = ToNewCString(tmpStr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::GetFolderCharsetOverride(bool * aCharacterSetOverride)
+{
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsresult rv = GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv,rv);
+ NS_ENSURE_TRUE(folder, NS_ERROR_FAILURE);
+ folder->GetCharsetOverride(aCharacterSetOverride);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::GetCharsetOverRide(char ** aCharacterSet)
+{
+ if (!mCharsetOverride.IsEmpty())
+ *aCharacterSet = ToNewCString(mCharsetOverride);
+ else
+ *aCharacterSet = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::SetCharsetOverRide(const char * aCharacterSet)
+{
+ mCharsetOverride = aCharacterSet;
+ return NS_OK;
+}
+
+/* void setMoveCopyMsgKeys (out nsMsgKey keysToFlag, in long numKeys); */
+NS_IMETHODIMP nsMailboxUrl::SetMoveCopyMsgKeys(nsMsgKey *keysToFlag, int32_t numKeys)
+{
+ m_keys.ReplaceElementsAt(0, m_keys.Length(), keysToFlag, numKeys);
+ if (!m_keys.IsEmpty() && m_messageKey == nsMsgKey_None)
+ m_messageKey = m_keys[0];
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMailboxUrl::GetMoveCopyMsgHdrForIndex(uint32_t msgIndex, nsIMsgDBHdr **msgHdr)
+{
+ NS_ENSURE_ARG(msgHdr);
+ if (msgIndex < m_keys.Length())
+ {
+ nsMsgKey nextKey = m_keys[msgIndex];
+ return GetMsgHdrForKey(nextKey, msgHdr);
+ }
+ return NS_MSG_MESSAGE_NOT_FOUND;
+}
+
+NS_IMETHODIMP nsMailboxUrl::GetNumMoveCopyMsgs(uint32_t *numMsgs)
+{
+ NS_ENSURE_ARG(numMsgs);
+ *numMsgs = m_keys.Length();
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsMailboxUrl.h b/mailnews/local/src/nsMailboxUrl.h
new file mode 100644
index 000000000..63973a916
--- /dev/null
+++ b/mailnews/local/src/nsMailboxUrl.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef nsMailboxUrl_h__
+#define nsMailboxUrl_h__
+
+#include "mozilla/Attributes.h"
+#include "nsIMailboxUrl.h"
+#include "nsMsgMailNewsUrl.h"
+#include "nsIStreamListener.h"
+#include "nsIFile.h"
+#include "nsCOMPtr.h"
+#include "MailNewsTypes.h"
+#include "nsTArray.h"
+
+class nsMailboxUrl : public nsIMailboxUrl, public nsMsgMailNewsUrl, public nsIMsgMessageUrl, public nsIMsgI18NUrl
+{
+public:
+ // nsIURI over-ride...
+ NS_IMETHOD SetSpec(const nsACString &aSpec) override;
+ NS_IMETHOD SetQuery(const nsACString &aQuery) override;
+
+ // from nsIMailboxUrl:
+ NS_IMETHOD SetMailboxParser(nsIStreamListener * aConsumer) override;
+ NS_IMETHOD GetMailboxParser(nsIStreamListener ** aConsumer) override;
+ NS_IMETHOD SetMailboxCopyHandler(nsIStreamListener * aConsumer) override;
+ NS_IMETHOD GetMailboxCopyHandler(nsIStreamListener ** aConsumer) override;
+
+ NS_IMETHOD GetMessageKey(nsMsgKey* aMessageKey) override;
+ NS_IMETHOD GetMessageSize(uint32_t *aMessageSize) override;
+ NS_IMETHOD SetMessageSize(uint32_t aMessageSize) override;
+ NS_IMETHOD GetMailboxAction(nsMailboxAction *result) override
+ {
+ NS_ENSURE_ARG_POINTER(result);
+ *result = m_mailboxAction;
+ return NS_OK;
+ }
+ NS_IMETHOD SetMailboxAction(nsMailboxAction aAction) override
+ {
+ m_mailboxAction = aAction;
+ return NS_OK;
+ }
+ NS_IMETHOD IsUrlType(uint32_t type, bool *isType) override;
+ NS_IMETHOD SetMoveCopyMsgKeys(nsMsgKey *keysToFlag, int32_t numKeys) override;
+ NS_IMETHOD GetMoveCopyMsgHdrForIndex(uint32_t msgIndex, nsIMsgDBHdr **msgHdr) override;
+ NS_IMETHOD GetNumMoveCopyMsgs(uint32_t *numMsgs) override;
+ NS_IMETHOD GetCurMoveCopyMsgIndex(uint32_t *result) override
+ {
+ NS_ENSURE_ARG_POINTER(result);
+ *result = m_curMsgIndex;
+ return NS_OK;
+ }
+ NS_IMETHOD SetCurMoveCopyMsgIndex(uint32_t aIndex) override
+ {
+ m_curMsgIndex = aIndex;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetFolder(nsIMsgFolder **msgFolder) override;
+
+ // nsIMsgMailNewsUrl override
+ NS_IMETHOD CloneInternal(uint32_t aRefHandlingMode,
+ const nsACString& newRef,
+ nsIURI **_retval) override;
+
+ // nsMailboxUrl
+ nsMailboxUrl();
+ NS_DECL_NSIMSGMESSAGEURL
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIMSGI18NURL
+
+protected:
+ virtual ~nsMailboxUrl();
+ // protocol specific code to parse a url...
+ virtual nsresult ParseUrl();
+ nsresult GetMsgHdrForKey(nsMsgKey msgKey, nsIMsgDBHdr ** aMsgHdr);
+
+ // mailboxurl specific state
+ nsCOMPtr<nsIStreamListener> m_mailboxParser;
+ nsCOMPtr<nsIStreamListener> m_mailboxCopyHandler;
+
+ nsMailboxAction m_mailboxAction; // the action this url represents...parse mailbox, display messages, etc.
+ nsCOMPtr <nsIFile> m_filePath;
+ char *m_messageID;
+ uint32_t m_messageSize;
+ nsMsgKey m_messageKey;
+ nsCString m_file;
+ // This is currently only set when we're doing something with a .eml file.
+ // If that changes, we should change the name of this var.
+ nsCOMPtr<nsIMsgDBHdr> m_dummyHdr;
+
+ // used by save message to disk
+ nsCOMPtr<nsIFile> m_messageFile;
+ bool m_addDummyEnvelope;
+ bool m_canonicalLineEnding;
+ nsresult ParseSearchPart();
+
+ // for multiple msg move/copy
+ nsTArray<nsMsgKey> m_keys;
+ int32_t m_curMsgIndex;
+
+ // truncated message support
+ nsCString m_originalSpec;
+ nsCString mURI; // the RDF URI associated with this url.
+ nsCString mCharsetOverride; // used by nsIMsgI18NUrl...
+};
+
+#endif // nsMailboxUrl_h__
diff --git a/mailnews/local/src/nsMovemailIncomingServer.cpp b/mailnews/local/src/nsMovemailIncomingServer.cpp
new file mode 100644
index 000000000..cee915e25
--- /dev/null
+++ b/mailnews/local/src/nsMovemailIncomingServer.cpp
@@ -0,0 +1,178 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsMsgLocalCID.h"
+#include "nsMsgFolderFlags.h"
+#include "nsIMsgLocalMailFolder.h"
+#include "nsIMovemailService.h"
+#include "nsIFile.h"
+#include "msgCore.h" // pre-compiled headers
+#include "nsMovemailIncomingServer.h"
+#include "nsServiceManagerUtils.h"
+
+
+static NS_DEFINE_CID(kCMovemailServiceCID, NS_MOVEMAILSERVICE_CID);
+
+
+NS_IMPL_ISUPPORTS_INHERITED(nsMovemailIncomingServer,
+ nsMsgIncomingServer,
+ nsIMovemailIncomingServer,
+ nsILocalMailIncomingServer)
+
+
+
+nsMovemailIncomingServer::nsMovemailIncomingServer()
+{
+ m_canHaveFilters = true;
+}
+
+nsMovemailIncomingServer::~nsMovemailIncomingServer()
+{
+}
+
+NS_IMETHODIMP
+nsMovemailIncomingServer::PerformBiff(nsIMsgWindow *aMsgWindow)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMovemailService> movemailService(do_GetService(
+ kCMovemailServiceCID, &rv));
+ if (NS_FAILED(rv)) return rv;
+ nsCOMPtr<nsIMsgFolder> inbox;
+ nsCOMPtr<nsIMsgFolder> rootMsgFolder;
+ nsCOMPtr<nsIUrlListener> urlListener;
+ rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
+ if(NS_SUCCEEDED(rv) && rootMsgFolder)
+ {
+ rootMsgFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox,
+ getter_AddRefs(inbox));
+ if (!inbox) return NS_ERROR_FAILURE;
+ }
+
+ SetPerformingBiff(true);
+ urlListener = do_QueryInterface(inbox);
+
+ bool downloadOnBiff = false;
+ rv = GetDownloadOnBiff(&downloadOnBiff);
+ if (downloadOnBiff)
+ {
+ nsCOMPtr <nsIMsgLocalMailFolder> localInbox = do_QueryInterface(inbox,
+ &rv);
+ if (localInbox && NS_SUCCEEDED(rv))
+ {
+ bool valid = false;
+ nsCOMPtr <nsIMsgDatabase> db;
+ rv = inbox->GetMsgDatabase(getter_AddRefs(db));
+ if (NS_SUCCEEDED(rv) && db)
+ {
+ rv = db->GetSummaryValid(&valid);
+ }
+ if (NS_SUCCEEDED(rv) && valid)
+ {
+ rv = movemailService->GetNewMail(aMsgWindow, urlListener, inbox,
+ this, nullptr);
+ }
+ else
+ {
+ bool isLocked;
+ inbox->GetLocked(&isLocked);
+ if (!isLocked)
+ {
+ rv = localInbox->ParseFolder(aMsgWindow, urlListener);
+ }
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = localInbox->SetCheckForNewMessagesAfterParsing(true);
+ }
+ }
+ }
+ }
+ else
+ {
+ movemailService->CheckForNewMail(urlListener, inbox, this, nullptr);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailIncomingServer::SetFlagsOnDefaultMailboxes()
+{
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder =
+ do_QueryInterface(rootFolder, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return localFolder->SetFlagsOnDefaultMailboxes(nsMsgFolderFlags::SpecialUse);
+}
+
+NS_IMETHODIMP nsMovemailIncomingServer::CreateDefaultMailboxes()
+{
+ nsresult rv = CreateLocalFolder(NS_LITERAL_STRING("Inbox"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CreateLocalFolder(NS_LITERAL_STRING("Trash"));
+}
+
+
+NS_IMETHODIMP
+nsMovemailIncomingServer::GetNewMail(nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aUrlListener,
+ nsIMsgFolder *aMsgFolder,
+ nsIURI **aResult)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIMovemailService> movemailService =
+ do_GetService(kCMovemailServiceCID, &rv);
+
+ if (NS_FAILED(rv)) return rv;
+
+ rv = movemailService->GetNewMail(aMsgWindow, aUrlListener,
+ aMsgFolder, this, aResult);
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMovemailIncomingServer::GetDownloadMessagesAtStartup(bool *getMessagesAtStartup)
+{
+ NS_ENSURE_ARG_POINTER(getMessagesAtStartup);
+ *getMessagesAtStartup = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailIncomingServer::GetCanBeDefaultServer(bool *aCanBeDefaultServer)
+{
+ NS_ENSURE_ARG_POINTER(aCanBeDefaultServer);
+ *aCanBeDefaultServer = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailIncomingServer::GetCanSearchMessages(bool *canSearchMessages)
+{
+ NS_ENSURE_ARG_POINTER(canSearchMessages);
+ *canSearchMessages = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailIncomingServer::GetServerRequiresPasswordForBiff(bool *aServerRequiresPasswordForBiff)
+{
+ NS_ENSURE_ARG_POINTER(aServerRequiresPasswordForBiff);
+ *aServerRequiresPasswordForBiff = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailIncomingServer::GetAccountManagerChrome(nsAString& aResult)
+{
+ aResult.AssignLiteral("am-main.xul");
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsMovemailIncomingServer.h b/mailnews/local/src/nsMovemailIncomingServer.h
new file mode 100644
index 000000000..7fefec965
--- /dev/null
+++ b/mailnews/local/src/nsMovemailIncomingServer.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef __nsMovemailIncomingServer_h
+#define __nsMovemailIncomingServer_h
+
+#include "mozilla/Attributes.h"
+#include "msgCore.h"
+#include "nsIMovemailIncomingServer.h"
+#include "nsILocalMailIncomingServer.h"
+#include "nsMailboxServer.h"
+
+/* get some implementation from nsMsgIncomingServer */
+class nsMovemailIncomingServer : public nsMailboxServer,
+ public nsIMovemailIncomingServer,
+ public nsILocalMailIncomingServer
+
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIMOVEMAILINCOMINGSERVER
+ NS_DECL_NSILOCALMAILINCOMINGSERVER
+
+ nsMovemailIncomingServer();
+
+ NS_IMETHOD PerformBiff(nsIMsgWindow *aMsgWindow) override;
+ NS_IMETHOD GetDownloadMessagesAtStartup(bool *getMessages) override;
+ NS_IMETHOD GetCanBeDefaultServer(bool *canBeDefaultServer) override;
+ NS_IMETHOD GetCanSearchMessages(bool *canSearchMessages) override;
+ NS_IMETHOD GetServerRequiresPasswordForBiff(bool *aServerRequiresPasswordForBiff) override;
+ NS_IMETHOD GetAccountManagerChrome(nsAString& aResult) override;
+
+private:
+ virtual ~nsMovemailIncomingServer();
+};
+
+
+#endif
diff --git a/mailnews/local/src/nsMovemailService.cpp b/mailnews/local/src/nsMovemailService.cpp
new file mode 100644
index 000000000..a4280c975
--- /dev/null
+++ b/mailnews/local/src/nsMovemailService.cpp
@@ -0,0 +1,694 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <unistd.h> // for link(), used in spool-file locking
+
+#include "prenv.h"
+#include "private/pprio.h" // for our kernel-based locking
+#include "nspr.h"
+
+#include "msgCore.h" // precompiled header...
+
+#include "nsMovemailService.h"
+#include "nsIMovemailService.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsIMovemailIncomingServer.h"
+#include "nsIMsgProtocolInfo.h"
+#include "nsParseMailbox.h"
+#include "nsIMsgFolder.h"
+#include "nsIPrompt.h"
+
+#include "nsIFile.h"
+#include "nsMailDirServiceDefs.h"
+#include "nsMsgUtils.h"
+
+#include "nsCOMPtr.h"
+#include "nsMsgFolderFlags.h"
+
+#include "nsILineInputStream.h"
+#include "nsISeekableStream.h"
+#include "nsNetUtil.h"
+#include "nsAutoPtr.h"
+#include "nsIStringBundle.h"
+#include "nsIMsgPluggableStore.h"
+#include "mozilla/Services.h"
+#include "nsIInputStream.h"
+#include "nsIOutputStream.h"
+
+#include "mozilla/Logging.h"
+#if defined(PR_LOGGING)
+//
+// export NSPR_LOG_MODULES=Movemail:5
+//
+static PRLogModuleInfo *gMovemailLog = nullptr;
+#define LOG(args) MOZ_LOG(gMovemailLog, mozilla::LogLevel::Debug, args)
+#else
+#define LOG(args)
+#endif
+
+#define PREF_MAIL_ROOT_MOVEMAIL "mail.root.movemail" // old - for backward compatibility only
+#define PREF_MAIL_ROOT_MOVEMAIL_REL "mail.root.movemail-rel"
+
+#define LOCK_SUFFIX ".lock"
+#define MOZLOCK_SUFFIX ".mozlock"
+
+const char * gDefaultSpoolPaths[] = {
+ "/var/spool/mail/",
+ "/usr/spool/mail/",
+ "/var/mail/",
+ "/usr/mail/"
+};
+#define NUM_DEFAULT_SPOOL_PATHS (sizeof(gDefaultSpoolPaths)/sizeof(gDefaultSpoolPaths[0]))
+
+namespace {
+class MOZ_STACK_CLASS SpoolLock
+{
+public:
+ /**
+ * Try to create a lock for the spool file while we operate on it.
+ *
+ * @param aSpoolName The path to the spool file.
+ * @param aSeconds The number of seconds to retry the locking.
+ * @param aMovemail The movemail service requesting the lock.
+ * @param aServer The nsIMsgIncomingServer requesting the lock.
+ */
+ SpoolLock(nsACString *aSpoolPath, int aSeconds, nsMovemailService &aMovemail,
+ nsIMsgIncomingServer *aServer);
+
+ ~SpoolLock();
+
+ bool isLocked();
+
+private:
+ bool mLocked;
+ nsCString mSpoolName;
+ bool mUsingLockFile;
+ RefPtr<nsMovemailService> mOwningService;
+ nsCOMPtr<nsIMsgIncomingServer> mServer;
+
+ bool ObtainSpoolLock(unsigned int aSeconds);
+ bool YieldSpoolLock();
+};
+
+}
+
+nsMovemailService::nsMovemailService()
+{
+#if defined(PR_LOGGING)
+ if (!gMovemailLog)
+ gMovemailLog = PR_NewLogModule("Movemail");
+#endif
+ LOG(("nsMovemailService created: 0x%x\n", this));
+}
+
+nsMovemailService::~nsMovemailService()
+{}
+
+
+NS_IMPL_ISUPPORTS(nsMovemailService,
+ nsIMovemailService,
+ nsIMsgProtocolInfo)
+
+
+NS_IMETHODIMP
+nsMovemailService::CheckForNewMail(nsIUrlListener * aUrlListener,
+ nsIMsgFolder *inbox,
+ nsIMovemailIncomingServer *movemailServer,
+ nsIURI ** aURL)
+{
+ nsresult rv = NS_OK;
+ LOG(("nsMovemailService::CheckForNewMail\n"));
+ return rv;
+}
+
+void
+nsMovemailService::Error(const char* errorCode,
+ const char16_t **params,
+ uint32_t length)
+{
+ if (!mMsgWindow) return;
+
+ nsCOMPtr<nsIPrompt> dialog;
+ nsresult rv = mMsgWindow->GetPromptDialog(getter_AddRefs(dialog));
+ if (NS_FAILED(rv))
+ return;
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ if (!bundleService)
+ return;
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/localMsgs.properties", getter_AddRefs(bundle));
+ if (NS_FAILED(rv))
+ return;
+
+ nsString errStr;
+ // Format the error string if necessary
+ if (params)
+ bundle->FormatStringFromName(NS_ConvertASCIItoUTF16(errorCode).get(),
+ params, length, getter_Copies(errStr));
+ else
+ bundle->GetStringFromName(NS_ConvertASCIItoUTF16(errorCode).get(),
+ getter_Copies(errStr));
+
+ if (!errStr.IsEmpty()) {
+ dialog->Alert(nullptr, errStr.get());
+ }
+}
+
+SpoolLock::SpoolLock(nsACString *aSpoolName, int aSeconds,
+ nsMovemailService &aMovemail,
+ nsIMsgIncomingServer *aServer)
+: mLocked(false),
+ mSpoolName(*aSpoolName),
+ mOwningService(&aMovemail),
+ mServer(aServer)
+{
+ if (!ObtainSpoolLock(aSeconds)) {
+ NS_ConvertUTF8toUTF16 lockFile(mSpoolName);
+ lockFile.AppendLiteral(LOCK_SUFFIX);
+ const char16_t* params[] = { lockFile.get() };
+ mOwningService->Error("movemailCantCreateLock", params, 1);
+ return;
+ }
+ mServer->SetServerBusy(true);
+ mLocked = true;
+}
+
+SpoolLock::~SpoolLock() {
+ if (mLocked && !YieldSpoolLock()) {
+ NS_ConvertUTF8toUTF16 lockFile(mSpoolName);
+ lockFile.AppendLiteral(LOCK_SUFFIX);
+ const char16_t* params[] = { lockFile.get() };
+ mOwningService->Error("movemailCantDeleteLock", params, 1);
+ }
+ mServer->SetServerBusy(false);
+}
+
+bool
+SpoolLock::isLocked() {
+ return mLocked;
+}
+
+bool
+SpoolLock::ObtainSpoolLock(unsigned int aSeconds /* number of seconds to retry */)
+{
+ /*
+ * Locking procedures:
+ * If the directory is not writable, we want to use the appropriate system
+ * utilites to lock the file.
+ * If the directory is writable, we want to go through the create-and-link
+ * locking procedures to make it atomic for certain networked file systems.
+ * This involves creating a .mozlock file and attempting to hard-link it to
+ * the customary .lock file.
+ */
+ nsCOMPtr<nsIFile> spoolFile;
+ nsresult rv = NS_NewNativeLocalFile(mSpoolName,
+ true,
+ getter_AddRefs(spoolFile));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ nsCOMPtr<nsIFile> directory;
+ rv = spoolFile->GetParent(getter_AddRefs(directory));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ rv = directory->IsWritable(&mUsingLockFile);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ if (!mUsingLockFile) {
+ LOG(("Attempting to use kernel file lock"));
+ PRFileDesc *fd;
+ rv = spoolFile->OpenNSPRFileDesc(PR_RDWR, 0, &fd);
+ NS_ENSURE_SUCCESS(rv, false);
+ PRStatus lock_result;
+ unsigned int retry_count = 0;
+
+ do {
+ lock_result = PR_TLockFile(fd);
+
+ retry_count++;
+ LOG(("Attempt %d of %d to lock file", retry_count, aSeconds));
+ if (aSeconds > 0 && lock_result == PR_FAILURE) {
+ // pause 1sec, waiting for .lock to go away
+ PRIntervalTime sleepTime = 1000; // 1 second
+ PR_Sleep(sleepTime);
+ }
+ } while (lock_result == PR_FAILURE && retry_count < aSeconds);
+ LOG(("Lock result: %d", lock_result));
+ PR_Close(fd);
+ return lock_result == PR_SUCCESS;
+ }
+ // How to lock using files:
+ // step 1: create SPOOLNAME.mozlock
+ // 1a: can remove it if it already exists (probably crash-droppings)
+ // step 2: hard-link SPOOLNAME.mozlock to SPOOLNAME.lock for NFS atomicity
+ // 2a: if SPOOLNAME.lock is >60sec old then nuke it from orbit
+ // 2b: repeat step 2 until retry-count expired or hard-link succeeds
+ // step 3: remove SPOOLNAME.mozlock
+ // step 4: If step 2 hard-link failed, fail hard; we do not hold the lock
+ // DONE.
+ //
+ // (step 2a not yet implemented)
+
+ nsAutoCString mozlockstr(mSpoolName);
+ mozlockstr.AppendLiteral(MOZLOCK_SUFFIX);
+ nsAutoCString lockstr(mSpoolName);
+ lockstr.AppendLiteral(LOCK_SUFFIX);
+
+ // Create nsIFile for the spool.mozlock file
+ nsCOMPtr<nsIFile> tmplocfile;
+ rv = NS_NewNativeLocalFile(mozlockstr, true, getter_AddRefs(tmplocfile));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ // THOUGHT: hmm, perhaps use MakeUnique to generate us a unique mozlock?
+ // ... perhaps not, MakeUnique implementation looks racey -- use mktemp()?
+
+ // step 1: create SPOOLNAME.mozlock
+ rv = tmplocfile->Create(nsIFile::NORMAL_FILE_TYPE, 0666);
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
+ // can't create our .mozlock file... game over already
+ LOG(("Failed to create file %s\n", mozlockstr.get()));
+ return false;
+ }
+
+ // step 2: hard-link .mozlock file to .lock file (this wackiness
+ // is necessary for non-racey locking on NFS-mounted spool dirs)
+ // n.b. XPCOM utilities don't support hard-linking yet, so we
+ // skip out to <unistd.h> and the POSIX interface for link()
+ int link_result = 0;
+ unsigned int retry_count = 0;
+
+ do {
+ link_result = link(mozlockstr.get(), lockstr.get());
+
+ retry_count++;
+ LOG(("Attempt %d of %d to create lock file", retry_count, aSeconds));
+
+ if (aSeconds > 0 && link_result == -1) {
+ // pause 1sec, waiting for .lock to go away
+ PRIntervalTime sleepTime = 1000; // 1 second
+ PR_Sleep(sleepTime);
+ }
+ } while (link_result == -1 && retry_count < aSeconds);
+ LOG(("Link result: %d", link_result));
+
+ // step 3: remove .mozlock file, in any case
+ rv = tmplocfile->Remove(false /* non-recursive */);
+ if (NS_FAILED(rv)) {
+ // Could not delete our .mozlock file... very unusual, but
+ // not fatal.
+ LOG(("Unable to delete %s", mozlockstr.get()));
+ }
+
+ // step 4: now we know whether we succeeded or failed
+ return link_result == 0;
+}
+
+
+/**
+ * Remove our mail-spool-file lock (n.b. we should only try this if
+ * we're the ones who made the lock in the first place! I.e. if mLocked is true.)
+ */
+bool
+SpoolLock::YieldSpoolLock()
+{
+ LOG(("YieldSpoolLock(%s)", mSpoolName.get()));
+
+ if (!mUsingLockFile) {
+ nsCOMPtr<nsIFile> spoolFile;
+ nsresult rv = NS_NewNativeLocalFile(mSpoolName,
+ true,
+ getter_AddRefs(spoolFile));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ PRFileDesc *fd;
+ rv = spoolFile->OpenNSPRFileDesc(PR_RDWR, 0, &fd);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ bool unlockSucceeded = PR_UnlockFile(fd) == PR_SUCCESS;
+ PR_Close(fd);
+ if (unlockSucceeded)
+ LOG(("YieldSpoolLock was successful."));
+ return unlockSucceeded;
+ }
+
+ nsAutoCString lockstr(mSpoolName);
+ lockstr.AppendLiteral(LOCK_SUFFIX);
+
+ nsresult rv;
+
+ // Create nsIFile for the spool.lock file
+ nsCOMPtr<nsIFile> locklocfile;
+ rv = NS_NewNativeLocalFile(lockstr, true, getter_AddRefs(locklocfile));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ // Check if the lock file exists
+ bool exists;
+ rv = locklocfile->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ // Delete the file if it exists
+ if (exists) {
+ rv = locklocfile->Remove(false /* non-recursive */);
+ NS_ENSURE_SUCCESS(rv, false);
+ }
+
+ LOG(("YieldSpoolLock was successful."));
+
+ // Success.
+ return true;
+}
+
+static nsresult
+LocateSpoolFile(nsACString & spoolPath)
+{
+ bool isFile;
+ nsresult rv;
+
+ nsCOMPtr<nsIFile> spoolFile;
+ rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(spoolFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ char * mailEnv = PR_GetEnv("MAIL");
+ char * userEnv = PR_GetEnv("USER");
+ if (!userEnv)
+ userEnv = PR_GetEnv("USERNAME");
+
+ if (mailEnv) {
+ rv = spoolFile->InitWithNativePath(nsDependentCString(mailEnv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = spoolFile->IsFile(&isFile);
+ if (NS_SUCCEEDED(rv) && isFile)
+ spoolPath = mailEnv;
+ }
+ else if (userEnv) {
+ // Try to build the mailbox path from the username and a number
+ // of guessed spool directory paths.
+ nsAutoCString tmpPath;
+ uint32_t i;
+ for (i = 0; i < NUM_DEFAULT_SPOOL_PATHS; i++) {
+ tmpPath = gDefaultSpoolPaths[i];
+ tmpPath += userEnv;
+ rv = spoolFile->InitWithNativePath(tmpPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = spoolFile->IsFile(&isFile);
+ if (NS_SUCCEEDED(rv) && isFile) {
+ spoolPath = tmpPath;
+ break;
+ }
+ }
+ }
+
+ return rv;
+}
+
+nsresult
+nsMovemailService::GetNewMail(nsIMsgWindow *aMsgWindow,
+ nsIUrlListener* /* aUrlListener */,
+ nsIMsgFolder* /* aMsgFolder */,
+ nsIMovemailIncomingServer *aMovemailServer,
+ nsIURI ** /* aURL */)
+{
+ LOG(("nsMovemailService::GetNewMail"));
+
+ NS_ENSURE_ARG_POINTER(aMovemailServer);
+ // It is OK if aMsgWindow is null.
+ mMsgWindow = aMsgWindow;
+
+ nsresult rv;
+
+ nsCOMPtr<nsIMsgIncomingServer> in_server =
+ do_QueryInterface(aMovemailServer, &rv);
+ NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && in_server,
+ NS_MSG_INVALID_OR_MISSING_SERVER);
+
+ // Attempt to locate the mail spool file
+ nsAutoCString spoolPath;
+ rv = in_server->GetCharValue("spoolDir", spoolPath);
+ if (NS_FAILED(rv) || spoolPath.IsEmpty())
+ rv = LocateSpoolFile(spoolPath);
+ if (NS_FAILED(rv) || spoolPath.IsEmpty()) {
+ Error("movemailSpoolFileNotFound", nullptr, 0);
+ return NS_ERROR_FAILURE;
+ }
+
+ NS_ConvertUTF8toUTF16 wideSpoolPath(spoolPath);
+ const char16_t* spoolPathString[] = { wideSpoolPath.get() };
+
+ // Create an input stream for the spool file
+ nsCOMPtr<nsIFile> spoolFile;
+ rv = NS_NewNativeLocalFile(spoolPath, true, getter_AddRefs(spoolFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIInputStream> spoolInputStream;
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(spoolInputStream), spoolFile);
+ if (NS_FAILED(rv)) {
+ Error("movemailCantOpenSpoolFile", spoolPathString, 1);
+ return rv;
+ }
+
+ // Get a line input interface for the spool file
+ nsCOMPtr<nsILineInputStream> lineInputStream =
+ do_QueryInterface(spoolInputStream, &rv);
+ NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && lineInputStream, rv);
+
+ nsCOMPtr<nsIMsgFolder> serverFolder;
+ nsCOMPtr<nsIMsgFolder> inbox;
+
+ rv = in_server->GetRootFolder(getter_AddRefs(serverFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = serverFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox,
+ getter_AddRefs(inbox));
+ NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && inbox, rv);
+
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = in_server->GetMsgStore(getter_AddRefs(msgStore));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // create a new mail parser
+ RefPtr<nsParseNewMailState> newMailParser = new nsParseNewMailState;
+
+ // Try and obtain the lock for the spool file.
+ SpoolLock lock(&spoolPath, 5, *this, in_server);
+ if (!lock.isLocked())
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIMsgDBHdr> newHdr;
+ nsCOMPtr<nsIOutputStream> outputStream;
+
+ // MIDDLE of the FUN : consume the mailbox data.
+ bool isMore = true;
+ nsAutoCString buffer;
+ uint32_t bytesWritten = 0;
+ while (isMore &&
+ NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore)))
+ {
+ // If first string is empty and we're now at EOF then abort parsing.
+ if (buffer.IsEmpty() && !isMore && !bytesWritten) {
+ LOG(("Empty spool file"));
+ break;
+ }
+
+ buffer.AppendLiteral(MSG_LINEBREAK);
+
+ if (isMore && StringBeginsWith(buffer, NS_LITERAL_CSTRING("From "))) {
+ // Finish previous header and message, if any.
+ if (newHdr) {
+ outputStream->Flush();
+ newMailParser->PublishMsgHeader(nullptr);
+ rv = msgStore->FinishNewMessage(outputStream, newHdr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ newMailParser->Clear();
+ }
+ bool reusable;
+ rv = msgStore->GetNewMsgOutputStream(inbox, getter_AddRefs(newHdr),
+ &reusable, getter_AddRefs(outputStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = newMailParser->Init(serverFolder, inbox,
+ nullptr, newHdr, outputStream);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ if (!outputStream) {
+ // If we do not have outputStream here, something bad happened.
+ // We probably didn't find the proper message start delimiter "From "
+ // and are now reading in the middle of a message. Bail out.
+ Error("movemailCantParseSpool", spoolPathString, 1);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ newMailParser->HandleLine(buffer.BeginWriting(), buffer.Length());
+ rv = outputStream->Write(buffer.get(), buffer.Length(), &bytesWritten);
+ NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && (bytesWritten == buffer.Length()),
+ NS_ERROR_FAILURE);
+
+ // "From " lines delimit messages, start a new one here.
+ if (isMore && StringBeginsWith(buffer, NS_LITERAL_CSTRING("From "))) {
+ buffer.AssignLiteral("X-Mozilla-Status: 8000" MSG_LINEBREAK);
+ newMailParser->HandleLine(buffer.BeginWriting(), buffer.Length());
+ rv = outputStream->Write(buffer.get(), buffer.Length(), &bytesWritten);
+ NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && (bytesWritten == buffer.Length()),
+ NS_ERROR_FAILURE);
+
+ buffer.AssignLiteral("X-Mozilla-Status2: 00000000" MSG_LINEBREAK);
+ newMailParser->HandleLine(buffer.BeginWriting(), buffer.Length());
+ rv = outputStream->Write(buffer.get(), buffer.Length(), &bytesWritten);
+ NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && (bytesWritten == buffer.Length()),
+ NS_ERROR_FAILURE);
+ }
+ }
+ if (outputStream) {
+ outputStream->Flush();
+ newMailParser->PublishMsgHeader(nullptr);
+ newMailParser->OnStopRequest(nullptr, nullptr, NS_OK);
+ rv = msgStore->FinishNewMessage(outputStream, newHdr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ outputStream->Close();
+ }
+ // Truncate the spool file as we parsed it successfully.
+ rv = spoolFile->SetFileSize(0);
+ if (NS_FAILED(rv)) {
+ Error("movemailCantTruncateSpoolFile", spoolPathString, 1);
+ }
+
+ LOG(("GetNewMail returning rv=%d", rv));
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsMovemailService::SetDefaultLocalPath(nsIFile *aPath)
+{
+ NS_ENSURE_ARG(aPath);
+ return NS_SetPersistentFile(PREF_MAIL_ROOT_MOVEMAIL_REL, PREF_MAIL_ROOT_MOVEMAIL, aPath);
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetDefaultLocalPath(nsIFile ** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+
+ nsresult rv;
+ bool havePref;
+ nsCOMPtr<nsIFile> localFile;
+ rv = NS_GetPersistentFile(PREF_MAIL_ROOT_MOVEMAIL_REL,
+ PREF_MAIL_ROOT_MOVEMAIL,
+ NS_APP_MAIL_50_DIR,
+ havePref,
+ getter_AddRefs(localFile));
+ if (NS_FAILED(rv)) return rv;
+
+ bool exists;
+ rv = localFile->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && !exists)
+ rv = localFile->Create(nsIFile::DIRECTORY_TYPE, 0775);
+ if (NS_FAILED(rv)) return rv;
+
+ if (!havePref || !exists) {
+ rv = NS_SetPersistentFile(PREF_MAIL_ROOT_MOVEMAIL_REL, PREF_MAIL_ROOT_MOVEMAIL, localFile);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to set root dir pref.");
+ }
+
+ NS_IF_ADDREF(*aResult = localFile);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsMovemailService::GetServerIID(nsIID* *aServerIID)
+{
+ *aServerIID = new nsIID(NS_GET_IID(nsIMovemailIncomingServer));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetRequiresUsername(bool *aRequiresUsername)
+{
+ NS_ENSURE_ARG_POINTER(aRequiresUsername);
+ *aRequiresUsername = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetPreflightPrettyNameWithEmailAddress(bool *aPreflightPrettyNameWithEmailAddress)
+{
+ NS_ENSURE_ARG_POINTER(aPreflightPrettyNameWithEmailAddress);
+ *aPreflightPrettyNameWithEmailAddress = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetCanLoginAtStartUp(bool *aCanLoginAtStartUp)
+{
+ NS_ENSURE_ARG_POINTER(aCanLoginAtStartUp);
+ *aCanLoginAtStartUp = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetCanDelete(bool *aCanDelete)
+{
+ NS_ENSURE_ARG_POINTER(aCanDelete);
+ *aCanDelete = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetCanGetMessages(bool *aCanGetMessages)
+{
+ NS_ENSURE_ARG_POINTER(aCanGetMessages);
+ *aCanGetMessages = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetCanGetIncomingMessages(bool *aCanGetIncomingMessages)
+{
+ NS_ENSURE_ARG_POINTER(aCanGetIncomingMessages);
+ *aCanGetIncomingMessages = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetCanDuplicate(bool *aCanDuplicate)
+{
+ NS_ENSURE_ARG_POINTER(aCanDuplicate);
+ *aCanDuplicate = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetDefaultDoBiff(bool *aDoBiff)
+{
+ NS_ENSURE_ARG_POINTER(aDoBiff);
+ // by default, do biff for movemail
+ *aDoBiff = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetDefaultServerPort(bool isSecure, int32_t *aDefaultPort)
+{
+ NS_ENSURE_ARG_POINTER(aDefaultPort);
+ *aDefaultPort = -1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetShowComposeMsgLink(bool *showComposeMsgLink)
+{
+ NS_ENSURE_ARG_POINTER(showComposeMsgLink);
+ *showComposeMsgLink = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMovemailService::GetFoldersCreatedAsync(bool *aAsyncCreation)
+{
+ NS_ENSURE_ARG_POINTER(aAsyncCreation);
+ *aAsyncCreation = false;
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsMovemailService.h b/mailnews/local/src/nsMovemailService.h
new file mode 100644
index 000000000..5b1ae1053
--- /dev/null
+++ b/mailnews/local/src/nsMovemailService.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMovemailService_h___
+#define nsMovemailService_h___
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+
+#include "nsIMovemailService.h"
+#include "nsIMsgProtocolInfo.h"
+#include "nsIMsgWindow.h"
+
+class nsMovemailService : public nsIMsgProtocolInfo, public nsIMovemailService
+{
+public:
+ nsMovemailService();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMOVEMAILSERVICE
+ NS_DECL_NSIMSGPROTOCOLINFO
+
+ void Error(const char* errorCode, const char16_t **params, uint32_t length);
+
+private:
+ virtual ~nsMovemailService();
+ nsCOMPtr<nsIMsgWindow> mMsgWindow;
+};
+
+#endif /* nsMovemailService_h___ */
diff --git a/mailnews/local/src/nsMsgBrkMBoxStore.cpp b/mailnews/local/src/nsMsgBrkMBoxStore.cpp
new file mode 100644
index 000000000..6eb3063ad
--- /dev/null
+++ b/mailnews/local/src/nsMsgBrkMBoxStore.cpp
@@ -0,0 +1,1124 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ Class for handling Berkeley Mailbox stores.
+*/
+
+#include "prlog.h"
+#include "msgCore.h"
+#include "nsMsgBrkMBoxStore.h"
+#include "nsIMsgFolder.h"
+#include "nsMsgFolderFlags.h"
+#include "nsILocalMailIncomingServer.h"
+#include "nsIMsgLocalMailFolder.h"
+#include "nsCOMArray.h"
+#include "nsIFile.h"
+#include "nsIMsgHdr.h"
+#include "nsNetUtil.h"
+#include "nsIMsgDatabase.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsMsgUtils.h"
+#include "nsMsgDBCID.h"
+#include "nsIDBFolderInfo.h"
+#include "nsIArray.h"
+#include "nsArrayUtils.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsMailHeaders.h"
+#include "nsReadLine.h"
+#include "nsParseMailbox.h"
+#include "nsIMailboxService.h"
+#include "nsMsgLocalCID.h"
+#include "nsIMsgFolderCompactor.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "mozilla/Preferences.h"
+#include "prprf.h"
+#include <cstdlib> // for std::abs(int/long)
+#include <cmath> // for std::abs(float/double)
+
+nsMsgBrkMBoxStore::nsMsgBrkMBoxStore()
+{
+}
+
+nsMsgBrkMBoxStore::~nsMsgBrkMBoxStore()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsMsgBrkMBoxStore, nsIMsgPluggableStore)
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::DiscoverSubFolders(nsIMsgFolder *aParentFolder,
+ bool aDeep)
+{
+ NS_ENSURE_ARG_POINTER(aParentFolder);
+
+ nsCOMPtr<nsIFile> path;
+ nsresult rv = aParentFolder->GetFilePath(getter_AddRefs(path));
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool exists;
+ path->Exists(&exists);
+ if (!exists) {
+ rv = path->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return AddSubFolders(aParentFolder, path, aDeep);
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::CreateFolder(nsIMsgFolder *aParent,
+ const nsAString &aFolderName,
+ nsIMsgFolder **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+ NS_ENSURE_ARG_POINTER(aResult);
+ if (aFolderName.IsEmpty())
+ return NS_MSG_ERROR_INVALID_FOLDER_NAME;
+
+ nsCOMPtr<nsIFile> path;
+ nsCOMPtr<nsIMsgFolder> child;
+ nsresult rv = aParent->GetFilePath(getter_AddRefs(path));
+ if (NS_FAILED(rv))
+ return rv;
+ //Get a directory based on our current path.
+ rv = CreateDirectoryForFolder(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Now we have a valid directory or we have returned.
+ // Make sure the new folder name is valid
+ nsAutoString safeFolderName(aFolderName);
+ NS_MsgHashIfNecessary(safeFolderName);
+
+ path->Append(safeFolderName);
+ bool exists;
+ path->Exists(&exists);
+ if (exists) //check this because localized names are different from disk names
+ return NS_MSG_FOLDER_EXISTS;
+
+ rv = path->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ //GetFlags and SetFlags in AddSubfolder will fail because we have no db at
+ // this point but mFlags is set.
+ rv = aParent->AddSubfolder(safeFolderName, getter_AddRefs(child));
+ if (!child || NS_FAILED(rv))
+ {
+ path->Remove(false);
+ return rv;
+ }
+ // Create an empty database for this mail folder, set its name from the user
+ nsCOMPtr<nsIMsgDBService> msgDBService =
+ do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+ if (msgDBService)
+ {
+ nsCOMPtr<nsIMsgDatabase> unusedDB;
+ rv = msgDBService->OpenFolderDB(child, true, getter_AddRefs(unusedDB));
+ if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
+ rv = msgDBService->CreateNewDB(child, getter_AddRefs(unusedDB));
+
+ if ((NS_SUCCEEDED(rv) || rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE) &&
+ unusedDB)
+ {
+ //need to set the folder name
+ nsCOMPtr<nsIDBFolderInfo> folderInfo;
+ rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
+ if (NS_SUCCEEDED(rv))
+ folderInfo->SetMailboxName(safeFolderName);
+
+ unusedDB->SetSummaryValid(true);
+ unusedDB->Close(true);
+ aParent->UpdateSummaryTotals(true);
+ }
+ else
+ {
+ path->Remove(false);
+ rv = NS_MSG_CANT_CREATE_FOLDER;
+ }
+ }
+ child.forget(aResult);
+ return rv;
+}
+
+// Get the current attributes of the mbox file, corrected for caching
+void nsMsgBrkMBoxStore::GetMailboxModProperties(nsIMsgFolder *aFolder,
+ int64_t *aSize, uint32_t *aDate)
+{
+ // We'll simply return 0 on errors.
+ *aDate = 0;
+ *aSize = 0;
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ rv = pathFile->GetFileSize(aSize);
+ if (NS_FAILED(rv))
+ return; // expected result for virtual folders
+
+ PRTime lastModTime;
+ rv = pathFile->GetLastModifiedTime(&lastModTime);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ *aDate = (uint32_t) (lastModTime / PR_MSEC_PER_SEC);
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::HasSpaceAvailable(nsIMsgFolder *aFolder,
+ int64_t aSpaceRequested,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_ARG_POINTER(aFolder);
+
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool allow4GBfolders = mozilla::Preferences::GetBool("mailnews.allowMboxOver4GB", true);
+
+ if (!allow4GBfolders) {
+ // Allow the mbox to only reach 0xFFC00000 = 4 GiB - 4 MiB.
+ int64_t fileSize;
+ rv = pathFile->GetFileSize(&fileSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = ((fileSize + aSpaceRequested) < 0xFFC00000LL);
+ if (!*aResult)
+ return NS_ERROR_FILE_TOO_BIG;
+ }
+
+ *aResult = DiskSpaceAvailableInStore(pathFile, aSpaceRequested);
+ if (!*aResult)
+ return NS_ERROR_FILE_DISK_FULL;
+
+ return NS_OK;
+}
+
+static bool gGotGlobalPrefs = false;
+static int32_t gTimeStampLeeway = 60;
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::IsSummaryFileValid(nsIMsgFolder *aFolder,
+ nsIMsgDatabase *aDB,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aDB);
+ NS_ENSURE_ARG_POINTER(aResult);
+ // We only check local folders for db validity.
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder(do_QueryInterface(aFolder));
+ if (!localFolder)
+ {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIDBFolderInfo> folderInfo;
+ rv = aDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
+ NS_ENSURE_SUCCESS(rv, rv);
+ int64_t folderSize;
+ uint32_t folderDate;
+ int32_t numUnreadMessages;
+
+ *aResult = false;
+
+ folderInfo->GetNumUnreadMessages(&numUnreadMessages);
+ folderInfo->GetFolderSize(&folderSize);
+ folderInfo->GetFolderDate(&folderDate);
+
+ int64_t fileSize = 0;
+ uint32_t actualFolderTimeStamp = 0;
+ GetMailboxModProperties(aFolder, &fileSize, &actualFolderTimeStamp);
+
+ if (folderSize == fileSize && numUnreadMessages >= 0)
+ {
+ if (!folderSize)
+ {
+ *aResult = true;
+ return NS_OK;
+ }
+ if (!gGotGlobalPrefs)
+ {
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (pPrefBranch)
+ {
+ rv = pPrefBranch->GetIntPref("mail.db_timestamp_leeway", &gTimeStampLeeway);
+ gGotGlobalPrefs = true;
+ }
+ }
+ // if those values are ok, check time stamp
+ if (gTimeStampLeeway == 0)
+ *aResult = folderDate == actualFolderTimeStamp;
+ else
+ *aResult = std::abs((int32_t) (actualFolderTimeStamp - folderDate)) <= gTimeStampLeeway;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::SetSummaryFileValid(nsIMsgFolder *aFolder,
+ nsIMsgDatabase *aDB,
+ bool aValid)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aDB);
+ // We only need to do this for local folders.
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder(do_QueryInterface(aFolder));
+ if (!localFolder)
+ return NS_OK;
+
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIDBFolderInfo> folderInfo;
+ rv = aDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool exists;
+ pathFile->Exists(&exists);
+ if (!exists)
+ return NS_MSG_ERROR_FOLDER_MISSING;
+
+ if (aValid)
+ {
+ uint32_t actualFolderTimeStamp;
+ int64_t fileSize;
+ GetMailboxModProperties(aFolder, &fileSize, &actualFolderTimeStamp);
+ folderInfo->SetFolderSize(fileSize);
+ folderInfo->SetFolderDate(actualFolderTimeStamp);
+ }
+ else
+ {
+ folderInfo->SetVersion(0); // that ought to do the trick.
+ }
+ aDB->Commit(nsMsgDBCommitType::kLargeCommit);
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::DeleteFolder(nsIMsgFolder *aFolder)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ //Delete mailbox
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ pathFile->Remove(false);
+
+ bool isDirectory = false;
+ pathFile->IsDirectory(&isDirectory);
+ if (!isDirectory)
+ {
+ nsAutoString leafName;
+ pathFile->GetLeafName(leafName);
+ leafName.AppendLiteral(FOLDER_SUFFIX);
+ pathFile->SetLeafName(leafName);
+ }
+ isDirectory = false;
+ pathFile->IsDirectory(&isDirectory);
+ //If this is a directory, then remove it.
+ return isDirectory ? pathFile->Remove(true) : NS_OK;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::RenameFolder(nsIMsgFolder *aFolder,
+ const nsAString & aNewName,
+ nsIMsgFolder **aNewFolder)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aNewFolder);
+
+ uint32_t numChildren;
+ aFolder->GetNumSubFolders(&numChildren);
+ nsString existingName;
+ aFolder->GetName(existingName);
+
+ nsCOMPtr<nsIFile> oldPathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(oldPathFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIMsgFolder> parentFolder;
+ rv = aFolder->GetParent(getter_AddRefs(parentFolder));
+ if (!parentFolder)
+ return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr<nsISupports> parentSupport = do_QueryInterface(parentFolder);
+
+ nsCOMPtr<nsIFile> oldSummaryFile;
+ rv = aFolder->GetSummaryFile(getter_AddRefs(oldSummaryFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> dirFile;
+ oldPathFile->Clone(getter_AddRefs(dirFile));
+
+ if (numChildren > 0)
+ {
+ rv = CreateDirectoryForFolder(dirFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsAutoString safeName(aNewName);
+ NS_MsgHashIfNecessary(safeName);
+
+ nsAutoCString oldLeafName;
+ oldPathFile->GetNativeLeafName(oldLeafName);
+
+ nsCOMPtr<nsIFile> parentPathFile;
+ parentFolder->GetFilePath(getter_AddRefs(parentPathFile));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool isDirectory = false;
+ parentPathFile->IsDirectory(&isDirectory);
+ if (!isDirectory)
+ {
+ nsAutoString leafName;
+ parentPathFile->GetLeafName(leafName);
+ leafName.AppendLiteral(FOLDER_SUFFIX);
+ rv = parentPathFile->SetLeafName(leafName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ aFolder->ForceDBClosed();
+ //save off dir name before appending .msf
+ rv = oldPathFile->MoveTo(nullptr, safeName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsString dbName(safeName);
+ dbName += NS_LITERAL_STRING(SUMMARY_SUFFIX);
+ oldSummaryFile->MoveTo(nullptr, dbName);
+
+ if (numChildren > 0)
+ {
+ // rename "*.sbd" directory
+ nsAutoString newNameDirStr(safeName);
+ newNameDirStr += NS_LITERAL_STRING(".sbd");
+ dirFile->MoveTo(nullptr, newNameDirStr);
+ }
+
+ return parentFolder->AddSubfolder(safeName, aNewFolder);
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::CopyFolder(nsIMsgFolder *aSrcFolder,
+ nsIMsgFolder *aDstFolder,
+ bool aIsMoveFolder,
+ nsIMsgWindow *aMsgWindow,
+ nsIMsgCopyServiceListener *aListener,
+ const nsAString &aNewName)
+{
+ NS_ENSURE_ARG_POINTER(aSrcFolder);
+ NS_ENSURE_ARG_POINTER(aDstFolder);
+
+ nsAutoString folderName;
+ if (aNewName.IsEmpty())
+ aSrcFolder->GetName(folderName);
+ else
+ folderName.Assign(aNewName);
+
+ nsAutoString safeFolderName(folderName);
+ NS_MsgHashIfNecessary(safeFolderName);
+ nsCOMPtr<nsIMsgLocalMailFolder> localSrcFolder(do_QueryInterface(aSrcFolder));
+ nsCOMPtr<nsIMsgDatabase> srcDB;
+ if (localSrcFolder)
+ localSrcFolder->GetDatabaseWOReparse(getter_AddRefs(srcDB));
+ bool summaryValid = !!srcDB;
+ srcDB = nullptr;
+ aSrcFolder->ForceDBClosed();
+
+ nsCOMPtr<nsIFile> oldPath;
+ nsresult rv = aSrcFolder->GetFilePath(getter_AddRefs(oldPath));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIFile> summaryFile;
+ GetSummaryFileLocation(oldPath, getter_AddRefs(summaryFile));
+
+ nsCOMPtr<nsIFile> newPath;
+ rv = aDstFolder->GetFilePath(getter_AddRefs(newPath));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool newPathIsDirectory = false;
+ newPath->IsDirectory(&newPathIsDirectory);
+ if (!newPathIsDirectory)
+ {
+ AddDirectorySeparator(newPath);
+ rv = newPath->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ if (rv == NS_ERROR_FILE_ALREADY_EXISTS)
+ rv = NS_OK;
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIFile> origPath;
+ oldPath->Clone(getter_AddRefs(origPath));
+
+ //copying necessary for aborting.... if failure return
+ rv = oldPath->CopyTo(newPath, safeFolderName);
+ NS_ENSURE_SUCCESS(rv, rv); // Will fail if a file by that name exists
+
+ // Copy to dir can fail if filespec does not exist. If copy fails, we test
+ // if the filespec exist or not, if it does not that's ok, we continue
+ // without copying it. If it fails and filespec exist and is not zero sized
+ // there is real problem
+ // Copy the file to the new dir
+ nsAutoString dbName(safeFolderName);
+ dbName += NS_LITERAL_STRING(SUMMARY_SUFFIX);
+ rv = summaryFile->CopyTo(newPath, dbName);
+ if (NS_FAILED(rv)) // Test if the copy is successful
+ {
+ // Test if the filespec has data
+ bool exists;
+ int64_t fileSize;
+ summaryFile->Exists(&exists);
+ summaryFile->GetFileSize(&fileSize);
+ if (exists && fileSize > 0)
+ NS_ENSURE_SUCCESS(rv, rv); // Yes, it should have worked !
+ // else case is filespec is zero sized, no need to copy it,
+ // not an error
+ }
+
+ nsCOMPtr<nsIMsgFolder> newMsgFolder;
+ rv = aDstFolder->AddSubfolder(safeFolderName, getter_AddRefs(newMsgFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // linux and mac are not good about maintaining the file stamp when copying
+ // folders around. So if the source folder db is good, set the dest db as
+ // good too.
+ nsCOMPtr<nsIMsgDatabase> destDB;
+ if (summaryValid)
+ {
+ nsAutoString folderLeafName;
+ origPath->GetLeafName(folderLeafName);
+ newPath->Append(folderLeafName);
+ nsCOMPtr<nsIMsgDBService> msgDBService =
+ do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = msgDBService->OpenMailDBFromFile(newPath, newMsgFolder, false,
+ true, getter_AddRefs(destDB));
+ if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE && destDB)
+ destDB->SetSummaryValid(true);
+ }
+ newMsgFolder->SetPrettyName(folderName);
+ uint32_t flags;
+ aSrcFolder->GetFlags(&flags);
+ newMsgFolder->SetFlags(flags);
+ bool changed = false;
+ rv = aSrcFolder->MatchOrChangeFilterDestination(newMsgFolder, true, &changed);
+ if (changed)
+ aSrcFolder->AlertFilterChanged(aMsgWindow);
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = aSrcFolder->GetSubFolders(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Copy subfolders to the new location
+ nsresult copyStatus = NS_OK;
+ nsCOMPtr<nsIMsgLocalMailFolder> localNewFolder(do_QueryInterface(newMsgFolder, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ bool hasMore;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore &&
+ NS_SUCCEEDED(copyStatus))
+ {
+ nsCOMPtr<nsISupports> item;
+ enumerator->GetNext(getter_AddRefs(item));
+
+ nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(item));
+ if (!folder)
+ continue;
+
+ copyStatus = localNewFolder->CopyFolderLocal(folder, false,
+ aMsgWindow, aListener);
+ // Test if the call succeeded, if not we have to stop recursive call
+ if (NS_FAILED(copyStatus))
+ {
+ // Copy failed we have to notify caller to handle the error and stop
+ // moving the folders. In case this happens to the topmost level of
+ // recursive call, then we just need to break from the while loop and
+ // go to error handling code.
+ if (!aIsMoveFolder)
+ return copyStatus;
+ break;
+ }
+ }
+ }
+
+ if (aIsMoveFolder && NS_SUCCEEDED(copyStatus))
+ {
+ if (localNewFolder)
+ {
+ nsCOMPtr<nsISupports> srcSupport(do_QueryInterface(aSrcFolder));
+ localNewFolder->OnCopyCompleted(srcSupport, true);
+ }
+
+ // Notify the "folder" that was dragged and dropped has been created. No
+ // need to do this for its subfolders. isMoveFolder will be true for folder.
+ aDstFolder->NotifyItemAdded(newMsgFolder);
+
+ nsCOMPtr<nsIMsgFolder> msgParent;
+ aSrcFolder->GetParent(getter_AddRefs(msgParent));
+ aSrcFolder->SetParent(nullptr);
+ if (msgParent)
+ {
+ // The files have already been moved, so delete storage false
+ msgParent->PropagateDelete(aSrcFolder, false, aMsgWindow);
+ oldPath->Remove(false); //berkeley mailbox
+ // We need to force closed the source db
+ nsCOMPtr<nsIMsgDatabase> srcDB;
+ aSrcFolder->Delete();
+
+ nsCOMPtr<nsIFile> parentPath;
+ rv = msgParent->GetFilePath(getter_AddRefs(parentPath));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ AddDirectorySeparator(parentPath);
+ nsCOMPtr<nsISimpleEnumerator> children;
+ parentPath->GetDirectoryEntries(getter_AddRefs(children));
+ bool more;
+ // checks if the directory is empty or not
+ if (children && NS_SUCCEEDED(children->HasMoreElements(&more)) && !more)
+ parentPath->Remove(true);
+ }
+ }
+ else
+ {
+ // This is the case where the copy of a subfolder failed.
+ // We have to delete the newDirectory tree to make a "rollback".
+ // Someone should add a popup to warn the user that the move was not
+ // possible.
+ if (aIsMoveFolder && NS_FAILED(copyStatus))
+ {
+ nsCOMPtr<nsIMsgFolder> msgParent;
+ newMsgFolder->ForceDBClosed();
+ newMsgFolder->GetParent(getter_AddRefs(msgParent));
+ newMsgFolder->SetParent(nullptr);
+ if (msgParent)
+ {
+ msgParent->PropagateDelete(newMsgFolder, false, aMsgWindow);
+ newMsgFolder->Delete();
+ newMsgFolder->ForceDBClosed();
+ AddDirectorySeparator(newPath);
+ newPath->Remove(true); //berkeley mailbox
+ }
+ return NS_ERROR_FAILURE;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::GetNewMsgOutputStream(nsIMsgFolder *aFolder,
+ nsIMsgDBHdr **aNewMsgHdr,
+ bool *aReusable,
+ nsIOutputStream **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aNewMsgHdr);
+ NS_ENSURE_ARG_POINTER(aReusable);
+ NS_ENSURE_ARG_POINTER(aResult);
+
+#ifdef _DEBUG
+ NS_ASSERTION(m_streamOutstandingFolder != aFolder, "didn't finish prev msg");
+ m_streamOutstandingFolder = aFolder;
+#endif
+ *aReusable = true;
+ nsCOMPtr<nsIFile> mboxFile;
+ aFolder->GetFilePath(getter_AddRefs(mboxFile));
+ nsCOMPtr<nsIMsgDatabase> db;
+ aFolder->GetMsgDatabase(getter_AddRefs(db));
+ if (!db && !*aNewMsgHdr)
+ NS_WARNING("no db, and no message header");
+ bool exists;
+ nsresult rv;
+ mboxFile->Exists(&exists);
+ if (!exists) {
+ rv = mboxFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCString URI;
+ aFolder->GetURI(URI);
+ nsCOMPtr<nsISeekableStream> seekable;
+ if (m_outputStreams.Get(URI, aResult))
+ {
+ seekable = do_QueryInterface(*aResult, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
+ if (NS_FAILED(rv))
+ {
+ m_outputStreams.Remove(URI);
+ NS_RELEASE(*aResult);
+ }
+ }
+ if (!*aResult)
+ {
+ rv = MsgGetFileStream(mboxFile, aResult);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed opening offline store for output");
+ if (NS_FAILED(rv))
+ printf("failed opening offline store for %s\n", URI.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ seekable = do_QueryInterface(*aResult, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ m_outputStreams.Put(URI, *aResult);
+ }
+ int64_t filePos;
+ seekable->Tell(&filePos);
+
+ if (db && !*aNewMsgHdr)
+ {
+ db->CreateNewHdr(nsMsgKey_None, aNewMsgHdr);
+ }
+
+ if (*aNewMsgHdr)
+ {
+ char storeToken[100];
+ PR_snprintf(storeToken, sizeof(storeToken), "%lld", filePos);
+ (*aNewMsgHdr)->SetMessageOffset(filePos);
+ (*aNewMsgHdr)->SetStringProperty("storeToken", storeToken);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::DiscardNewMessage(nsIOutputStream *aOutputStream,
+ nsIMsgDBHdr *aNewHdr)
+{
+ NS_ENSURE_ARG_POINTER(aOutputStream);
+ NS_ENSURE_ARG_POINTER(aNewHdr);
+#ifdef _DEBUG
+ m_streamOutstandingFolder = nullptr;
+#endif
+ uint64_t hdrOffset;
+ aNewHdr->GetMessageOffset(&hdrOffset);
+ aOutputStream->Close();
+ nsCOMPtr<nsIFile> mboxFile;
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsresult rv = aNewHdr->GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ folder->GetFilePath(getter_AddRefs(mboxFile));
+ return mboxFile->SetFileSize(hdrOffset);
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::FinishNewMessage(nsIOutputStream *aOutputStream,
+ nsIMsgDBHdr *aNewHdr)
+{
+#ifdef _DEBUG
+ m_streamOutstandingFolder = nullptr;
+#endif
+ NS_ENSURE_ARG_POINTER(aOutputStream);
+// NS_ENSURE_ARG_POINTER(aNewHdr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::MoveNewlyDownloadedMessage(nsIMsgDBHdr *aNewHdr,
+ nsIMsgFolder *aDestFolder,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aNewHdr);
+ NS_ENSURE_ARG_POINTER(aDestFolder);
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::GetMsgInputStream(nsIMsgFolder *aMsgFolder,
+ const nsACString &aMsgToken,
+ int64_t *aOffset,
+ nsIMsgDBHdr *aMsgHdr,
+ bool *aReusable,
+ nsIInputStream **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aMsgFolder);
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_ARG_POINTER(aOffset);
+
+ // If there is no store token, then we set it to the existing message offset.
+ if (aMsgToken.IsEmpty())
+ {
+ uint64_t offset;
+ NS_ENSURE_ARG_POINTER(aMsgHdr);
+ aMsgHdr->GetMessageOffset(&offset);
+ *aOffset = int64_t(offset);
+ char storeToken[100];
+ PR_snprintf(storeToken, sizeof(storeToken), "%lld", *aOffset);
+ aMsgHdr->SetStringProperty("storeToken", storeToken);
+ }
+ else
+ *aOffset = ParseUint64Str(PromiseFlatCString(aMsgToken).get());
+ *aReusable = true;
+ nsCString URI;
+ nsCOMPtr<nsIFile> mboxFile;
+
+ aMsgFolder->GetURI(URI);
+ aMsgFolder->GetFilePath(getter_AddRefs(mboxFile));
+ return NS_NewLocalFileInputStream(aResult, mboxFile);
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::DeleteMessages(nsIArray *aHdrArray)
+{
+ return ChangeFlags(aHdrArray, nsMsgMessageFlags::Expunged, true);
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::CopyMessages(bool isMove, nsIArray *aHdrArray,
+ nsIMsgFolder *aDstFolder,
+ nsIMsgCopyServiceListener *aListener,
+ nsIArray **aDstHdrs,
+ nsITransaction **aUndoAction,
+ bool *aCopyDone)
+{
+ NS_ENSURE_ARG_POINTER(aHdrArray);
+ NS_ENSURE_ARG_POINTER(aDstFolder);
+ NS_ENSURE_ARG_POINTER(aDstHdrs);
+ NS_ENSURE_ARG_POINTER(aCopyDone);
+ *aDstHdrs = nullptr;
+ *aUndoAction = nullptr;
+ *aCopyDone = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::GetSupportsCompaction(bool *aSupportsCompaction)
+{
+ NS_ENSURE_ARG_POINTER(aSupportsCompaction);
+ *aSupportsCompaction = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::CompactFolder(nsIMsgFolder *aFolder,
+ nsIUrlListener *aListener,
+ nsIMsgWindow *aMsgWindow)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ nsresult rv;
+ nsCOMPtr<nsIMsgFolderCompactor> folderCompactor =
+ do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int64_t expungedBytes = 0;
+ aFolder->GetExpungedBytes(&expungedBytes);
+ // check if we need to compact the folder
+ return (expungedBytes > 0) ?
+ folderCompactor->Compact(aFolder, false, aListener, aMsgWindow) :
+ aFolder->NotifyCompactCompleted();
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::RebuildIndex(nsIMsgFolder *aFolder,
+ nsIMsgDatabase *aMsgDB,
+ nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aListener)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool isLocked;
+ aFolder->GetLocked(&isLocked);
+ if (isLocked)
+ {
+ NS_ASSERTION(false, "Could not get folder lock");
+ return NS_MSG_FOLDER_BUSY;
+ }
+
+ nsCOMPtr<nsIMailboxService> mailboxService =
+ do_GetService(NS_MAILBOXSERVICE_CONTRACTID1, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsMsgMailboxParser> parser = new nsMsgMailboxParser(aFolder);
+ NS_ENSURE_TRUE(parser, NS_ERROR_OUT_OF_MEMORY);
+ rv = parser->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mailboxService->ParseMailbox(aMsgWindow, pathFile, parser, aListener,
+ nullptr);
+ if (NS_SUCCEEDED(rv))
+ ResetForceReparse(aMsgDB);
+ return rv;
+}
+
+nsresult
+nsMsgBrkMBoxStore::GetOutputStream(nsIArray *aHdrArray,
+ nsCOMPtr<nsIOutputStream> &outputStream,
+ nsCOMPtr<nsISeekableStream> &seekableStream,
+ int64_t &restorePos)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, 0, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgFolder> folder;
+ msgHdr->GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCString URI;
+ folder->GetURI(URI);
+ restorePos = -1;
+ if (m_outputStreams.Get(URI, getter_AddRefs(outputStream)))
+ {
+ seekableStream = do_QueryInterface(outputStream);
+ rv = seekableStream->Tell(&restorePos);
+ if (NS_FAILED(rv))
+ {
+ outputStream = nullptr;
+ m_outputStreams.Remove(URI);
+ }
+ }
+ nsCOMPtr<nsIFile> mboxFile;
+ folder->GetFilePath(getter_AddRefs(mboxFile));
+ if (!outputStream)
+ {
+ rv = MsgGetFileStream(mboxFile, getter_AddRefs(outputStream));
+ seekableStream = do_QueryInterface(outputStream);
+ if (NS_SUCCEEDED(rv))
+ m_outputStreams.Put(URI, outputStream);
+ }
+ return rv;
+}
+
+void nsMsgBrkMBoxStore::SetDBValid(nsIMsgDBHdr *aHdr)
+{
+ nsCOMPtr<nsIMsgFolder> folder;
+ aHdr->GetFolder(getter_AddRefs(folder));
+ if (folder)
+ {
+ nsCOMPtr<nsIMsgDatabase> db;
+ folder->GetMsgDatabase(getter_AddRefs(db));
+ if (db)
+ SetSummaryFileValid(folder, db, true);
+ }
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeFlags(nsIArray *aHdrArray,
+ uint32_t aFlags,
+ bool aSet)
+{
+ NS_ENSURE_ARG_POINTER(aHdrArray);
+ nsCOMPtr<nsIOutputStream> outputStream;
+ nsCOMPtr<nsISeekableStream> seekableStream;
+ int64_t restoreStreamPos;
+
+ uint32_t messageCount;
+ nsresult rv = aHdrArray->GetLength(&messageCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!messageCount)
+ return NS_ERROR_INVALID_ARG;
+
+ rv = GetOutputStream(aHdrArray, outputStream, seekableStream,
+ restoreStreamPos);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ for (uint32_t i = 0; i < messageCount; i++)
+ {
+ msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
+ // Seek to x-mozilla-status offset and rewrite value.
+ rv = UpdateFolderFlag(msgHdr, aSet, aFlags, outputStream);
+ if (NS_FAILED(rv))
+ {
+ NS_WARNING("updateFolderFlag failed");
+ break;
+ }
+ }
+ if (restoreStreamPos != -1)
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, restoreStreamPos);
+ else if (outputStream)
+ outputStream->Close();
+ if (messageCount > 0)
+ {
+ msgHdr = do_QueryElementAt(aHdrArray, 0);
+ SetDBValid(msgHdr);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeKeywords(nsIArray *aHdrArray,
+ const nsACString &aKeywords,
+ bool aAdd)
+{
+ NS_ENSURE_ARG_POINTER(aHdrArray);
+ nsCOMPtr<nsIOutputStream> outputStream;
+ nsCOMPtr<nsISeekableStream> seekableStream;
+ int64_t restoreStreamPos;
+
+ uint32_t messageCount;
+ nsresult rv = aHdrArray->GetLength(&messageCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!messageCount)
+ return NS_ERROR_INVALID_ARG;
+
+ rv = GetOutputStream(aHdrArray, outputStream, seekableStream,
+ restoreStreamPos);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(outputStream, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<nsLineBuffer<char> > lineBuffer(new nsLineBuffer<char>);
+ NS_ENSURE_TRUE(lineBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ // For each message, we seek to the beginning of the x-mozilla-status header,
+ // and start reading lines, looking for x-mozilla-keys: headers; If we're
+ // adding the keyword and we find
+ // a header with the desired keyword already in it, we don't need to
+ // do anything. Likewise, if removing keyword and we don't find it,
+ // we don't need to do anything. Otherwise, if adding, we need to
+ // see if there's an x-mozilla-keys
+ // header with room for the new keyword. If so, we replace the
+ // corresponding number of spaces with the keyword. If no room,
+ // we can't do anything until the folder is compacted and another
+ // x-mozilla-keys header is added. In that case, we set a property
+ // on the header, which the compaction code will check.
+
+ nsTArray<nsCString> keywordArray;
+ ParseString(aKeywords, ' ', keywordArray);
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ for (uint32_t i = 0; i < messageCount; ++i) // for each message
+ {
+ msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint64_t messageOffset;
+ msgHdr->GetMessageOffset(&messageOffset);
+ uint32_t statusOffset = 0;
+ (void)msgHdr->GetStatusOffset(&statusOffset);
+ uint64_t desiredOffset = messageOffset + statusOffset;
+
+ ChangeKeywordsHelper(msgHdr, desiredOffset, lineBuffer, keywordArray,
+ aAdd, outputStream, seekableStream, inputStream);
+ }
+ lineBuffer = nullptr;
+ if (restoreStreamPos != -1)
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, restoreStreamPos);
+ else if (outputStream)
+ outputStream->Close();
+ if (messageCount > 0)
+ {
+ msgHdr = do_QueryElementAt(aHdrArray, 0);
+ SetDBValid(msgHdr);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::GetStoreType(nsACString& aType)
+{
+ aType.AssignLiteral("mbox");
+ return NS_OK;
+}
+
+// Iterates over the files in the "path" directory, and adds subfolders to
+// parent for each mailbox file found.
+nsresult
+nsMsgBrkMBoxStore::AddSubFolders(nsIMsgFolder *parent, nsCOMPtr<nsIFile> &path,
+ bool deep)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> tmp; // at top level so we can safely assign to path
+ bool isDirectory;
+ path->IsDirectory(&isDirectory);
+ if (!isDirectory)
+ {
+ rv = path->Clone(getter_AddRefs(tmp));
+ path = tmp;
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoString leafName;
+ path->GetLeafName(leafName);
+ leafName.AppendLiteral(".sbd");
+ path->SetLeafName(leafName);
+ path->IsDirectory(&isDirectory);
+ }
+ if (!isDirectory)
+ return NS_OK;
+ // first find out all the current subfolders and files, before using them
+ // while creating new subfolders; we don't want to modify and iterate the same
+ // directory at once.
+ nsCOMArray<nsIFile> currentDirEntries;
+ nsCOMPtr<nsISimpleEnumerator> directoryEnumerator;
+ rv = path->GetDirectoryEntries(getter_AddRefs(directoryEnumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore;
+ while (NS_SUCCEEDED(directoryEnumerator->HasMoreElements(&hasMore)) &&
+ hasMore)
+ {
+ nsCOMPtr<nsISupports> aSupport;
+ directoryEnumerator->GetNext(getter_AddRefs(aSupport));
+ nsCOMPtr<nsIFile> currentFile(do_QueryInterface(aSupport, &rv));
+ if (currentFile)
+ currentDirEntries.AppendObject(currentFile);
+ }
+
+ // add the folders
+ int32_t count = currentDirEntries.Count();
+ for (int32_t i = 0; i < count; ++i)
+ {
+ nsCOMPtr<nsIFile> currentFile(currentDirEntries[i]);
+
+ nsAutoString leafName;
+ currentFile->GetLeafName(leafName);
+ directoryEnumerator->HasMoreElements(&hasMore);
+ // here we should handle the case where the current file is a .sbd directory
+ // w/o a matching folder file, or a directory w/o the name .sbd
+ if (nsShouldIgnoreFile(leafName))
+ continue;
+
+ nsCOMPtr<nsIMsgFolder> child;
+ rv = parent->AddSubfolder(leafName, getter_AddRefs(child));
+ if (child)
+ {
+ nsString folderName;
+ child->GetName(folderName); // try to get it from cache/db
+ if (folderName.IsEmpty())
+ child->SetPrettyName(leafName);
+ if (deep)
+ {
+ nsCOMPtr<nsIFile> path;
+ rv = child->GetFilePath(getter_AddRefs(path));
+ AddSubFolders(child, path, true);
+ }
+ }
+ }
+ return rv == NS_MSG_FOLDER_EXISTS ? NS_OK : rv;
+}
+
+/* Finds the directory associated with this folder. That is if the path is
+ c:\Inbox, it will return c:\Inbox.sbd if it succeeds. If that path doesn't
+ currently exist then it will create it. Path is strictly an out parameter.
+ */
+nsresult nsMsgBrkMBoxStore::CreateDirectoryForFolder(nsIFile *path)
+{
+ nsresult rv = NS_OK;
+
+ bool pathIsDirectory = false;
+ path->IsDirectory(&pathIsDirectory);
+ if (!pathIsDirectory)
+ {
+ // If the current path isn't a directory, add directory separator
+ // and test it out.
+ nsAutoString leafName;
+ path->GetLeafName(leafName);
+ leafName.AppendLiteral(FOLDER_SUFFIX);
+ rv = path->SetLeafName(leafName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ //If that doesn't exist, then we have to create this directory
+ pathIsDirectory = false;
+ path->IsDirectory(&pathIsDirectory);
+ if (!pathIsDirectory)
+ {
+ bool pathExists;
+ path->Exists(&pathExists);
+ //If for some reason there's a file with the directory separator
+ //then we are going to fail.
+ rv = pathExists ? NS_MSG_COULD_NOT_CREATE_DIRECTORY :
+ path->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ }
+ }
+ return rv;
+}
+
diff --git a/mailnews/local/src/nsMsgBrkMBoxStore.h b/mailnews/local/src/nsMsgBrkMBoxStore.h
new file mode 100644
index 000000000..4d10e672d
--- /dev/null
+++ b/mailnews/local/src/nsMsgBrkMBoxStore.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ Class for handling Berkeley Mailbox stores.
+*/
+
+#ifndef nsMsgBrkMboxStore_h__
+#define nsMsgBrkMboxStore_h__
+
+#include "nsMsgLocalStoreUtils.h"
+#include "nsIFile.h"
+#include "nsInterfaceHashtable.h"
+#include "nsISeekableStream.h"
+
+class nsMsgBrkMBoxStore final : public nsMsgLocalStoreUtils, nsIMsgPluggableStore
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGPLUGGABLESTORE
+
+ nsMsgBrkMBoxStore();
+
+private:
+ ~nsMsgBrkMBoxStore();
+
+protected:
+ nsresult AddSubFolders(nsIMsgFolder *parent, nsCOMPtr<nsIFile> &path, bool deep);
+ nsresult CreateDirectoryForFolder(nsIFile *path);
+ nsresult GetOutputStream(nsIArray *aHdrArray,
+ nsCOMPtr<nsIOutputStream> &outputStream,
+ nsCOMPtr<nsISeekableStream> &seekableStream,
+ int64_t &restorePos);
+ void GetMailboxModProperties(nsIMsgFolder *aFolder,
+ int64_t *aSize, uint32_t *aDate);
+ void SetDBValid(nsIMsgDBHdr *aHdr);
+ // We don't want to keep re-opening an output stream when downloading
+ // multiple pop3 messages, or adjusting x-mozilla-status headers, so
+ // we cache output streams based on folder uri's. If the caller has closed
+ // the stream, we'll get a new one.
+ nsInterfaceHashtable<nsCStringHashKey, nsIOutputStream> m_outputStreams;
+
+#ifdef _DEBUG
+ nsCOMPtr<nsIMsgFolder> m_streamOutstandingFolder;
+#endif
+};
+
+#endif
diff --git a/mailnews/local/src/nsMsgLocalStoreUtils.cpp b/mailnews/local/src/nsMsgLocalStoreUtils.cpp
new file mode 100644
index 000000000..17d9b0f29
--- /dev/null
+++ b/mailnews/local/src/nsMsgLocalStoreUtils.cpp
@@ -0,0 +1,345 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "msgCore.h" // precompiled header...
+#include "nsMsgLocalStoreUtils.h"
+#include "nsIFile.h"
+#include "nsIDBFolderInfo.h"
+#include "nsIMsgDatabase.h"
+#include "prprf.h"
+
+#define EXTRA_SAFETY_SPACE 0x400000 // (4MiB)
+
+nsMsgLocalStoreUtils::nsMsgLocalStoreUtils()
+{
+}
+
+nsresult
+nsMsgLocalStoreUtils::AddDirectorySeparator(nsIFile *path)
+{
+ nsAutoString leafName;
+ path->GetLeafName(leafName);
+ leafName.AppendLiteral(FOLDER_SUFFIX);
+ return path->SetLeafName(leafName);
+}
+
+bool
+nsMsgLocalStoreUtils::nsShouldIgnoreFile(nsAString& name)
+{
+ char16_t firstChar = name.First();
+ if (firstChar == '.' || firstChar == '#' ||
+ name.CharAt(name.Length() - 1) == '~')
+ return true;
+
+ if (name.LowerCaseEqualsLiteral("msgfilterrules.dat") ||
+ name.LowerCaseEqualsLiteral("rules.dat") ||
+ name.LowerCaseEqualsLiteral("filterlog.html") ||
+ name.LowerCaseEqualsLiteral("junklog.html") ||
+ name.LowerCaseEqualsLiteral("rulesbackup.dat"))
+ return true;
+
+ // don't add summary files to the list of folders;
+ // don't add popstate files to the list either, or rules (sort.dat).
+ if (StringEndsWith(name, NS_LITERAL_STRING(".snm")) ||
+ name.LowerCaseEqualsLiteral("popstate.dat") ||
+ name.LowerCaseEqualsLiteral("sort.dat") ||
+ name.LowerCaseEqualsLiteral("mailfilt.log") ||
+ name.LowerCaseEqualsLiteral("filters.js") ||
+ StringEndsWith(name, NS_LITERAL_STRING(".toc")))
+ return true;
+
+ // ignore RSS data source files
+ if (name.LowerCaseEqualsLiteral("feeds.rdf") ||
+ name.LowerCaseEqualsLiteral("feeditems.rdf") ||
+ StringBeginsWith(name, NS_LITERAL_STRING("feeditems_error")))
+ return true;
+
+ // The .mozmsgs dir is for spotlight support
+ return (StringEndsWith(name, NS_LITERAL_STRING(".mozmsgs")) ||
+ StringEndsWith(name, NS_LITERAL_STRING(".sbd")) ||
+ StringEndsWith(name, NS_LITERAL_STRING(SUMMARY_SUFFIX)));
+}
+
+/**
+ * We're passed a stream positioned at the start of the message.
+ * We start reading lines, looking for x-mozilla-keys: headers; If we're
+ * adding the keyword and we find a header with the desired keyword already
+ * in it, we don't need to do anything. Likewise, if removing keyword and we
+ * don't find it,we don't need to do anything. Otherwise, if adding, we need
+ * to see if there's an x-mozilla-keys header with room for the new keyword.
+ * If so, we replace the corresponding number of spaces with the keyword.
+ * If no room, we can't do anything until the folder is compacted and another
+ * x-mozilla-keys header is added. In that case, we set a property
+ * on the header, which the compaction code will check.
+ * This is not true for maildir, however, since it won't require compaction.
+ */
+
+void
+nsMsgLocalStoreUtils::ChangeKeywordsHelper(nsIMsgDBHdr *message,
+ uint64_t desiredOffset,
+ nsLineBuffer<char> *lineBuffer,
+ nsTArray<nsCString> &keywordArray,
+ bool aAdd,
+ nsIOutputStream *outputStream,
+ nsISeekableStream *seekableStream,
+ nsIInputStream *inputStream)
+{
+ uint32_t bytesWritten;
+
+ for (uint32_t i = 0; i < keywordArray.Length(); i++)
+ {
+ nsAutoCString header;
+ nsAutoCString keywords;
+ bool done = false;
+ uint32_t len = 0;
+ nsAutoCString keywordToWrite(" ");
+
+ keywordToWrite.Append(keywordArray[i]);
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, desiredOffset);
+ // need to reset lineBuffer, which is cheaper than creating a new one.
+ lineBuffer->start = lineBuffer->end = lineBuffer->buf;
+ bool inKeywordHeader = false;
+ bool foundKeyword = false;
+ int64_t offsetToAddKeyword = 0;
+ bool more;
+ message->GetMessageSize(&len);
+ // loop through
+ while (!done)
+ {
+ int64_t lineStartPos;
+ seekableStream->Tell(&lineStartPos);
+ // we need to adjust the linestart pos by how much extra the line
+ // buffer has read from the stream.
+ lineStartPos -= (lineBuffer->end - lineBuffer->start);
+ // NS_ReadLine doesn't return line termination chars.
+ nsCString keywordHeaders;
+ nsresult rv = NS_ReadLine(inputStream, lineBuffer, keywordHeaders, &more);
+ if (NS_SUCCEEDED(rv))
+ {
+ if (keywordHeaders.IsEmpty())
+ break; // passed headers; no x-mozilla-keywords header; give up.
+ if (StringBeginsWith(keywordHeaders,
+ NS_LITERAL_CSTRING(HEADER_X_MOZILLA_KEYWORDS)))
+ inKeywordHeader = true;
+ else if (inKeywordHeader && (keywordHeaders.CharAt(0) == ' ' ||
+ keywordHeaders.CharAt(0) == '\t'))
+ ; // continuation header line
+ else if (inKeywordHeader)
+ break;
+ else
+ continue;
+ uint32_t keywordHdrLength = keywordHeaders.Length();
+ int32_t startOffset, keywordLength;
+ // check if we have the keyword
+ if (MsgFindKeyword(keywordArray[i], keywordHeaders, &startOffset,
+ &keywordLength))
+ {
+ foundKeyword = true;
+ if (!aAdd) // if we're removing, remove it, and break;
+ {
+ keywordHeaders.Cut(startOffset, keywordLength);
+ for (int32_t j = keywordLength; j > 0; j--)
+ keywordHeaders.Append(' ');
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, lineStartPos);
+ outputStream->Write(keywordHeaders.get(), keywordHeaders.Length(),
+ &bytesWritten);
+ }
+ offsetToAddKeyword = 0;
+ // if adding and we already have the keyword, done
+ done = true;
+ break;
+ }
+ // argh, we need to check all the lines to see if we already have the
+ // keyword, but if we don't find it, we want to remember the line and
+ // position where we have room to add the keyword.
+ if (aAdd)
+ {
+ nsAutoCString curKeywordHdr(keywordHeaders);
+ // strip off line ending spaces.
+ curKeywordHdr.Trim(" ", false, true);
+ if (!offsetToAddKeyword && curKeywordHdr.Length() +
+ keywordToWrite.Length() < keywordHdrLength)
+ offsetToAddKeyword = lineStartPos + curKeywordHdr.Length();
+ }
+ }
+ }
+ if (aAdd && !foundKeyword)
+ {
+ if (!offsetToAddKeyword)
+ message->SetUint32Property("growKeywords", 1);
+ else
+ {
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET,
+ offsetToAddKeyword);
+ outputStream->Write(keywordToWrite.get(), keywordToWrite.Length(),
+ &bytesWritten);
+ }
+ }
+ }
+}
+
+nsresult
+nsMsgLocalStoreUtils::UpdateFolderFlag(nsIMsgDBHdr *mailHdr, bool bSet,
+ nsMsgMessageFlagType flag,
+ nsIOutputStream *fileStream)
+{
+ uint32_t statusOffset;
+ uint64_t msgOffset;
+ nsresult rv = mailHdr->GetStatusOffset(&statusOffset);
+ // This probably means there's no x-mozilla-status header, so
+ // we just ignore this.
+ if (NS_FAILED(rv) || (statusOffset == 0))
+ return NS_OK;
+ rv = mailHdr->GetMessageOffset(&msgOffset);
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint64_t statusPos = msgOffset + statusOffset;
+ nsCOMPtr<nsISeekableStream> seekableStream(do_QueryInterface(fileStream, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, statusPos);
+ NS_ENSURE_SUCCESS(rv, rv);
+ char buf[50];
+ buf[0] = '\0';
+ nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(fileStream, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint32_t bytesRead;
+ if (NS_SUCCEEDED(inputStream->Read(buf, X_MOZILLA_STATUS_LEN + 6,
+ &bytesRead)))
+ {
+ buf[bytesRead] = '\0';
+ if (strncmp(buf, X_MOZILLA_STATUS, X_MOZILLA_STATUS_LEN) == 0 &&
+ strncmp(buf + X_MOZILLA_STATUS_LEN, ": ", 2) == 0 &&
+ strlen(buf) >= X_MOZILLA_STATUS_LEN + 6)
+ {
+ uint32_t flags;
+ uint32_t bytesWritten;
+ (void)mailHdr->GetFlags(&flags);
+ if (!(flags & nsMsgMessageFlags::Expunged))
+ {
+ char *p = buf + X_MOZILLA_STATUS_LEN + 2;
+
+ nsresult errorCode = NS_OK;
+ flags = nsDependentCString(p).ToInteger(&errorCode, 16);
+
+ uint32_t curFlags;
+ (void)mailHdr->GetFlags(&curFlags);
+ flags = (flags & nsMsgMessageFlags::Queued) |
+ (curFlags & ~nsMsgMessageFlags::RuntimeOnly);
+ if (bSet)
+ flags |= flag;
+ else
+ flags &= ~flag;
+ }
+ else
+ {
+ flags &= ~nsMsgMessageFlags::RuntimeOnly;
+ }
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, statusPos);
+ // We are filing out x-mozilla-status flags here
+ PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS_FORMAT,
+ flags & 0x0000FFFF);
+ int32_t lineLen = PL_strlen(buf);
+ uint64_t status2Pos = statusPos + lineLen;
+ fileStream->Write(buf, lineLen, &bytesWritten);
+
+ if (flag & 0xFFFF0000)
+ {
+ // Time to update x-mozilla-status2,
+ // first find it by finding end of previous line, see bug 234935.
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, status2Pos);
+ do
+ {
+ rv = inputStream->Read(buf, 1, &bytesRead);
+ status2Pos++;
+ } while (NS_SUCCEEDED(rv) && (*buf == '\n' || *buf == '\r'));
+ status2Pos--;
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, status2Pos);
+ if (NS_SUCCEEDED(inputStream->Read(buf, X_MOZILLA_STATUS2_LEN + 10,
+ &bytesRead)))
+ {
+ if (strncmp(buf, X_MOZILLA_STATUS2, X_MOZILLA_STATUS2_LEN) == 0 &&
+ strncmp(buf + X_MOZILLA_STATUS2_LEN, ": ", 2) == 0 &&
+ strlen(buf) >= X_MOZILLA_STATUS2_LEN + 10)
+ {
+ uint32_t dbFlags;
+ (void)mailHdr->GetFlags(&dbFlags);
+ dbFlags &= 0xFFFF0000;
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, status2Pos);
+ PR_snprintf(buf, sizeof(buf), X_MOZILLA_STATUS2_FORMAT, dbFlags);
+ fileStream->Write(buf, PL_strlen(buf), &bytesWritten);
+ }
+ }
+ }
+ }
+ else
+ {
+#ifdef DEBUG
+ printf("Didn't find %s where expected at position %ld\n"
+ "instead, found %s.\n",
+ X_MOZILLA_STATUS, (long) statusPos, buf);
+#endif
+ rv = NS_ERROR_FAILURE;
+ }
+ }
+ else
+ rv = NS_ERROR_FAILURE;
+ return rv;
+}
+
+/**
+ * Returns true if there is enough space on disk.
+ *
+ * @param aFile Any file in the message store that is on a logical
+ * disk volume so that it can be queried for disk space.
+ * @param aSpaceRequested The size of free space there must be on the disk
+ * to return true.
+ */
+bool
+nsMsgLocalStoreUtils::DiskSpaceAvailableInStore(nsIFile *aFile, uint64_t aSpaceRequested)
+{
+ int64_t diskFree;
+ nsresult rv = aFile->GetDiskSpaceAvailable(&diskFree);
+ if (NS_SUCCEEDED(rv)) {
+#ifdef DEBUG
+ printf("GetDiskSpaceAvailable returned: %lld bytes\n", (long long)diskFree);
+#endif
+ // When checking for disk space available, take into consideration
+ // possible database changes, therefore ask for a little more
+ // (EXTRA_SAFETY_SPACE) than what the requested size is. Also, due to disk
+ // sector sizes, allocation blocks, etc. The space "available" may be greater
+ // than the actual space usable.
+ return ((aSpaceRequested + EXTRA_SAFETY_SPACE) < (uint64_t) diskFree);
+ } else if (rv == NS_ERROR_NOT_IMPLEMENTED) {
+ // The call to GetDiskSpaceAvailable is not implemented!
+ // This will happen on certain platforms where GetDiskSpaceAvailable
+ // is not implemented. Since people on those platforms still need
+ // to download mail, we will simply bypass the disk-space check.
+ //
+ // We'll leave a debug message to warn people.
+#ifdef DEBUG
+ printf("Call to GetDiskSpaceAvailable FAILED because it is not implemented!\n");
+#endif
+ return true;
+ } else {
+ printf("Call to GetDiskSpaceAvailable FAILED!\n");
+ return false;
+ }
+}
+
+/**
+ * Resets forceReparse in the database.
+ *
+ * @param aMsgDb The database to reset.
+ */
+void
+nsMsgLocalStoreUtils::ResetForceReparse(nsIMsgDatabase *aMsgDB)
+{
+ if (aMsgDB)
+ {
+ nsCOMPtr<nsIDBFolderInfo> folderInfo;
+ aMsgDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
+ if (folderInfo)
+ folderInfo->SetBooleanProperty("forceReparse", false);
+ }
+}
diff --git a/mailnews/local/src/nsMsgLocalStoreUtils.h b/mailnews/local/src/nsMsgLocalStoreUtils.h
new file mode 100644
index 000000000..182e0d15a
--- /dev/null
+++ b/mailnews/local/src/nsMsgLocalStoreUtils.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsMsgLocalStoreUtils_h__
+#define nsMsgLocalStoreUtils_h__
+
+#include "msgCore.h"
+#include "nsIMsgPluggableStore.h"
+#include "nsStringGlue.h"
+#include "nsReadLine.h"
+#include "nsISeekableStream.h"
+#include "nsIMsgHdr.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsMailHeaders.h"
+#include "nsMsgUtils.h"
+#include "nsIOutputStream.h"
+#include "nsMsgMessageFlags.h"
+
+/**
+ * Utility Class for handling local mail stores. Berkeley Mailbox
+ * and MailDir stores inherit from this class to share some code.
+*/
+
+class nsMsgLocalStoreUtils
+{
+public:
+ nsMsgLocalStoreUtils();
+
+ static nsresult AddDirectorySeparator(nsIFile *path);
+ static bool nsShouldIgnoreFile(nsAString& name);
+ static void ChangeKeywordsHelper(nsIMsgDBHdr *message,
+ uint64_t desiredOffset,
+ nsLineBuffer<char> *lineBuffer,
+ nsTArray<nsCString> &keywordArray,
+ bool aAdd,
+ nsIOutputStream *outputStream,
+ nsISeekableStream *seekableStream,
+ nsIInputStream *inputStream);
+ static void ResetForceReparse(nsIMsgDatabase *aMsgDB);
+
+ nsresult UpdateFolderFlag(nsIMsgDBHdr *mailHdr, bool bSet,
+ nsMsgMessageFlagType flag,
+ nsIOutputStream *fileStream);
+ bool DiskSpaceAvailableInStore(nsIFile *aFile,
+ uint64_t aSpaceRequested);
+};
+
+#endif
diff --git a/mailnews/local/src/nsMsgMaildirStore.cpp b/mailnews/local/src/nsMsgMaildirStore.cpp
new file mode 100644
index 000000000..7a4633367
--- /dev/null
+++ b/mailnews/local/src/nsMsgMaildirStore.cpp
@@ -0,0 +1,1453 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ Class for handling Maildir stores.
+*/
+
+#include "prprf.h"
+#include "mozilla/Logging.h"
+#include "msgCore.h"
+#include "nsMsgMaildirStore.h"
+#include "nsIMsgFolder.h"
+#include "nsISimpleEnumerator.h"
+#include "nsMsgFolderFlags.h"
+#include "nsILocalMailIncomingServer.h"
+#include "nsCOMArray.h"
+#include "nsIFile.h"
+#include "nsNetUtil.h"
+#include "nsIMsgDatabase.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsMsgUtils.h"
+#include "nsMsgDBCID.h"
+#include "nsIDBFolderInfo.h"
+#include "nsIMutableArray.h"
+#include "nsArrayUtils.h"
+#include "nsMailHeaders.h"
+#include "nsParseMailbox.h"
+#include "nsIMailboxService.h"
+#include "nsMsgLocalCID.h"
+#include "nsIMsgLocalMailFolder.h"
+#include "nsITimer.h"
+#include "nsIMailboxUrl.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsIMsgFilterPlugin.h"
+#include "nsLocalUndoTxn.h"
+#include "nsIMessenger.h"
+
+static PRLogModuleInfo* MailDirLog;
+
+nsMsgMaildirStore::nsMsgMaildirStore()
+{
+ MailDirLog = PR_NewLogModule("MailDirStore");
+}
+
+nsMsgMaildirStore::~nsMsgMaildirStore()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsMsgMaildirStore, nsIMsgPluggableStore)
+
+// Iterates over the folders in the "path" directory, and adds subfolders to
+// parent for each Maildir folder found.
+nsresult nsMsgMaildirStore::AddSubFolders(nsIMsgFolder *parent, nsIFile *path,
+ bool deep)
+{
+ nsCOMArray<nsIFile> currentDirEntries;
+
+ nsCOMPtr<nsISimpleEnumerator> directoryEnumerator;
+ nsresult rv = path->GetDirectoryEntries(getter_AddRefs(directoryEnumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore;
+ while (NS_SUCCEEDED(directoryEnumerator->HasMoreElements(&hasMore)) &&
+ hasMore)
+ {
+ nsCOMPtr<nsISupports> aSupport;
+ directoryEnumerator->GetNext(getter_AddRefs(aSupport));
+ nsCOMPtr<nsIFile> currentFile(do_QueryInterface(aSupport, &rv));
+ if (currentFile) {
+ nsAutoString leafName;
+ currentFile->GetLeafName(leafName);
+ bool isDirectory = false;
+ currentFile->IsDirectory(&isDirectory);
+ // Make sure this really is a mail folder dir (i.e., a directory that
+ // contains cur and tmp sub-dirs, and not a .sbd or .mozmsgs dir).
+ if (isDirectory && !nsShouldIgnoreFile(leafName))
+ currentDirEntries.AppendObject(currentFile);
+ }
+ }
+
+ // add the folders
+ int32_t count = currentDirEntries.Count();
+ for (int32_t i = 0; i < count; ++i)
+ {
+ nsCOMPtr<nsIFile> currentFile(currentDirEntries[i]);
+
+ nsAutoString leafName;
+ currentFile->GetLeafName(leafName);
+
+ nsCOMPtr<nsIMsgFolder> child;
+ rv = parent->AddSubfolder(leafName, getter_AddRefs(child));
+ if (child)
+ {
+ nsString folderName;
+ child->GetName(folderName); // try to get it from cache/db
+ if (folderName.IsEmpty())
+ child->SetPrettyName(leafName);
+ if (deep)
+ {
+ nsCOMPtr<nsIFile> path;
+ rv = child->GetFilePath(getter_AddRefs(path));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Construct the .sbd directory path for the possible children of the
+ // folder.
+ GetDirectoryForFolder(path);
+ bool directory = false;
+ // Check that <folder>.sbd really is a directory.
+ path->IsDirectory(&directory);
+ if (directory)
+ AddSubFolders(child, path, true);
+ }
+ }
+ }
+ return rv == NS_MSG_FOLDER_EXISTS ? NS_OK : rv;
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::DiscoverSubFolders(nsIMsgFolder *aParentFolder,
+ bool aDeep)
+{
+ NS_ENSURE_ARG_POINTER(aParentFolder);
+
+ nsCOMPtr<nsIFile> path;
+ nsresult rv = aParentFolder->GetFilePath(getter_AddRefs(path));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isServer, directory = false;
+ aParentFolder->GetIsServer(&isServer);
+ if (!isServer)
+ GetDirectoryForFolder(path);
+
+ path->IsDirectory(&directory);
+ if (directory)
+ rv = AddSubFolders(aParentFolder, path, aDeep);
+
+ return (rv == NS_MSG_FOLDER_EXISTS) ? NS_OK : rv;
+}
+
+/**
+ * Create if missing a Maildir-style folder with "tmp" and "cur" subfolders
+ * but no "new" subfolder, because it doesn't make sense in the mail client
+ * context. ("new" directory is for messages on the server that haven't been
+* seen by a mail client).
+ * aFolderName is already "safe" - it has been through NS_MsgHashIfNecessary.
+ */
+nsresult nsMsgMaildirStore::CreateMaildir(nsIFile *path)
+{
+ nsresult rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
+ {
+ NS_WARNING("Could not create root directory for message folder");
+ return rv;
+ }
+
+ // Create tmp, cur leaves
+ nsCOMPtr<nsIFile> leaf(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ leaf->InitWithFile(path);
+
+ leaf->AppendNative(NS_LITERAL_CSTRING("tmp"));
+ rv = leaf->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
+ {
+ NS_WARNING("Could not create tmp directory for message folder");
+ return rv;
+ }
+
+ leaf->SetNativeLeafName(NS_LITERAL_CSTRING("cur"));
+ rv = leaf->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)
+ {
+ NS_WARNING("Could not create cur directory for message folder");
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::CreateFolder(nsIMsgFolder *aParent,
+ const nsAString &aFolderName,
+ nsIMsgFolder **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+ NS_ENSURE_ARG_POINTER(aResult);
+ if (aFolderName.IsEmpty())
+ return NS_MSG_ERROR_INVALID_FOLDER_NAME;
+
+ nsCOMPtr <nsIFile> path;
+ nsresult rv = aParent->GetFilePath(getter_AddRefs(path));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get a directory based on our current path
+ bool isServer;
+ aParent->GetIsServer(&isServer);
+ rv = CreateDirectoryForFolder(path, isServer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Make sure the new folder name is valid
+ nsAutoString safeFolderName(aFolderName);
+ NS_MsgHashIfNecessary(safeFolderName);
+
+ path->Append(safeFolderName);
+ bool exists;
+ path->Exists(&exists);
+ if (exists) //check this because localized names are different from disk names
+ return NS_MSG_FOLDER_EXISTS;
+
+ rv = CreateMaildir(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolder> child;
+ // GetFlags and SetFlags in AddSubfolder will fail because we have no db at
+ // this point but mFlags is set.
+ rv = aParent->AddSubfolder(safeFolderName, getter_AddRefs(child));
+ if (!child || NS_FAILED(rv))
+ {
+ path->Remove(true); // recursive
+ return rv;
+ }
+
+ // Create an empty database for this mail folder, set its name from the user
+ nsCOMPtr<nsIMsgDBService> msgDBService =
+ do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+ if (msgDBService)
+ {
+ nsCOMPtr<nsIMsgDatabase> unusedDB;
+ rv = msgDBService->OpenFolderDB(child, true, getter_AddRefs(unusedDB));
+ if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
+ rv = msgDBService->CreateNewDB(child, getter_AddRefs(unusedDB));
+
+ if ((NS_SUCCEEDED(rv) || rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE) &&
+ unusedDB)
+ {
+ //need to set the folder name
+ nsCOMPtr<nsIDBFolderInfo> folderInfo;
+ rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
+ if (NS_SUCCEEDED(rv))
+ folderInfo->SetMailboxName(safeFolderName);
+
+ unusedDB->SetSummaryValid(true);
+ unusedDB->Close(true);
+ aParent->UpdateSummaryTotals(true);
+ }
+ else
+ {
+ MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
+ ("CreateFolder - failed creating db for new folder\n"));
+ path->Remove(true); // recursive
+ rv = NS_MSG_CANT_CREATE_FOLDER;
+ }
+ }
+ child.swap(*aResult);
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::HasSpaceAvailable(nsIMsgFolder *aFolder,
+ int64_t aSpaceRequested,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_ARG_POINTER(aFolder);
+
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = DiskSpaceAvailableInStore(pathFile, aSpaceRequested);
+ if (!*aResult)
+ return NS_ERROR_FILE_DISK_FULL;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::IsSummaryFileValid(nsIMsgFolder *aFolder,
+ nsIMsgDatabase *aDB,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aDB);
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = true;
+ nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
+ aDB->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
+ nsresult rv = dbFolderInfo->GetBooleanProperty("maildirValid", false,
+ aResult);
+ if (!*aResult)
+ {
+ nsCOMPtr<nsIFile> newFile;
+ rv = aFolder->GetFilePath(getter_AddRefs(newFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ newFile->Append(NS_LITERAL_STRING("cur"));
+
+ // If the "cur" sub-dir doesn't exist, and there are no messages
+ // in the db, then the folder is probably new and the db is valid.
+ bool exists;
+ newFile->Exists(&exists);
+ if (!exists)
+ {
+ int32_t numMessages;
+ dbFolderInfo->GetNumMessages(&numMessages);
+ if (!numMessages)
+ *aResult = true;
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::SetSummaryFileValid(nsIMsgFolder *aFolder,
+ nsIMsgDatabase *aDB,
+ bool aValid)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aDB);
+ nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
+ aDB->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
+ NS_ENSURE_STATE(dbFolderInfo);
+ return dbFolderInfo->SetBooleanProperty("maildirValid", aValid);
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::DeleteFolder(nsIMsgFolder *aFolder)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+
+ // Delete Maildir structure
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = pathFile->Remove(true); // recursive
+ AddDirectorySeparator(pathFile);
+ bool exists;
+ pathFile->Exists(&exists);
+ if (exists)
+ pathFile->Remove(true);
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::RenameFolder(nsIMsgFolder *aFolder,
+ const nsAString & aNewName,
+ nsIMsgFolder **aNewFolder)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aNewFolder);
+
+ // old path
+ nsCOMPtr<nsIFile> oldPathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(oldPathFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // old sbd directory
+ nsCOMPtr<nsIFile> sbdPathFile;
+ uint32_t numChildren;
+ aFolder->GetNumSubFolders(&numChildren);
+ if (numChildren > 0)
+ {
+ sbdPathFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = sbdPathFile->InitWithFile(oldPathFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+ GetDirectoryForFolder(sbdPathFile);
+ }
+
+ // old summary
+ nsCOMPtr<nsIFile> oldSummaryFile;
+ rv = aFolder->GetSummaryFile(getter_AddRefs(oldSummaryFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Validate new name
+ nsAutoString safeName(aNewName);
+ NS_MsgHashIfNecessary(safeName);
+
+ aFolder->ForceDBClosed();
+
+ // rename folder
+ rv = oldPathFile->MoveTo(nullptr, safeName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (numChildren > 0)
+ {
+ // rename "*.sbd" directory
+ nsAutoString sbdName = safeName;
+ sbdName += NS_LITERAL_STRING(FOLDER_SUFFIX);
+ sbdPathFile->MoveTo(nullptr, sbdName);
+ }
+
+ // rename summary
+ nsAutoString summaryName(safeName);
+ summaryName += NS_LITERAL_STRING(SUMMARY_SUFFIX);
+ oldSummaryFile->MoveTo(nullptr, summaryName);
+
+ nsCOMPtr<nsIMsgFolder> parentFolder;
+ rv = aFolder->GetParent(getter_AddRefs(parentFolder));
+ if (!parentFolder)
+ return NS_ERROR_NULL_POINTER;
+
+ return parentFolder->AddSubfolder(safeName, aNewFolder);
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::CopyFolder(nsIMsgFolder *aSrcFolder,
+ nsIMsgFolder *aDstFolder,
+ bool aIsMoveFolder,
+ nsIMsgWindow *aMsgWindow,
+ nsIMsgCopyServiceListener *aListener,
+ const nsAString &aNewName)
+{
+ NS_ENSURE_ARG_POINTER(aSrcFolder);
+ NS_ENSURE_ARG_POINTER(aDstFolder);
+
+ nsAutoString folderName;
+ if (aNewName.IsEmpty())
+ aSrcFolder->GetName(folderName);
+ else
+ folderName.Assign(aNewName);
+
+ nsAutoString safeFolderName(folderName);
+ NS_MsgHashIfNecessary(safeFolderName);
+ nsCOMPtr<nsIMsgLocalMailFolder> localSrcFolder(do_QueryInterface(aSrcFolder));
+ aSrcFolder->ForceDBClosed();
+
+ nsCOMPtr<nsIFile> oldPath;
+ nsresult rv = aSrcFolder->GetFilePath(getter_AddRefs(oldPath));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIFile> summaryFile;
+ GetSummaryFileLocation(oldPath, getter_AddRefs(summaryFile));
+
+ nsCOMPtr<nsIFile> newPath;
+ rv = aDstFolder->GetFilePath(getter_AddRefs(newPath));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // create target directory based on our current path
+ bool isServer;
+ aDstFolder->GetIsServer(&isServer);
+ rv = CreateDirectoryForFolder(newPath, isServer);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> origPath;
+ oldPath->Clone(getter_AddRefs(origPath));
+
+ rv = oldPath->CopyTo(newPath, safeFolderName);
+ NS_ENSURE_SUCCESS(rv, rv); //will fail if a file by that name exists
+
+ // Copy to dir can fail if file does not exist. If copy fails, we test
+ // if the file exists or not, if it does not that's ok, we continue
+ // without copying it. If it fails and file exist and is not zero sized
+ // there is real problem.
+ nsAutoString dbName(safeFolderName);
+ dbName += NS_LITERAL_STRING(SUMMARY_SUFFIX);
+ rv = summaryFile->CopyTo(newPath, dbName);
+ if (!NS_SUCCEEDED(rv))
+ {
+ // Test if the file is not empty
+ bool exists;
+ int64_t fileSize;
+ summaryFile->Exists(&exists);
+ summaryFile->GetFileSize(&fileSize);
+ if (exists && fileSize > 0)
+ NS_ENSURE_SUCCESS(rv, rv); // Yes, it should have worked!
+ // else case is file is zero sized, no need to copy it,
+ // not an error
+ // else case is file does not exist - not an error
+ }
+
+ nsCOMPtr<nsIMsgFolder> newMsgFolder;
+ rv = aDstFolder->AddSubfolder(safeFolderName, getter_AddRefs(newMsgFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ newMsgFolder->SetPrettyName(folderName);
+ uint32_t flags;
+ aSrcFolder->GetFlags(&flags);
+ newMsgFolder->SetFlags(flags);
+ bool changed = false;
+ rv = aSrcFolder->MatchOrChangeFilterDestination(newMsgFolder, true, &changed);
+ if (changed)
+ aSrcFolder->AlertFilterChanged(aMsgWindow);
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = aSrcFolder->GetSubFolders(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Copy subfolders to the new location
+ nsresult copyStatus = NS_OK;
+ nsCOMPtr<nsIMsgLocalMailFolder> localNewFolder(do_QueryInterface(newMsgFolder, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ bool hasMore;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore &&
+ NS_SUCCEEDED(copyStatus))
+ {
+ nsCOMPtr<nsISupports> item;
+ enumerator->GetNext(getter_AddRefs(item));
+
+ nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(item));
+ if (!folder)
+ continue;
+
+ copyStatus = localNewFolder->CopyFolderLocal(folder, false, aMsgWindow,
+ aListener);
+ // Test if the call succeeded, if not we have to stop recursive call
+ if (NS_FAILED(copyStatus))
+ {
+ // Copy failed we have to notify caller to handle the error and stop
+ // moving the folders. In case this happens to the topmost level of
+ // recursive call, then we just need to break from the while loop and
+ // go to error handling code.
+ if (!aIsMoveFolder)
+ return copyStatus;
+ break;
+ }
+ }
+ }
+
+ if (aIsMoveFolder && NS_SUCCEEDED(copyStatus))
+ {
+ if (localNewFolder)
+ {
+ nsCOMPtr<nsISupports> srcSupport(do_QueryInterface(aSrcFolder));
+ localNewFolder->OnCopyCompleted(srcSupport, true);
+ }
+
+ // Notify that the folder that was dragged and dropped has been created.
+ // No need to do this for its subfolders - isMoveFolder will be true for folder.
+ aDstFolder->NotifyItemAdded(newMsgFolder);
+
+ nsCOMPtr<nsIMsgFolder> msgParent;
+ aSrcFolder->GetParent(getter_AddRefs(msgParent));
+ aSrcFolder->SetParent(nullptr);
+ if (msgParent)
+ {
+ // The files have already been moved, so delete storage false
+ msgParent->PropagateDelete(aSrcFolder, false, aMsgWindow);
+ oldPath->Remove(true);
+ nsCOMPtr<nsIMsgDatabase> srcDB; // we need to force closed the source db
+ aSrcFolder->Delete();
+
+ nsCOMPtr<nsIFile> parentPath;
+ rv = msgParent->GetFilePath(getter_AddRefs(parentPath));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ AddDirectorySeparator(parentPath);
+ nsCOMPtr<nsISimpleEnumerator> children;
+ parentPath->GetDirectoryEntries(getter_AddRefs(children));
+ bool more;
+ // checks if the directory is empty or not
+ if (children && NS_SUCCEEDED(children->HasMoreElements(&more)) && !more)
+ parentPath->Remove(true);
+ }
+ }
+ else
+ {
+ // This is the case where the copy of a subfolder failed.
+ // We have to delete the newDirectory tree to make a "rollback".
+ // Someone should add a popup to warn the user that the move was not
+ // possible.
+ if (aIsMoveFolder && NS_FAILED(copyStatus))
+ {
+ nsCOMPtr<nsIMsgFolder> msgParent;
+ newMsgFolder->ForceDBClosed();
+ newMsgFolder->GetParent(getter_AddRefs(msgParent));
+ newMsgFolder->SetParent(nullptr);
+ if (msgParent)
+ {
+ msgParent->PropagateDelete(newMsgFolder, false, aMsgWindow);
+ newMsgFolder->Delete();
+ newMsgFolder->ForceDBClosed();
+ AddDirectorySeparator(newPath);
+ newPath->Remove(true); //berkeley mailbox
+ }
+ return NS_ERROR_FAILURE;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgMaildirStore::GetNewMsgOutputStream(nsIMsgFolder *aFolder,
+ nsIMsgDBHdr **aNewMsgHdr,
+ bool *aReusable,
+ nsIOutputStream **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aNewMsgHdr);
+ NS_ENSURE_ARG_POINTER(aReusable);
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aReusable = false; // message per file
+
+ nsCOMPtr<nsIMsgDatabase> db;
+ aFolder->GetMsgDatabase(getter_AddRefs(db));
+ if (!db)
+ NS_ERROR("no db");
+
+ nsresult rv;
+
+ if (!*aNewMsgHdr)
+ {
+ rv = db->CreateNewHdr(nsMsgKey_None, aNewMsgHdr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ }
+ (*aNewMsgHdr)->SetMessageOffset(0);
+ // path to the message download folder
+ nsCOMPtr<nsIFile> newFile;
+ rv = aFolder->GetFilePath(getter_AddRefs(newFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ newFile->Append(NS_LITERAL_STRING("tmp"));
+
+ // let's check if the folder exists
+ bool exists;
+ newFile->Exists(&exists);
+ if (!exists) {
+ MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
+ ("GetNewMsgOutputStream - tmp subfolder does not exist!!\n"));
+ rv = newFile->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // generate new file name
+ nsAutoCString newName;
+ newName.AppendInt(static_cast<int64_t>(PR_Now()));
+ newFile->AppendNative(newName);
+ // CreateUnique, in case we get more than one message per millisecond :-)
+ rv = newFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+ newFile->GetNativeLeafName(newName);
+ // save the file name in the message header - otherwise no way to retrieve it
+ (*aNewMsgHdr)->SetStringProperty("storeToken", newName.get());
+ return MsgNewBufferedFileOutputStream(aResult, newFile,
+ PR_WRONLY | PR_CREATE_FILE, 00600);
+}
+
+NS_IMETHODIMP
+nsMsgMaildirStore::DiscardNewMessage(nsIOutputStream *aOutputStream,
+ nsIMsgDBHdr *aNewHdr)
+{
+ NS_ENSURE_ARG_POINTER(aOutputStream);
+ NS_ENSURE_ARG_POINTER(aNewHdr);
+
+ aOutputStream->Close();
+ // file path is stored in message header property "storeToken"
+ nsAutoCString fileName;
+ aNewHdr->GetStringProperty("storeToken", getter_Copies(fileName));
+ if (fileName.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIFile> path;
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsresult rv = aNewHdr->GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = folder->GetFilePath(getter_AddRefs(path));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // path to the message download folder
+ path->Append(NS_LITERAL_STRING("tmp"));
+ path->AppendNative(fileName);
+
+ return path->Remove(false);
+}
+
+NS_IMETHODIMP
+nsMsgMaildirStore::FinishNewMessage(nsIOutputStream *aOutputStream,
+ nsIMsgDBHdr *aNewHdr)
+{
+ NS_ENSURE_ARG_POINTER(aOutputStream);
+ NS_ENSURE_ARG_POINTER(aNewHdr);
+
+ aOutputStream->Close();
+
+ nsCOMPtr<nsIFile> folderPath;
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsresult rv = aNewHdr->GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = folder->GetFilePath(getter_AddRefs(folderPath));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // file path is stored in message header property
+ nsAutoCString fileName;
+ aNewHdr->GetStringProperty("storeToken", getter_Copies(fileName));
+ if (fileName.IsEmpty())
+ {
+ NS_ERROR("FinishNewMessage - no storeToken in msg hdr!!\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ // path to the new destination
+ nsCOMPtr<nsIFile> toPath;
+ folderPath->Clone(getter_AddRefs(toPath));
+ toPath->Append(NS_LITERAL_STRING("cur"));
+
+ // let's check if the folder exists
+ bool exists;
+ toPath->Exists(&exists);
+ if (!exists)
+ {
+ rv = toPath->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // path to the downloaded message
+ nsCOMPtr<nsIFile> fromPath;
+ folderPath->Clone(getter_AddRefs(fromPath));
+ fromPath->Append(NS_LITERAL_STRING("tmp"));
+ fromPath->AppendNative(fileName);
+
+ // let's check if the tmp file exists
+ fromPath->Exists(&exists);
+ if (!exists)
+ {
+ // Perhaps the message has already moved. See bug 1028372 to fix this.
+ toPath->AppendNative(fileName);
+ toPath->Exists(&exists);
+ if (exists) // then there is nothing to do
+ return NS_OK;
+
+ NS_ERROR("FinishNewMessage - oops! file does not exist!");
+ return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
+ }
+
+ nsCOMPtr<nsIFile> existingPath;
+ toPath->Clone(getter_AddRefs(existingPath));
+ existingPath->AppendNative(fileName);
+ existingPath->Exists(&exists);
+
+ if (exists) {
+ rv = existingPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+ existingPath->GetNativeLeafName(fileName);
+ aNewHdr->SetStringProperty("storeToken", fileName.get());
+ }
+
+ return fromPath->MoveToNative(toPath, fileName);
+}
+
+NS_IMETHODIMP
+nsMsgMaildirStore::MoveNewlyDownloadedMessage(nsIMsgDBHdr *aHdr,
+ nsIMsgFolder *aDestFolder,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aHdr);
+ NS_ENSURE_ARG_POINTER(aDestFolder);
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ nsCOMPtr<nsIFile> folderPath;
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsresult rv = aHdr->GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = folder->GetFilePath(getter_AddRefs(folderPath));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // file path is stored in message header property
+ nsAutoCString fileName;
+ aHdr->GetStringProperty("storeToken", getter_Copies(fileName));
+ if (fileName.IsEmpty())
+ {
+ NS_ERROR("FinishNewMessage - no storeToken in msg hdr!!\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ // path to the downloaded message
+ nsCOMPtr<nsIFile> fromPath;
+ folderPath->Clone(getter_AddRefs(fromPath));
+ fromPath->Append(NS_LITERAL_STRING("cur"));
+ fromPath->AppendNative(fileName);
+
+ // let's check if the tmp file exists
+ bool exists;
+ fromPath->Exists(&exists);
+ if (!exists)
+ {
+ NS_ERROR("FinishNewMessage - oops! file does not exist!");
+ return NS_ERROR_FAILURE;
+ }
+
+ // move to the "cur" subfolder
+ nsCOMPtr<nsIFile> toPath;
+ aDestFolder->GetFilePath(getter_AddRefs(folderPath));
+ folderPath->Clone(getter_AddRefs(toPath));
+ toPath->Append(NS_LITERAL_STRING("cur"));
+
+ // let's check if the folder exists
+ toPath->Exists(&exists);
+ if (!exists)
+ {
+ rv = toPath->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIMsgDatabase> destMailDB;
+ rv = aDestFolder->GetMsgDatabase(getter_AddRefs(destMailDB));
+ NS_WARNING_ASSERTION(destMailDB && NS_SUCCEEDED(rv),
+ "failed to open mail db moving message");
+
+ nsCOMPtr<nsIMsgDBHdr> newHdr;
+ if (destMailDB)
+ rv = destMailDB->CopyHdrFromExistingHdr(nsMsgKey_None, aHdr, true,
+ getter_AddRefs(newHdr));
+ if (NS_SUCCEEDED(rv) && !newHdr)
+ rv = NS_ERROR_UNEXPECTED;
+
+ if (NS_FAILED(rv)) {
+ aDestFolder->ThrowAlertMsg("filterFolderHdrAddFailed", nullptr);
+ return rv;
+ }
+
+ nsCOMPtr<nsIFile> existingPath;
+ toPath->Clone(getter_AddRefs(existingPath));
+ existingPath->AppendNative(fileName);
+ existingPath->Exists(&exists);
+
+ if (exists) {
+ rv = existingPath->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+ existingPath->GetNativeLeafName(fileName);
+ newHdr->SetStringProperty("storeToken", fileName.get());
+ }
+
+ rv = fromPath->MoveToNative(toPath, fileName);
+ *aResult = NS_SUCCEEDED(rv);
+ if (NS_FAILED(rv))
+ aDestFolder->ThrowAlertMsg("filterFolderWriteFailed", nullptr);
+
+ if (NS_FAILED(rv)) {
+ if (destMailDB)
+ destMailDB->Close(true);
+
+ return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
+ }
+
+ bool movedMsgIsNew = false;
+ // if we have made it this far then the message has successfully been
+ // written to the new folder now add the header to the destMailDB.
+
+ uint32_t newFlags;
+ newHdr->GetFlags(&newFlags);
+ nsMsgKey msgKey;
+ newHdr->GetMessageKey(&msgKey);
+ if (!(newFlags & nsMsgMessageFlags::Read))
+ {
+ nsCString junkScoreStr;
+ (void) newHdr->GetStringProperty("junkscore", getter_Copies(junkScoreStr));
+ if (atoi(junkScoreStr.get()) != nsIJunkMailPlugin::IS_SPAM_SCORE) {
+ newHdr->OrFlags(nsMsgMessageFlags::New, &newFlags);
+ destMailDB->AddToNewList(msgKey);
+ movedMsgIsNew = true;
+ }
+ }
+
+ nsCOMPtr<nsIMsgFolderNotificationService> notifier(
+ do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ if (notifier)
+ notifier->NotifyMsgAdded(newHdr);
+
+ if (movedMsgIsNew) {
+ aDestFolder->SetHasNewMessages(true);
+
+ // Notify the message was moved.
+ if (notifier) {
+ notifier->NotifyItemEvent(folder,
+ NS_LITERAL_CSTRING("UnincorporatedMessageMoved"),
+ newHdr);
+ }
+ }
+
+ nsCOMPtr<nsIMsgDatabase> sourceDB;
+ rv = folder->GetMsgDatabase(getter_AddRefs(sourceDB));
+
+ if (NS_SUCCEEDED(rv) && sourceDB)
+ sourceDB->RemoveHeaderMdbRow(aHdr);
+
+ destMailDB->SetSummaryValid(true);
+ aDestFolder->UpdateSummaryTotals(true);
+ destMailDB->Commit(nsMsgDBCommitType::kLargeCommit);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgMaildirStore::GetMsgInputStream(nsIMsgFolder *aMsgFolder,
+ const nsACString &aMsgToken,
+ int64_t *aOffset,
+ nsIMsgDBHdr *aMsgHdr,
+ bool *aReusable,
+ nsIInputStream **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aMsgFolder);
+ NS_ENSURE_ARG_POINTER(aOffset);
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ *aReusable = false; // message per file
+ *aOffset = 0;
+
+ // construct path to file
+ nsCOMPtr<nsIFile> path;
+ nsresult rv = aMsgFolder->GetFilePath(getter_AddRefs(path));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aMsgToken.IsEmpty())
+ {
+ MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
+ ("GetMsgInputStream - empty storeToken!!\n"));
+ return NS_ERROR_FAILURE;
+ }
+
+ path->Append(NS_LITERAL_STRING("cur"));
+
+ // let's check if the folder exists
+ bool exists;
+ path->Exists(&exists);
+ if (!exists) {
+ MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
+ ("GetMsgInputStream - oops! cur subfolder does not exist!\n"));
+ rv = path->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ path->AppendNative(aMsgToken);
+ return NS_NewLocalFileInputStream(aResult, path);
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::DeleteMessages(nsIArray *aHdrArray)
+{
+ uint32_t messageCount;
+ nsresult rv = aHdrArray->GetLength(&messageCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgFolder> folder;
+
+ for (uint32_t i = 0; i < messageCount; i++)
+ {
+ nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
+ if (NS_FAILED(rv))
+ continue;
+ msgHdr->GetFolder(getter_AddRefs(folder));
+ nsCOMPtr<nsIFile> path;
+ rv = folder->GetFilePath(getter_AddRefs(path));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString fileName;
+ msgHdr->GetStringProperty("storeToken", getter_Copies(fileName));
+
+ if (fileName.IsEmpty())
+ {
+ MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
+ ("DeleteMessages - empty storeToken!!\n"));
+ // Perhaps an offline store has not downloaded this particular message.
+ continue;
+ }
+
+ path->Append(NS_LITERAL_STRING("cur"));
+ path->AppendNative(fileName);
+
+ // Let's check if the message exists.
+ bool exists;
+ path->Exists(&exists);
+ if (!exists)
+ {
+ MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
+ ("DeleteMessages - file does not exist !!\n"));
+ // Perhaps an offline store has not downloaded this particular message.
+ continue;
+ }
+ path->Remove(false);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgMaildirStore::CopyMessages(bool aIsMove, nsIArray *aHdrArray,
+ nsIMsgFolder *aDstFolder,
+ nsIMsgCopyServiceListener *aListener,
+ nsIArray **aDstHdrs,
+ nsITransaction **aUndoAction,
+ bool *aCopyDone)
+{
+ NS_ENSURE_ARG_POINTER(aHdrArray);
+ NS_ENSURE_ARG_POINTER(aDstFolder);
+ NS_ENSURE_ARG_POINTER(aCopyDone);
+ NS_ENSURE_ARG_POINTER(aUndoAction);
+
+ *aCopyDone = false;
+
+ nsCOMPtr<nsIMsgFolder> srcFolder;
+ nsresult rv;
+ nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, 0, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = msgHdr->GetFolder(getter_AddRefs(srcFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Both source and destination folders must use maildir type store.
+ nsCOMPtr<nsIMsgPluggableStore> srcStore;
+ nsAutoCString srcType;
+ srcFolder->GetMsgStore(getter_AddRefs(srcStore));
+ if (srcStore)
+ srcStore->GetStoreType(srcType);
+ nsCOMPtr<nsIMsgPluggableStore> dstStore;
+ nsAutoCString dstType;
+ aDstFolder->GetMsgStore(getter_AddRefs(dstStore));
+ if (dstStore)
+ dstStore->GetStoreType(dstType);
+ if (!srcType.EqualsLiteral("maildir") || !dstType.EqualsLiteral("maildir"))
+ return NS_OK;
+
+ // Both source and destination must be local folders. In theory we could
+ // do efficient copies of the offline store of IMAP, but this is not
+ // supported yet. For that, we need to deal with both correct handling
+ // of deletes from the src server, and msgKey = UIDL in the dst folder.
+ nsCOMPtr<nsIMsgLocalMailFolder> destLocalFolder(do_QueryInterface(aDstFolder));
+ if (!destLocalFolder)
+ return NS_OK;
+ nsCOMPtr<nsIMsgLocalMailFolder> srcLocalFolder(do_QueryInterface(srcFolder));
+ if (!srcLocalFolder)
+ return NS_OK;
+
+ // We should be able to use a file move for an efficient copy.
+
+ nsCOMPtr<nsIFile> destFolderPath;
+ nsCOMPtr<nsIMsgDatabase> destDB;
+ aDstFolder->GetMsgDatabase(getter_AddRefs(destDB));
+ rv = aDstFolder->GetFilePath(getter_AddRefs(destFolderPath));
+ NS_ENSURE_SUCCESS(rv, rv);
+ destFolderPath->Append(NS_LITERAL_STRING("cur"));
+
+ nsCOMPtr<nsIFile> srcFolderPath;
+ rv = srcFolder->GetFilePath(getter_AddRefs(srcFolderPath));
+ NS_ENSURE_SUCCESS(rv, rv);
+ srcFolderPath->Append(NS_LITERAL_STRING("cur"));
+
+ nsCOMPtr<nsIMsgDatabase> srcDB;
+ srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
+ RefPtr<nsLocalMoveCopyMsgTxn> msgTxn = new nsLocalMoveCopyMsgTxn;
+ NS_ENSURE_TRUE(msgTxn, NS_ERROR_OUT_OF_MEMORY);
+ if (NS_SUCCEEDED(msgTxn->Init(srcFolder, aDstFolder, aIsMove)))
+ {
+ if (aIsMove)
+ msgTxn->SetTransactionType(nsIMessenger::eMoveMsg);
+ else
+ msgTxn->SetTransactionType(nsIMessenger::eCopyMsg);
+ }
+
+ if (aListener)
+ aListener->OnStartCopy();
+
+ nsCOMPtr<nsIMutableArray> dstHdrs(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint32_t messageCount;
+ rv = aHdrArray->GetLength(&messageCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (uint32_t i = 0; i < messageCount; i++)
+ {
+ nsCOMPtr<nsIMsgDBHdr> srcHdr = do_QueryElementAt(aHdrArray, i, &rv);
+ if (NS_FAILED(rv))
+ {
+ MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
+ ("srcHdr null\n"));
+ continue;
+ }
+ nsMsgKey srcKey;
+ srcHdr->GetMessageKey(&srcKey);
+ msgTxn->AddSrcKey(srcKey);
+ nsAutoCString fileName;
+ srcHdr->GetStringProperty("storeToken", getter_Copies(fileName));
+ if (fileName.IsEmpty())
+ {
+ MOZ_LOG(MailDirLog, mozilla::LogLevel::Info,
+ ("GetMsgInputStream - empty storeToken!!\n"));
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIFile> srcFile;
+ rv = srcFolderPath->Clone(getter_AddRefs(srcFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ srcFile->AppendNative(fileName);
+
+ nsCOMPtr<nsIFile> destFile;
+ destFolderPath->Clone(getter_AddRefs(destFile));
+ destFile->AppendNative(fileName);
+ bool exists;
+ destFile->Exists(&exists);
+ if (exists)
+ {
+ rv = destFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+ destFile->GetNativeLeafName(fileName);
+ }
+ if (aIsMove)
+ rv = srcFile->MoveToNative(destFolderPath, fileName);
+ else
+ rv = srcFile->CopyToNative(destFolderPath, fileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgDBHdr> destHdr;
+ if (destDB)
+ {
+ rv = destDB->CopyHdrFromExistingHdr(nsMsgKey_None, srcHdr, true, getter_AddRefs(destHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+ destHdr->SetStringProperty("storeToken", fileName.get());
+ dstHdrs->AppendElement(destHdr, false);
+ nsMsgKey dstKey;
+ destHdr->GetMessageKey(&dstKey);
+ msgTxn->AddDstKey(dstKey);
+ if (aListener)
+ aListener->SetMessageKey(dstKey);
+ }
+ }
+ nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ if (notifier)
+ notifier->NotifyMsgsMoveCopyCompleted(aIsMove, aHdrArray, aDstFolder,
+ dstHdrs);
+
+ // For now, we only support local dest folders, and for those we are done and
+ // can delete the messages. Perhaps this should be moved into the folder
+ // when we try to support other folder types.
+ if (aIsMove)
+ {
+ for (uint32_t i = 0; i < messageCount; ++i)
+ {
+ nsCOMPtr<nsIMsgDBHdr> msgDBHdr(do_QueryElementAt(aHdrArray, i, &rv));
+ rv = srcDB->DeleteHeader(msgDBHdr, nullptr, false, true);
+ }
+ }
+
+ *aCopyDone = true;
+ nsCOMPtr<nsISupports> srcSupports(do_QueryInterface(srcFolder));
+ if (destLocalFolder)
+ destLocalFolder->OnCopyCompleted(srcSupports, true);
+ if (aListener)
+ aListener->OnStopCopy(NS_OK);
+ msgTxn.forget(aUndoAction);
+ dstHdrs.forget(aDstHdrs);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgMaildirStore::GetSupportsCompaction(bool *aSupportsCompaction)
+{
+ NS_ENSURE_ARG_POINTER(aSupportsCompaction);
+ *aSupportsCompaction = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::CompactFolder(nsIMsgFolder *aFolder,
+ nsIUrlListener *aListener,
+ nsIMsgWindow *aMsgWindow)
+{
+ return NS_OK;
+}
+
+class MaildirStoreParser
+{
+public:
+ MaildirStoreParser(nsIMsgFolder *aFolder, nsIMsgDatabase *aMsgDB,
+ nsISimpleEnumerator *aDirectoryEnumerator,
+ nsIUrlListener *aUrlListener);
+ virtual ~MaildirStoreParser();
+
+ nsresult ParseNextMessage(nsIFile *aFile);
+ static void TimerCallback(nsITimer *aTimer, void *aClosure);
+ nsresult StartTimer();
+
+ nsCOMPtr<nsISimpleEnumerator> m_directoryEnumerator;
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsCOMPtr<nsIMsgDatabase> m_db;
+ nsCOMPtr<nsITimer> m_timer;
+ nsCOMPtr<nsIUrlListener> m_listener;
+};
+
+MaildirStoreParser::MaildirStoreParser(nsIMsgFolder *aFolder,
+ nsIMsgDatabase *aMsgDB,
+ nsISimpleEnumerator *aDirEnum,
+ nsIUrlListener *aUrlListener)
+{
+ m_folder = aFolder;
+ m_db = aMsgDB;
+ m_directoryEnumerator = aDirEnum;
+ m_listener = aUrlListener;
+}
+
+MaildirStoreParser::~MaildirStoreParser()
+{
+}
+
+nsresult MaildirStoreParser::ParseNextMessage(nsIFile *aFile)
+{
+ nsresult rv;
+ nsCOMPtr<nsIInputStream> inputStream;
+ nsCOMPtr<nsIMsgParseMailMsgState> msgParser =
+ do_CreateInstance(NS_PARSEMAILMSGSTATE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ msgParser->SetMailDB(m_db);
+ nsCOMPtr<nsIMsgDBHdr> newMsgHdr;
+ rv = m_db->CreateNewHdr(nsMsgKey_None, getter_AddRefs(newMsgHdr));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ newMsgHdr->SetMessageOffset(0);
+
+ rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile);
+ if (NS_SUCCEEDED(rv) && inputStream)
+ {
+ nsMsgLineStreamBuffer *inputStreamBuffer =
+ new nsMsgLineStreamBuffer(FILE_IO_BUFFER_SIZE, true, false);
+ int64_t fileSize;
+ aFile->GetFileSize(&fileSize);
+ msgParser->SetNewMsgHdr(newMsgHdr);
+ msgParser->SetState(nsIMsgParseMailMsgState::ParseHeadersState);
+ msgParser->SetEnvelopePos(0);
+ bool needMoreData = false;
+ char * newLine = nullptr;
+ uint32_t numBytesInLine = 0;
+ // we only have to read the headers, because we know the message size
+ // from the file size. So we can do this in one time slice.
+ do
+ {
+ newLine = inputStreamBuffer->ReadNextLine(inputStream, numBytesInLine,
+ needMoreData);
+ if (newLine)
+ {
+ msgParser->ParseAFolderLine(newLine, numBytesInLine);
+ NS_Free(newLine);
+ }
+ } while (newLine && numBytesInLine > 0);
+
+ msgParser->FinishHeader();
+ // A single message needs to be less than 4GB
+ newMsgHdr->SetMessageSize((uint32_t) fileSize);
+ m_db->AddNewHdrToDB(newMsgHdr, true);
+ nsAutoCString storeToken;
+ aFile->GetNativeLeafName(storeToken);
+ newMsgHdr->SetStringProperty("storeToken", storeToken.get());
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+ return rv;
+}
+
+void MaildirStoreParser::TimerCallback(nsITimer *aTimer, void *aClosure)
+{
+ MaildirStoreParser *parser = (MaildirStoreParser *) aClosure;
+ bool hasMore;
+ parser->m_directoryEnumerator->HasMoreElements(&hasMore);
+ if (!hasMore)
+ {
+ nsCOMPtr<nsIMsgPluggableStore> store;
+ parser->m_folder->GetMsgStore(getter_AddRefs(store));
+ parser->m_timer->Cancel();
+ parser->m_db->SetSummaryValid(true);
+// store->SetSummaryFileValid(parser->m_folder, parser->m_db, true);
+ if (parser->m_listener)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIMailboxUrl> mailboxurl =
+ do_CreateInstance(NS_MAILBOXURL_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && mailboxurl)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> url = do_QueryInterface(mailboxurl);
+ url->SetUpdatingFolder(true);
+ nsAutoCString uriSpec("mailbox://");
+ // ### TODO - what if SetSpec fails?
+ (void) url->SetSpec(uriSpec);
+ parser->m_listener->OnStopRunningUrl(url, NS_OK);
+ }
+ }
+ // Parsing complete and timer cancelled, so we release the parser object.
+ delete parser;
+ return;
+ }
+ nsCOMPtr<nsISupports> aSupport;
+ parser->m_directoryEnumerator->GetNext(getter_AddRefs(aSupport));
+ nsresult rv;
+ nsCOMPtr<nsIFile> currentFile(do_QueryInterface(aSupport, &rv));
+ NS_ENSURE_SUCCESS_VOID(rv);
+ parser->ParseNextMessage(currentFile);
+ // ### TODO - what if this fails?
+}
+
+nsresult MaildirStoreParser::StartTimer()
+{
+ nsresult rv;
+ m_timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ m_timer->InitWithFuncCallback(TimerCallback, (void *) this, 0,
+ nsITimer::TYPE_REPEATING_SLACK);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::RebuildIndex(nsIMsgFolder *aFolder,
+ nsIMsgDatabase *aMsgDB,
+ nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aListener)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ // This code needs to iterate over the maildir files, and parse each
+ // file and add a msg hdr to the db for the file.
+ nsCOMPtr<nsIFile> path;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(path));
+ NS_ENSURE_SUCCESS(rv, rv);
+ path->Append(NS_LITERAL_STRING("cur"));
+
+ nsCOMPtr<nsISimpleEnumerator> directoryEnumerator;
+ rv = path->GetDirectoryEntries(getter_AddRefs(directoryEnumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ MaildirStoreParser *fileParser = new MaildirStoreParser(aFolder, aMsgDB,
+ directoryEnumerator,
+ aListener);
+ NS_ENSURE_TRUE(fileParser, NS_ERROR_OUT_OF_MEMORY);
+ fileParser->StartTimer();
+ ResetForceReparse(aMsgDB);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::ChangeFlags(nsIArray *aHdrArray,
+ uint32_t aFlags,
+ bool aSet)
+{
+ NS_ENSURE_ARG_POINTER(aHdrArray);
+
+ uint32_t messageCount;
+ nsresult rv = aHdrArray->GetLength(&messageCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ for (uint32_t i = 0; i < messageCount; i++)
+ {
+ nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
+ // get output stream for header
+ nsCOMPtr<nsIOutputStream> outputStream;
+ rv = GetOutputStream(msgHdr, outputStream);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Seek to x-mozilla-status offset and rewrite value.
+ rv = UpdateFolderFlag(msgHdr, aSet, aFlags, outputStream);
+ if (NS_FAILED(rv))
+ NS_WARNING("updateFolderFlag failed");
+ }
+ return NS_OK;
+}
+
+// get output stream from header
+nsresult
+nsMsgMaildirStore::GetOutputStream(nsIMsgDBHdr *aHdr,
+ nsCOMPtr<nsIOutputStream> &aOutputStream)
+{
+ // file name is stored in message header property "storeToken"
+ nsAutoCString fileName;
+ aHdr->GetStringProperty("storeToken", getter_Copies(fileName));
+ if (fileName.IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsresult rv = aHdr->GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> folderPath;
+ rv = folder->GetFilePath(getter_AddRefs(folderPath));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> maildirFile;
+ folderPath->Clone(getter_AddRefs(maildirFile));
+ maildirFile->Append(NS_LITERAL_STRING("cur"));
+ maildirFile->AppendNative(fileName);
+
+ return MsgGetFileStream(maildirFile, getter_AddRefs(aOutputStream));
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::ChangeKeywords(nsIArray *aHdrArray,
+ const nsACString &aKeywords,
+ bool aAdd)
+{
+ NS_ENSURE_ARG_POINTER(aHdrArray);
+ NS_ENSURE_ARG_POINTER(aHdrArray);
+ nsCOMPtr<nsIOutputStream> outputStream;
+ nsCOMPtr<nsISeekableStream> seekableStream;
+
+ uint32_t messageCount;
+ nsresult rv = aHdrArray->GetLength(&messageCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!messageCount)
+ return NS_ERROR_INVALID_ARG;
+
+ nsAutoPtr<nsLineBuffer<char> > lineBuffer(new nsLineBuffer<char>);
+ NS_ENSURE_TRUE(lineBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ nsTArray<nsCString> keywordArray;
+ ParseString(aKeywords, ' ', keywordArray);
+
+ for (uint32_t i = 0; i < messageCount; ++i) // for each message
+ {
+ nsCOMPtr<nsIMsgDBHdr> message = do_QueryElementAt(aHdrArray, i, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // get output stream for header
+ nsCOMPtr<nsIOutputStream> outputStream;
+ rv = GetOutputStream(message, outputStream);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIInputStream> inputStream = do_QueryInterface(outputStream, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsISeekableStream> seekableStream(do_QueryInterface(inputStream, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint32_t statusOffset = 0;
+ (void)message->GetStatusOffset(&statusOffset);
+ uint64_t desiredOffset = statusOffset;
+
+ ChangeKeywordsHelper(message, desiredOffset, lineBuffer, keywordArray,
+ aAdd, outputStream, seekableStream, inputStream);
+ if (inputStream)
+ inputStream->Close();
+ // ### TODO - if growKeywords property is set on the message header,
+ // we need to rewrite the message file with extra room for the keywords,
+ // or schedule some sort of background task to do this.
+ }
+ lineBuffer = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgMaildirStore::GetStoreType(nsACString& aType)
+{
+ aType.AssignLiteral("maildir");
+ return NS_OK;
+}
+
+/**
+ * Finds the directory associated with this folder. That is if the path is
+ * c:\Inbox, it will return c:\Inbox.sbd if it succeeds. Path is strictly
+ * an out parameter.
+ */
+nsresult nsMsgMaildirStore::GetDirectoryForFolder(nsIFile *path)
+{
+ // add directory separator to the path
+ nsAutoString leafName;
+ path->GetLeafName(leafName);
+ leafName.AppendLiteral(FOLDER_SUFFIX);
+ return path->SetLeafName(leafName);
+}
+
+nsresult nsMsgMaildirStore::CreateDirectoryForFolder(nsIFile *path,
+ bool aIsServer)
+{
+ nsresult rv = NS_OK;
+ if (!aIsServer)
+ {
+ rv = GetDirectoryForFolder(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ bool pathIsDirectory = false;
+ path->IsDirectory(&pathIsDirectory);
+ if (!pathIsDirectory)
+ {
+ bool pathExists;
+ path->Exists(&pathExists);
+ //If for some reason there's a file with the directory separator
+ //then we are going to fail.
+ rv = pathExists ? NS_MSG_COULD_NOT_CREATE_DIRECTORY :
+ path->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ }
+ return rv;
+}
diff --git a/mailnews/local/src/nsMsgMaildirStore.h b/mailnews/local/src/nsMsgMaildirStore.h
new file mode 100644
index 000000000..f15944e5d
--- /dev/null
+++ b/mailnews/local/src/nsMsgMaildirStore.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ Class for handling Maildir stores.
+*/
+
+#ifndef nsMsgMaildirStore_h__
+#define nsMsgMaildirStore_h__
+
+#include "nsMsgLocalStoreUtils.h"
+#include "nsIFile.h"
+#include "nsMsgMessageFlags.h"
+
+class nsMsgMaildirStore final : public nsMsgLocalStoreUtils, nsIMsgPluggableStore
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGPLUGGABLESTORE
+
+ nsMsgMaildirStore();
+
+private:
+ ~nsMsgMaildirStore();
+
+protected:
+ nsresult GetDirectoryForFolder(nsIFile *path);
+ nsresult CreateDirectoryForFolder(nsIFile *path, bool aIsServer);
+
+ nsresult CreateMaildir(nsIFile *path);
+ nsresult AddSubFolders(nsIMsgFolder *parent, nsIFile *path, bool deep);
+ nsresult GetOutputStream(nsIMsgDBHdr *aHdr,
+ nsCOMPtr<nsIOutputStream> &aOutputStream);
+
+};
+#endif
diff --git a/mailnews/local/src/nsNoIncomingServer.cpp b/mailnews/local/src/nsNoIncomingServer.cpp
new file mode 100644
index 000000000..1c18bf7fd
--- /dev/null
+++ b/mailnews/local/src/nsNoIncomingServer.cpp
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 4; 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" // pre-compiled headers
+
+#include "prmem.h"
+#include "plstr.h"
+#include "prprf.h"
+#include "nsNoIncomingServer.h"
+#include "nsMsgLocalCID.h"
+#include "nsMsgFolderFlags.h"
+#include "nsIMsgLocalMailFolder.h"
+#include "nsIMsgMailSession.h"
+#include "nsMsgBaseCID.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIPop3IncomingServer.h"
+#include "nsServiceManagerUtils.h"
+#include "nsMsgUtils.h"
+
+NS_IMPL_ISUPPORTS_INHERITED(nsNoIncomingServer,
+ nsMsgIncomingServer,
+ nsINoIncomingServer,
+ nsILocalMailIncomingServer)
+
+nsNoIncomingServer::nsNoIncomingServer()
+{
+}
+
+nsNoIncomingServer::~nsNoIncomingServer()
+{
+}
+
+NS_IMETHODIMP
+nsNoIncomingServer::GetLocalStoreType(nsACString& type)
+{
+ type.AssignLiteral("mailbox");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoIncomingServer::GetLocalDatabaseType(nsACString& type)
+{
+ type.AssignLiteral("mailbox");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoIncomingServer::GetAccountManagerChrome(nsAString& aResult)
+{
+ aResult.AssignLiteral("am-serverwithnoidentities.xul");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoIncomingServer::SetFlagsOnDefaultMailboxes()
+{
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder =
+ do_QueryInterface(rootFolder, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // None server may have an inbox if it's deferred to,
+ // or if it's the smart mailboxes account.
+ localFolder->SetFlagsOnDefaultMailboxes(nsMsgFolderFlags::SpecialUse);
+
+ return NS_OK;
+}
+
+// TODO: make this work with maildir message store, bug 890742.
+NS_IMETHODIMP nsNoIncomingServer::CopyDefaultMessages(const char *folderNameOnDisk)
+{
+ NS_ENSURE_ARG(folderNameOnDisk);
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get defaults directory for messenger files. MailSession service appends 'messenger' to the
+ // the app defaults folder and returns it. Locale will be added to the path, if there is one.
+ nsCOMPtr<nsIFile> defaultMessagesFile;
+ rv = mailSession->GetDataFilesDir("messenger", getter_AddRefs(defaultMessagesFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // check if bin/defaults/messenger/<folderNameOnDisk>
+ // (or bin/defaults/messenger/<locale>/<folderNameOnDisk> if we had a locale provide) exists.
+ // it doesn't have to exist. if it doesn't, return
+ rv = defaultMessagesFile->AppendNative(nsDependentCString(folderNameOnDisk));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists;
+ rv = defaultMessagesFile->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!exists)
+ return NS_OK;
+
+ nsCOMPtr<nsIFile> parentDir;
+ rv = GetLocalPath(getter_AddRefs(parentDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // check if parentDir/<folderNameOnDisk> exists
+ {
+ nsCOMPtr<nsIFile> testDir;
+ rv = parentDir->Clone(getter_AddRefs(testDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = testDir->AppendNative(nsDependentCString(folderNameOnDisk));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = testDir->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // if it exists add to the end, else copy
+ if (exists)
+ {
+#ifdef DEBUG
+ printf("append default %s (unimplemented)\n", folderNameOnDisk);
+#endif
+ // todo for bug #1181 (the bug ID seems wrong...)
+ // open folderFile, seek to end
+ // read defaultMessagesFile, write to folderFile
+ }
+ else {
+#ifdef DEBUG
+ printf("copy default %s\n",folderNameOnDisk);
+#endif
+ rv = defaultMessagesFile->CopyTo(parentDir, EmptyString());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsNoIncomingServer::CreateDefaultMailboxes()
+{
+ nsresult rv;
+ bool isHidden = false;
+ GetHidden(&isHidden);
+ if (isHidden)
+ return NS_OK;
+
+ // notice, no Inbox, unless we're deferred to...
+ bool isDeferredTo;
+ if (NS_SUCCEEDED(GetIsDeferredTo(&isDeferredTo)) && isDeferredTo)
+ {
+ rv = CreateLocalFolder(NS_LITERAL_STRING("Inbox"));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ rv = CreateLocalFolder(NS_LITERAL_STRING("Trash"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // copy the default templates into the Templates folder
+ rv = CopyDefaultMessages("Templates");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CreateLocalFolder(NS_LITERAL_STRING("Unsent Messages"));
+}
+
+NS_IMETHODIMP
+nsNoIncomingServer::GetNewMail(nsIMsgWindow *aMsgWindow, nsIUrlListener *aUrlListener, nsIMsgFolder *aInbox, nsIURI **aResult)
+{
+ nsCOMArray<nsIPop3IncomingServer> deferredServers;
+ nsresult rv = GetDeferredServers(this, deferredServers);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!deferredServers.IsEmpty())
+ {
+ rv = deferredServers[0]->DownloadMailFromServers(deferredServers.Elements(),
+ deferredServers.Length(), aMsgWindow, aInbox, aUrlListener);
+ }
+ // listener might be counting on us to send a notification.
+ else if (aUrlListener)
+ aUrlListener->OnStopRunningUrl(nullptr, NS_OK);
+ return rv;
+}
+
+
+NS_IMETHODIMP
+nsNoIncomingServer::GetCanSearchMessages(bool *canSearchMessages)
+{
+ NS_ENSURE_ARG_POINTER(canSearchMessages);
+ *canSearchMessages = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoIncomingServer::GetServerRequiresPasswordForBiff(bool *aServerRequiresPasswordForBiff)
+{
+ NS_ENSURE_ARG_POINTER(aServerRequiresPasswordForBiff);
+ *aServerRequiresPasswordForBiff = false; // for local folders, we don't require a password
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoIncomingServer::GetSortOrder(int32_t* aSortOrder)
+{
+ NS_ENSURE_ARG_POINTER(aSortOrder);
+ *aSortOrder = 200000000;
+ return NS_OK;
+}
+
diff --git a/mailnews/local/src/nsNoIncomingServer.h b/mailnews/local/src/nsNoIncomingServer.h
new file mode 100644
index 000000000..809531627
--- /dev/null
+++ b/mailnews/local/src/nsNoIncomingServer.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef __nsNoIncomingServer_h
+#define __nsNoIncomingServer_h
+
+#include "mozilla/Attributes.h"
+#include "msgCore.h"
+#include "nsINoIncomingServer.h"
+#include "nsILocalMailIncomingServer.h"
+#include "nsMsgIncomingServer.h"
+#include "nsMailboxServer.h"
+
+/* get some implementation from nsMsgIncomingServer */
+class nsNoIncomingServer : public nsMailboxServer,
+ public nsINoIncomingServer,
+ public nsILocalMailIncomingServer
+
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSINOINCOMINGSERVER
+ NS_DECL_NSILOCALMAILINCOMINGSERVER
+
+ nsNoIncomingServer();
+
+ NS_IMETHOD GetLocalStoreType(nsACString& type) override;
+ NS_IMETHOD GetLocalDatabaseType(nsACString& type) override;
+ NS_IMETHOD GetCanSearchMessages(bool *canSearchMessages) override;
+ NS_IMETHOD GetServerRequiresPasswordForBiff(bool *aServerRequiresPasswordForBiff) override;
+ NS_IMETHOD GetAccountManagerChrome(nsAString& aResult) override;
+ NS_IMETHOD GetSortOrder(int32_t* aSortOrder) override;
+
+private:
+ virtual ~nsNoIncomingServer();
+};
+
+
+#endif
diff --git a/mailnews/local/src/nsNoneService.cpp b/mailnews/local/src/nsNoneService.cpp
new file mode 100644
index 000000000..dad56c063
--- /dev/null
+++ b/mailnews/local/src/nsNoneService.cpp
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsNoneService.h"
+#include "nsINoIncomingServer.h"
+#include "nsINoneService.h"
+#include "nsIMsgProtocolInfo.h"
+
+#include "nsMsgLocalCID.h"
+#include "nsMsgBaseCID.h"
+#include "nsIFile.h"
+#include "nsCOMPtr.h"
+#include "nsMsgUtils.h"
+
+#include "nsIDirectoryService.h"
+#include "nsMailDirServiceDefs.h"
+
+#define PREF_MAIL_ROOT_NONE "mail.root.none" // old - for backward compatibility only
+#define PREF_MAIL_ROOT_NONE_REL "mail.root.none-rel"
+
+nsNoneService::nsNoneService()
+{
+}
+
+nsNoneService::~nsNoneService()
+{}
+
+NS_IMPL_ISUPPORTS(nsNoneService, nsINoneService, nsIMsgProtocolInfo)
+
+NS_IMETHODIMP
+nsNoneService::SetDefaultLocalPath(nsIFile *aPath)
+{
+ NS_ENSURE_ARG(aPath);
+ return NS_SetPersistentFile(PREF_MAIL_ROOT_NONE_REL, PREF_MAIL_ROOT_NONE, aPath);
+}
+
+NS_IMETHODIMP
+nsNoneService::GetDefaultLocalPath(nsIFile ** aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+
+ bool havePref;
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_GetPersistentFile(PREF_MAIL_ROOT_NONE_REL,
+ PREF_MAIL_ROOT_NONE,
+ NS_APP_MAIL_50_DIR,
+ havePref,
+ getter_AddRefs(localFile));
+ if (NS_FAILED(rv)) return rv;
+
+ bool exists;
+ rv = localFile->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && !exists)
+ rv = localFile->Create(nsIFile::DIRECTORY_TYPE, 0775);
+ if (NS_FAILED(rv)) return rv;
+
+ if (!havePref || !exists)
+ {
+ rv = NS_SetPersistentFile(PREF_MAIL_ROOT_NONE_REL, PREF_MAIL_ROOT_NONE, localFile);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to set root dir pref.");
+ }
+
+ NS_IF_ADDREF(*aResult = localFile);
+ return NS_OK;
+
+}
+
+
+NS_IMETHODIMP
+nsNoneService::GetServerIID(nsIID* *aServerIID)
+{
+ *aServerIID = new nsIID(NS_GET_IID(nsINoIncomingServer));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoneService::GetRequiresUsername(bool *aRequiresUsername)
+{
+ NS_ENSURE_ARG_POINTER(aRequiresUsername);
+ *aRequiresUsername = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoneService::GetPreflightPrettyNameWithEmailAddress(bool *aPreflightPrettyNameWithEmailAddress)
+{
+ NS_ENSURE_ARG_POINTER(aPreflightPrettyNameWithEmailAddress);
+ *aPreflightPrettyNameWithEmailAddress = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoneService::GetCanLoginAtStartUp(bool *aCanLoginAtStartUp)
+{
+ NS_ENSURE_ARG_POINTER(aCanLoginAtStartUp);
+ *aCanLoginAtStartUp = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoneService::GetCanDelete(bool *aCanDelete)
+{
+ NS_ENSURE_ARG_POINTER(aCanDelete);
+ *aCanDelete = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoneService::GetCanDuplicate(bool *aCanDuplicate)
+{
+ NS_ENSURE_ARG_POINTER(aCanDuplicate);
+ *aCanDuplicate = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoneService::GetCanGetMessages(bool *aCanGetMessages)
+{
+ NS_ENSURE_ARG_POINTER(aCanGetMessages);
+ *aCanGetMessages = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoneService::GetCanGetIncomingMessages(bool *aCanGetIncomingMessages)
+{
+ NS_ENSURE_ARG_POINTER(aCanGetIncomingMessages);
+ *aCanGetIncomingMessages = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoneService::GetDefaultDoBiff(bool *aDoBiff)
+{
+ NS_ENSURE_ARG_POINTER(aDoBiff);
+ // by default, don't do biff for "none" servers
+ *aDoBiff = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoneService::GetDefaultServerPort(bool isSecure, int32_t *aDefaultPort)
+{
+ NS_ENSURE_ARG_POINTER(aDefaultPort);
+ *aDefaultPort = -1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoneService::GetShowComposeMsgLink(bool *showComposeMsgLink)
+{
+ NS_ENSURE_ARG_POINTER(showComposeMsgLink);
+ *showComposeMsgLink = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNoneService::GetFoldersCreatedAsync(bool *aAsyncCreation)
+{
+ NS_ENSURE_ARG_POINTER(aAsyncCreation);
+ *aAsyncCreation = false;
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsNoneService.h b/mailnews/local/src/nsNoneService.h
new file mode 100644
index 000000000..fe8495528
--- /dev/null
+++ b/mailnews/local/src/nsNoneService.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsNoneService_h___
+#define nsNoneService_h___
+
+#include "nscore.h"
+
+#include "nsIMsgProtocolInfo.h"
+#include "nsINoneService.h"
+
+class nsNoneService : public nsIMsgProtocolInfo, public nsINoneService
+{
+public:
+
+ nsNoneService();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGPROTOCOLINFO
+ NS_DECL_NSINONESERVICE
+
+private:
+ virtual ~nsNoneService();
+};
+
+#endif /* nsNoneService_h___ */
diff --git a/mailnews/local/src/nsParseMailbox.cpp b/mailnews/local/src/nsParseMailbox.cpp
new file mode 100644
index 000000000..9d68e5cd1
--- /dev/null
+++ b/mailnews/local/src/nsParseMailbox.cpp
@@ -0,0 +1,2624 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "msgCore.h"
+#include "nsIURI.h"
+#include "nsParseMailbox.h"
+#include "nsIMsgHdr.h"
+#include "nsIMsgDatabase.h"
+#include "nsMsgMessageFlags.h"
+#include "nsIDBFolderInfo.h"
+#include "nsIInputStream.h"
+#include "nsIFile.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgDBCID.h"
+#include "nsIMailboxUrl.h"
+#include "nsNetUtil.h"
+#include "nsMsgFolderFlags.h"
+#include "nsIMsgFolder.h"
+#include "nsIURL.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsIMsgFilterList.h"
+#include "nsIMsgFilter.h"
+#include "nsIIOService.h"
+#include "nsNetCID.h"
+#include "nsRDFCID.h"
+#include "nsIRDFService.h"
+#include "nsMsgI18N.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsIMsgLocalMailFolder.h"
+#include "nsMsgUtils.h"
+#include "prprf.h"
+#include "prmem.h"
+#include "nsISeekableStream.h"
+#include "nsIMimeHeaders.h"
+#include "nsIMsgMdnGenerator.h"
+#include "nsMsgSearchCore.h"
+#include "nsMailHeaders.h"
+#include "nsIMsgMailSession.h"
+#include "nsIMsgComposeParams.h"
+#include "nsMsgCompCID.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIDocShell.h"
+#include "nsIMsgCompose.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsIMsgComposeService.h"
+#include "nsIMsgCopyService.h"
+#include "nsICryptoHash.h"
+#include "nsIStringBundle.h"
+#include "nsIMsgFilterPlugin.h"
+#include "nsIMutableArray.h"
+#include "nsArrayUtils.h"
+#include "nsIMsgFilterCustomAction.h"
+#include <ctype.h>
+#include "nsIMsgPluggableStore.h"
+#include "mozilla/Services.h"
+#include "nsQueryObject.h"
+#include "nsIOutputStream.h"
+#include "mozilla/Attributes.h"
+
+static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+
+/* the following macros actually implement addref, release and query interface for our component. */
+NS_IMPL_ISUPPORTS_INHERITED(nsMsgMailboxParser,
+ nsParseMailMessageState,
+ nsIStreamListener,
+ nsIRequestObserver)
+
+// Whenever data arrives from the connection, core netlib notifices the protocol by calling
+// OnDataAvailable. We then read and process the incoming data from the input stream.
+NS_IMETHODIMP nsMsgMailboxParser::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *aIStream, uint64_t sourceOffset, uint32_t aLength)
+{
+ // right now, this really just means turn around and process the url
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIURI> url = do_QueryInterface(ctxt, &rv);
+ if (NS_SUCCEEDED(rv))
+ rv = ProcessMailboxInputStream(url, aIStream, aLength);
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgMailboxParser::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
+{
+ m_startTime = PR_Now();
+
+
+ // extract the appropriate event sinks from the url and initialize them in our protocol data
+ // the URL should be queried for a nsIMailboxURL. If it doesn't support a mailbox URL interface then
+ // we have an error.
+ nsresult rv = NS_OK;
+
+ nsCOMPtr<nsIIOService> ioServ =
+ mozilla::services::GetIOService();
+ NS_ENSURE_TRUE(ioServ, NS_ERROR_UNEXPECTED);
+
+ nsCOMPtr<nsIMailboxUrl> runningUrl = do_QueryInterface(ctxt, &rv);
+
+ nsCOMPtr<nsIMsgMailNewsUrl> url = do_QueryInterface(ctxt);
+ nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(m_folder);
+
+ if (NS_SUCCEEDED(rv) && runningUrl && folder)
+ {
+ url->GetStatusFeedback(getter_AddRefs(m_statusFeedback));
+
+ // okay, now fill in our event sinks...Note that each getter ref counts before
+ // it returns the interface to us...we'll release when we are done
+
+ folder->GetName(m_folderName);
+
+ nsCOMPtr<nsIFile> path;
+ folder->GetFilePath(getter_AddRefs(path));
+
+ if (path)
+ {
+ int64_t fileSize;
+ path->GetFileSize(&fileSize);
+ // the size of the mailbox file is our total base line for measuring progress
+ m_graph_progress_total = fileSize;
+ UpdateStatusText("buildingSummary");
+ nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+ if (msgDBService)
+ {
+ // Use OpenFolderDB to always open the db so that db's m_folder
+ // is set correctly.
+ rv = msgDBService->OpenFolderDB(folder, true,
+ getter_AddRefs(m_mailDB));
+ if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
+ rv = msgDBService->CreateNewDB(folder,
+ getter_AddRefs(m_mailDB));
+
+ if (m_mailDB)
+ m_mailDB->AddListener(this);
+ }
+ NS_ASSERTION(m_mailDB, "failed to open mail db parsing folder");
+
+ // try to get a backup message database
+ nsresult rvignore = folder->GetBackupMsgDatabase(
+ getter_AddRefs(m_backupMailDB));
+
+ // We'll accept failures and move on, as we're dealing with some
+ // sort of unknown problem to begin with.
+ if (NS_FAILED(rvignore))
+ {
+ if (m_backupMailDB)
+ m_backupMailDB->RemoveListener(this);
+ m_backupMailDB = nullptr;
+ }
+ else if (m_backupMailDB)
+ {
+ m_backupMailDB->AddListener(this);
+ }
+ }
+ }
+
+ // need to get the mailbox name out of the url and call SetMailboxName with it.
+ // then, we need to open the mail db for this parser.
+ return rv;
+}
+
+// stop binding is a "notification" informing us that the stream associated with aURL is going away.
+NS_IMETHODIMP nsMsgMailboxParser::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult aStatus)
+{
+ DoneParsingFolder(aStatus);
+ // what can we do? we can close the stream?
+ m_urlInProgress = false; // don't close the connection...we may be re-using it.
+
+ if (m_mailDB)
+ m_mailDB->RemoveListener(this);
+ // and we want to mark ourselves for deletion or some how inform our protocol manager that we are
+ // available for another url if there is one....
+
+ ReleaseFolderLock();
+ // be sure to clear any status text and progress info..
+ m_graph_progress_received = 0;
+ UpdateProgressPercent();
+ UpdateStatusText("localStatusDocumentDone");
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsParseMailMessageState::OnHdrPropertyChanged(nsIMsgDBHdr *aHdrToChange,
+ bool aPreChange, uint32_t *aStatus, nsIDBChangeListener * aInstigator)
+{
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsParseMailMessageState::OnHdrFlagsChanged(nsIMsgDBHdr *aHdrChanged,
+ uint32_t aOldFlags, uint32_t aNewFlags, nsIDBChangeListener *aInstigator)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsParseMailMessageState::OnHdrDeleted(nsIMsgDBHdr *aHdrChanged,
+ nsMsgKey aParentKey, int32_t aFlags, nsIDBChangeListener *aInstigator)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsParseMailMessageState::OnHdrAdded(nsIMsgDBHdr *aHdrAdded,
+ nsMsgKey aParentKey, int32_t aFlags, nsIDBChangeListener *aInstigator)
+{
+ return NS_OK;
+}
+
+/* void OnParentChanged (in nsMsgKey aKeyChanged, in nsMsgKey oldParent, in nsMsgKey newParent, in nsIDBChangeListener aInstigator); */
+NS_IMETHODIMP
+nsParseMailMessageState::OnParentChanged(nsMsgKey aKeyChanged,
+ nsMsgKey oldParent, nsMsgKey newParent, nsIDBChangeListener *aInstigator)
+{
+ return NS_OK;
+}
+
+/* void OnAnnouncerGoingAway (in nsIDBChangeAnnouncer instigator); */
+NS_IMETHODIMP
+nsParseMailMessageState::OnAnnouncerGoingAway(nsIDBChangeAnnouncer *instigator)
+{
+ if (m_backupMailDB && m_backupMailDB == instigator)
+ {
+ m_backupMailDB->RemoveListener(this);
+ m_backupMailDB = nullptr;
+ }
+ else if (m_mailDB)
+ {
+ m_mailDB->RemoveListener(this);
+ m_mailDB = nullptr;
+ m_newMsgHdr = nullptr;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::OnEvent(nsIMsgDatabase *aDB, const char *aEvent)
+{
+ return NS_OK;
+}
+
+/* void OnReadChanged (in nsIDBChangeListener instigator); */
+NS_IMETHODIMP
+nsParseMailMessageState::OnReadChanged(nsIDBChangeListener *instigator)
+{
+ return NS_OK;
+}
+
+/* void OnJunkScoreChanged (in nsIDBChangeListener instigator); */
+NS_IMETHODIMP
+nsParseMailMessageState::OnJunkScoreChanged(nsIDBChangeListener *instigator)
+{
+ return NS_OK;
+}
+
+nsMsgMailboxParser::nsMsgMailboxParser() : nsMsgLineBuffer(nullptr, false)
+{
+ Init();
+}
+
+nsMsgMailboxParser::nsMsgMailboxParser(nsIMsgFolder *aFolder) : nsMsgLineBuffer(nullptr, false)
+{
+ m_folder = do_GetWeakReference(aFolder);
+}
+
+nsMsgMailboxParser::~nsMsgMailboxParser()
+{
+ ReleaseFolderLock();
+}
+
+nsresult nsMsgMailboxParser::Init()
+{
+ m_obuffer = nullptr;
+ m_obuffer_size = 0;
+ m_graph_progress_total = 0;
+ m_graph_progress_received = 0;
+ return AcquireFolderLock();
+}
+
+void nsMsgMailboxParser::UpdateStatusText (const char* stringName)
+{
+ if (m_statusFeedback)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ if (!bundleService)
+ return;
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/localMsgs.properties", getter_AddRefs(bundle));
+ if (NS_FAILED(rv))
+ return;
+ nsString finalString;
+ const char16_t * stringArray[] = { m_folderName.get() };
+ rv = bundle->FormatStringFromName(NS_ConvertASCIItoUTF16(stringName).get(),
+ stringArray, 1, getter_Copies(finalString));
+ m_statusFeedback->ShowStatusString(finalString);
+ }
+}
+
+void nsMsgMailboxParser::UpdateProgressPercent ()
+{
+ if (m_statusFeedback && m_graph_progress_total != 0)
+ {
+ // prevent overflow by dividing both by 100
+ int64_t progressTotal = m_graph_progress_total / 100;
+ int64_t progressReceived = m_graph_progress_received / 100;
+ if (progressTotal > 0)
+ m_statusFeedback->ShowProgress((100 *(progressReceived)) / progressTotal);
+ }
+}
+
+nsresult nsMsgMailboxParser::ProcessMailboxInputStream(nsIURI* aURL, nsIInputStream *aIStream, uint32_t aLength)
+{
+ nsresult ret = NS_OK;
+
+ uint32_t bytesRead = 0;
+
+ if (NS_SUCCEEDED(m_inputStream.GrowBuffer(aLength)))
+ {
+ // OK, this sucks, but we're going to have to copy into our
+ // own byte buffer, and then pass that to the line buffering code,
+ // which means a couple buffer copies.
+ ret = aIStream->Read(m_inputStream.GetBuffer(), aLength, &bytesRead);
+ if (NS_SUCCEEDED(ret))
+ ret = BufferInput(m_inputStream.GetBuffer(), bytesRead);
+ }
+ if (m_graph_progress_total > 0)
+ {
+ if (NS_SUCCEEDED(ret))
+ m_graph_progress_received += bytesRead;
+ }
+ return (ret);
+}
+
+void nsMsgMailboxParser::DoneParsingFolder(nsresult status)
+{
+ /* End of file. Flush out any partial line remaining in the buffer. */
+ FlushLastLine();
+ PublishMsgHeader(nullptr);
+
+ // only mark the db valid if we've succeeded.
+ if (NS_SUCCEEDED(status) && m_mailDB) // finished parsing, so flush db folder info
+ UpdateDBFolderInfo();
+ else if (m_mailDB)
+ m_mailDB->SetSummaryValid(false);
+
+ // remove the backup database
+ if (m_backupMailDB)
+ {
+ nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(m_folder);
+ if (folder)
+ folder->RemoveBackupMsgDatabase();
+ m_backupMailDB = nullptr;
+ }
+
+ // if (m_folder != nullptr)
+ // m_folder->SummaryChanged();
+ FreeBuffers();
+}
+
+void nsMsgMailboxParser::FreeBuffers()
+{
+ /* We're done reading the folder - we don't need these things
+ any more. */
+ PR_FREEIF (m_obuffer);
+ m_obuffer_size = 0;
+}
+
+void nsMsgMailboxParser::UpdateDBFolderInfo()
+{
+ UpdateDBFolderInfo(m_mailDB);
+}
+
+// update folder info in db so we know not to reparse.
+void nsMsgMailboxParser::UpdateDBFolderInfo(nsIMsgDatabase *mailDB)
+{
+ mailDB->SetSummaryValid(true);
+}
+
+// Tell the world about the message header (add to db, and view, if any)
+int32_t nsMsgMailboxParser::PublishMsgHeader(nsIMsgWindow *msgWindow)
+{
+ FinishHeader();
+ if (m_newMsgHdr)
+ {
+ char storeToken[100];
+ PR_snprintf(storeToken, sizeof(storeToken), "%lld", m_envelope_pos);
+ m_newMsgHdr->SetStringProperty("storeToken", storeToken);
+
+ uint32_t flags;
+ (void)m_newMsgHdr->GetFlags(&flags);
+ if (flags & nsMsgMessageFlags::Expunged)
+ {
+ nsCOMPtr<nsIDBFolderInfo> folderInfo;
+ m_mailDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
+ uint32_t size;
+ (void)m_newMsgHdr->GetMessageSize(&size);
+ folderInfo->ChangeExpungedBytes(size);
+ m_newMsgHdr = nullptr;
+ }
+ else if (m_mailDB)
+ {
+ // add hdr but don't notify - shouldn't be requiring notifications
+ // during summary file rebuilding
+ m_mailDB->AddNewHdrToDB(m_newMsgHdr, false);
+ m_newMsgHdr = nullptr;
+ }
+ else
+ NS_ASSERTION(false, "no database while parsing local folder"); // should have a DB, no?
+ }
+ else if (m_mailDB)
+ {
+ nsCOMPtr<nsIDBFolderInfo> folderInfo;
+ m_mailDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
+ if (folderInfo)
+ folderInfo->ChangeExpungedBytes(m_position - m_envelope_pos);
+ }
+ return 0;
+}
+
+void nsMsgMailboxParser::AbortNewHeader()
+{
+ if (m_newMsgHdr && m_mailDB)
+ m_newMsgHdr = nullptr;
+}
+
+void nsMsgMailboxParser::OnNewMessage(nsIMsgWindow *msgWindow)
+{
+ PublishMsgHeader(msgWindow);
+ Clear();
+}
+
+nsresult nsMsgMailboxParser::HandleLine(const char *line, uint32_t lineLength)
+{
+ /* If this is the very first line of a non-empty folder, make sure it's an envelope */
+ if (m_graph_progress_received == 0)
+ {
+ /* This is the first block from the file. Check to see if this
+ looks like a mail file. */
+ const char *s = line;
+ const char *end = s + lineLength;
+ while (s < end && IS_SPACE(*s))
+ s++;
+ if ((end - s) < 20 || !IsEnvelopeLine(s, end - s))
+ {
+// char buf[500];
+// PR_snprintf (buf, sizeof(buf),
+// XP_GetString(MK_MSG_NON_MAIL_FILE_READ_QUESTION),
+// folder_name);
+// else if (!FE_Confirm (m_context, buf))
+// return NS_MSG_NOT_A_MAIL_FOLDER; /* #### NOT_A_MAIL_FILE */
+ }
+ }
+// m_graph_progress_received += lineLength;
+
+ // mailbox parser needs to do special stuff when it finds an envelope
+ // after parsing a message body. So do that.
+ if (line[0] == 'F' && IsEnvelopeLine(line, lineLength))
+ {
+ // **** This used to be
+ // PR_ASSERT (m_parseMsgState->m_state == nsMailboxParseBodyState);
+ // **** I am not sure this is a right thing to do. This happens when
+ // going online, downloading a message while playing back append
+ // draft/template offline operation. We are mixing
+ // nsMailboxParseBodyState &&
+ // nsMailboxParseHeadersState. David I need your help here too. **** jt
+
+ NS_ASSERTION (m_state == nsIMsgParseMailMsgState::ParseBodyState ||
+ m_state == nsIMsgParseMailMsgState::ParseHeadersState, "invalid parse state"); /* else folder corrupted */
+ OnNewMessage(nullptr);
+ nsresult rv = StartNewEnvelope(line, lineLength);
+ NS_ASSERTION(NS_SUCCEEDED(rv), " error starting envelope parsing mailbox");
+ // at the start of each new message, update the progress bar
+ UpdateProgressPercent();
+ return rv;
+ }
+
+ // otherwise, the message parser can handle it completely.
+ if (m_mailDB != nullptr) // if no DB, do we need to parse at all?
+ return ParseFolderLine(line, lineLength);
+
+ return NS_ERROR_NULL_POINTER; // need to error out if we don't have a db.
+}
+
+void
+nsMsgMailboxParser::ReleaseFolderLock()
+{
+ nsresult result;
+ nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(m_folder);
+ if (!folder)
+ return;
+ bool haveSemaphore;
+ nsCOMPtr <nsISupports> supports = do_QueryInterface(static_cast<nsIMsgParseMailMsgState*>(this));
+ result = folder->TestSemaphore(supports, &haveSemaphore);
+ if (NS_SUCCEEDED(result) && haveSemaphore)
+ (void) folder->ReleaseSemaphore(supports);
+}
+
+nsresult
+nsMsgMailboxParser::AcquireFolderLock()
+{
+ nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(m_folder);
+ if (!folder)
+ return NS_ERROR_NULL_POINTER;
+ nsCOMPtr<nsISupports> supports = do_QueryObject(this);
+ return folder->AcquireSemaphore(supports);
+}
+
+NS_IMPL_ISUPPORTS(nsParseMailMessageState, nsIMsgParseMailMsgState, nsIDBChangeListener)
+
+nsParseMailMessageState::nsParseMailMessageState()
+{
+ m_position = 0;
+ m_new_key = nsMsgKey_None;
+ m_IgnoreXMozillaStatus = false;
+ m_state = nsIMsgParseMailMsgState::ParseBodyState;
+
+ // setup handling of custom db headers, headers that are added to .msf files
+ // as properties of the nsMsgHdr objects, controlled by the
+ // pref mailnews.customDBHeaders, a space-delimited list of headers.
+ // E.g., if mailnews.customDBHeaders is "X-Spam-Score", and we're parsing
+ // a mail message with the X-Spam-Score header, we'll set the
+ // "x-spam-score" property of nsMsgHdr to the value of the header.
+ m_customDBHeaderValues = nullptr;
+ nsCString customDBHeaders; // not shown in search UI
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (pPrefBranch)
+ {
+ pPrefBranch->GetCharPref("mailnews.customDBHeaders", getter_Copies(customDBHeaders));
+ ToLowerCase(customDBHeaders);
+ if (customDBHeaders.Find("content-base") == -1)
+ customDBHeaders.Insert(NS_LITERAL_CSTRING("content-base "), 0);
+ ParseString(customDBHeaders, ' ', m_customDBHeaders);
+
+ // now add customHeaders
+ nsCString customHeadersString; // shown in search UI
+ nsTArray<nsCString> customHeadersArray;
+ pPrefBranch->GetCharPref("mailnews.customHeaders", getter_Copies(customHeadersString));
+ ToLowerCase(customHeadersString);
+ customHeadersString.StripWhitespace();
+ ParseString(customHeadersString, ':', customHeadersArray);
+ for (uint32_t i = 0; i < customHeadersArray.Length(); i++)
+ {
+ if (!m_customDBHeaders.Contains(customHeadersArray[i]))
+ m_customDBHeaders.AppendElement(customHeadersArray[i]);
+ }
+
+ if (m_customDBHeaders.Length())
+ {
+ m_customDBHeaderValues = new struct message_header [m_customDBHeaders.Length()];
+ if (!m_customDBHeaderValues)
+ m_customDBHeaders.Clear();
+ }
+ }
+ Clear();
+}
+
+nsParseMailMessageState::~nsParseMailMessageState()
+{
+ ClearAggregateHeader (m_toList);
+ ClearAggregateHeader (m_ccList);
+ delete [] m_customDBHeaderValues;
+}
+
+void nsParseMailMessageState::Init(uint64_t fileposition)
+{
+ m_state = nsIMsgParseMailMsgState::ParseBodyState;
+ m_position = fileposition;
+ m_newMsgHdr = nullptr;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::Clear()
+{
+ m_message_id.length = 0;
+ m_references.length = 0;
+ m_date.length = 0;
+ m_delivery_date.length = 0;
+ m_from.length = 0;
+ m_sender.length = 0;
+ m_newsgroups.length = 0;
+ m_subject.length = 0;
+ m_status.length = 0;
+ m_mozstatus.length = 0;
+ m_mozstatus2.length = 0;
+ m_envelope_from.length = 0;
+ m_envelope_date.length = 0;
+ m_priority.length = 0;
+ m_keywords.length = 0;
+ m_mdn_dnt.length = 0;
+ m_return_path.length = 0;
+ m_account_key.length = 0;
+ m_in_reply_to.length = 0;
+ m_replyTo.length = 0;
+ m_content_type.length = 0;
+ m_mdn_original_recipient.length = 0;
+ m_bccList.length = 0;
+ m_body_lines = 0;
+ m_newMsgHdr = nullptr;
+ m_envelope_pos = 0;
+ m_new_key = nsMsgKey_None;
+ ClearAggregateHeader (m_toList);
+ ClearAggregateHeader (m_ccList);
+ m_headers.ResetWritePos();
+ m_envelope.ResetWritePos();
+ m_receivedTime = 0;
+ m_receivedValue.Truncate();
+ for (uint32_t i = 0; i < m_customDBHeaders.Length(); i++)
+ m_customDBHeaderValues[i].length = 0;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::SetState(nsMailboxParseState aState)
+{
+ m_state = aState;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::GetState(nsMailboxParseState *aState)
+{
+ if (!aState)
+ return NS_ERROR_NULL_POINTER;
+
+ *aState = m_state;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsParseMailMessageState::GetEnvelopePos(uint64_t *aEnvelopePos)
+{
+ NS_ENSURE_ARG_POINTER(aEnvelopePos);
+
+ *aEnvelopePos = m_envelope_pos;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::SetEnvelopePos(uint64_t aEnvelopePos)
+{
+ m_envelope_pos = aEnvelopePos;
+ m_position = m_envelope_pos;
+ m_headerstartpos = m_position;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::GetNewMsgHdr(nsIMsgDBHdr ** aMsgHeader)
+{
+ NS_ENSURE_ARG_POINTER(aMsgHeader);
+ NS_IF_ADDREF(*aMsgHeader = m_newMsgHdr);
+ return m_newMsgHdr ? NS_OK : NS_ERROR_NULL_POINTER;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::SetNewMsgHdr(nsIMsgDBHdr *aMsgHeader)
+{
+ m_newMsgHdr = aMsgHeader;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::ParseAFolderLine(const char *line, uint32_t lineLength)
+{
+ ParseFolderLine(line, lineLength);
+ return NS_OK;
+}
+
+nsresult nsParseMailMessageState::ParseFolderLine(const char *line, uint32_t lineLength)
+{
+ nsresult rv;
+
+ if (m_state == nsIMsgParseMailMsgState::ParseHeadersState)
+ {
+ if (EMPTY_MESSAGE_LINE(line))
+ {
+ /* End of headers. Now parse them. */
+ rv = ParseHeaders();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "error parsing headers parsing mailbox");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = FinalizeHeaders();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "error finalizing headers parsing mailbox");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ m_state = nsIMsgParseMailMsgState::ParseBodyState;
+ }
+ else
+ {
+ /* Otherwise, this line belongs to a header. So append it to the
+ header data, and stay in MBOX `MIME_PARSE_HEADERS' state.
+ */
+ m_headers.AppendBuffer(line, lineLength);
+ }
+ }
+ else if ( m_state == nsIMsgParseMailMsgState::ParseBodyState)
+ {
+ m_body_lines++;
+ }
+
+ m_position += lineLength;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::SetMailDB(nsIMsgDatabase *mailDB)
+{
+ m_mailDB = mailDB;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::SetBackupMailDB(nsIMsgDatabase *aBackupMailDB)
+{
+ m_backupMailDB = aBackupMailDB;
+ if (m_backupMailDB)
+ m_backupMailDB->AddListener(this);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::SetNewKey(nsMsgKey aKey)
+{
+ m_new_key = aKey;
+ return NS_OK;
+}
+
+/* #define STRICT_ENVELOPE */
+
+bool
+nsParseMailMessageState::IsEnvelopeLine(const char *buf, int32_t buf_size)
+{
+#ifdef STRICT_ENVELOPE
+ /* The required format is
+ From jwz Fri Jul 1 09:13:09 1994
+ But we should also allow at least:
+ From jwz Fri, Jul 01 09:13:09 1994
+ From jwz Fri Jul 1 09:13:09 1994 PST
+ From jwz Fri Jul 1 09:13:09 1994 (+0700)
+
+ We can't easily call XP_ParseTimeString() because the string is not
+ null terminated (ok, we could copy it after a quick check...) but
+ XP_ParseTimeString() may be too lenient for our purposes.
+
+ DANGER!! The released version of 2.0b1 was (on some systems,
+ some Unix, some NT, possibly others) writing out envelope lines
+ like "From - 10/13/95 11:22:33" which STRICT_ENVELOPE will reject!
+ */
+ const char *date, *end;
+
+ if (buf_size < 29) return false;
+ if (*buf != 'F') return false;
+ if (strncmp(buf, "From ", 5)) return false;
+
+ end = buf + buf_size;
+ date = buf + 5;
+
+ /* Skip horizontal whitespace between "From " and user name. */
+ while ((*date == ' ' || *date == '\t') && date < end)
+ date++;
+
+ /* If at the end, it doesn't match. */
+ if (IS_SPACE(*date) || date == end)
+ return false;
+
+ /* Skip over user name. */
+ while (!IS_SPACE(*date) && date < end)
+ date++;
+
+ /* Skip horizontal whitespace between user name and date. */
+ while ((*date == ' ' || *date == '\t') && date < end)
+ date++;
+
+ /* Don't want this to be localized. */
+# define TMP_ISALPHA(x) (((x) >= 'A' && (x) <= 'Z') || \
+ ((x) >= 'a' && (x) <= 'z'))
+
+ /* take off day-of-the-week. */
+ if (date >= end - 3)
+ return false;
+ if (!TMP_ISALPHA(date[0]) || !TMP_ISALPHA(date[1]) || !TMP_ISALPHA(date[2]))
+ return false;
+ date += 3;
+ /* Skip horizontal whitespace (and commas) between dotw and month. */
+ if (*date != ' ' && *date != '\t' && *date != ',')
+ return false;
+ while ((*date == ' ' || *date == '\t' || *date == ',') && date < end)
+ date++;
+
+ /* take off month. */
+ if (date >= end - 3)
+ return false;
+ if (!TMP_ISALPHA(date[0]) || !TMP_ISALPHA(date[1]) || !TMP_ISALPHA(date[2]))
+ return false;
+ date += 3;
+ /* Skip horizontal whitespace between month and dotm. */
+ if (date == end || (*date != ' ' && *date != '\t'))
+ return false;
+ while ((*date == ' ' || *date == '\t') && date < end)
+ date++;
+
+ /* Skip over digits and whitespace. */
+ while (((*date >= '0' && *date <= '9') || *date == ' ' || *date == '\t') &&
+ date < end)
+ date++;
+ /* Next character should be a colon. */
+ if (date >= end || *date != ':')
+ return false;
+
+ /* Ok, that ought to be enough... */
+
+# undef TMP_ISALPHA
+
+#else /* !STRICT_ENVELOPE */
+
+ if (buf_size < 5) return false;
+ if (*buf != 'F') return false;
+ if (strncmp(buf, "From ", 5)) return false;
+
+#endif /* !STRICT_ENVELOPE */
+
+ return true;
+}
+
+// We've found the start of the next message, so finish this one off.
+NS_IMETHODIMP nsParseMailMessageState::FinishHeader()
+{
+ if (m_newMsgHdr)
+ {
+ m_newMsgHdr->SetMessageOffset(m_envelope_pos);
+ m_newMsgHdr->SetMessageSize(m_position - m_envelope_pos);
+ m_newMsgHdr->SetLineCount(m_body_lines);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsParseMailMessageState::GetAllHeaders(char ** pHeaders, int32_t *pHeadersSize)
+{
+ if (!pHeaders || !pHeadersSize)
+ return NS_ERROR_NULL_POINTER;
+ *pHeaders = m_headers.GetBuffer();
+ *pHeadersSize = m_headers.GetBufferPos();
+ return NS_OK;
+}
+
+// generate headers as a string, with CRLF between the headers
+NS_IMETHODIMP nsParseMailMessageState::GetHeaders(char ** pHeaders)
+{
+ NS_ENSURE_ARG_POINTER(pHeaders);
+ nsCString crlfHeaders;
+ char *curHeader = m_headers.GetBuffer();
+ for (uint32_t headerPos = 0; headerPos < m_headers.GetBufferPos();)
+ {
+ crlfHeaders.Append(curHeader);
+ crlfHeaders.Append(CRLF);
+ int32_t headerLen = strlen(curHeader);
+ curHeader += headerLen + 1;
+ headerPos += headerLen + 1;
+ }
+ *pHeaders = ToNewCString(crlfHeaders);
+ return NS_OK;
+}
+
+struct message_header *nsParseMailMessageState::GetNextHeaderInAggregate (nsTArray<struct message_header*> &list)
+{
+ // When parsing a message with multiple To or CC header lines, we're storing each line in a
+ // list, where the list represents the "aggregate" total of all the header. Here we get a new
+ // line for the list
+
+ struct message_header *header = (struct message_header*) PR_Calloc (1, sizeof(struct message_header));
+ list.AppendElement (header);
+ return header;
+}
+
+void nsParseMailMessageState::GetAggregateHeader (nsTArray<struct message_header*> &list, struct message_header *outHeader)
+{
+ // When parsing a message with multiple To or CC header lines, we're storing each line in a
+ // list, where the list represents the "aggregate" total of all the header. Here we combine
+ // all the lines together, as though they were really all found on the same line
+
+ struct message_header *header = nullptr;
+ int length = 0;
+ size_t i;
+
+ // Count up the bytes required to allocate the aggregated header
+ for (i = 0; i < list.Length(); i++)
+ {
+ header = list.ElementAt(i);
+ length += (header->length + 1); //+ for ","
+ }
+
+ if (length > 0)
+ {
+ char *value = (char*) PR_CALLOC (length + 1); //+1 for null term
+ if (value)
+ {
+ // Catenate all the To lines together, separated by commas
+ value[0] = '\0';
+ size_t size = list.Length();
+ for (i = 0; i < size; i++)
+ {
+ header = list.ElementAt(i);
+ PL_strncat (value, header->value, header->length);
+ if (i + 1 < size)
+ PL_strcat (value, ",");
+ }
+ outHeader->length = length;
+ outHeader->value = value;
+ }
+ }
+ else
+ {
+ outHeader->length = 0;
+ outHeader->value = nullptr;
+ }
+}
+
+void nsParseMailMessageState::ClearAggregateHeader (nsTArray<struct message_header*> &list)
+{
+ // Reset the aggregate headers. Free only the message_header struct since
+ // we don't own the value pointer
+
+ for (size_t i = 0; i < list.Length(); i++)
+ PR_Free (list.ElementAt(i));
+ list.Clear();
+}
+
+// We've found a new envelope to parse.
+nsresult nsParseMailMessageState::StartNewEnvelope(const char *line, uint32_t lineLength)
+{
+ m_envelope_pos = m_position;
+ m_state = nsIMsgParseMailMsgState::ParseHeadersState;
+ m_position += lineLength;
+ m_headerstartpos = m_position;
+ return ParseEnvelope (line, lineLength);
+}
+
+/* largely lifted from mimehtml.c, which does similar parsing, sigh...
+*/
+nsresult nsParseMailMessageState::ParseHeaders ()
+{
+ char *buf = m_headers.GetBuffer();
+ uint32_t buf_length = m_headers.GetBufferPos();
+ if (buf_length == 0)
+ {
+ // No header of an expected type is present. Consider this a successful
+ // parse so email still shows on summary and can be accessed and deleted.
+ return NS_OK;
+ }
+ char *buf_end = buf + buf_length;
+ if (!(buf_length > 1 && (buf[buf_length - 1] == '\r' ||
+ buf[buf_length - 1] == '\n')))
+ {
+ NS_WARNING("Header text should always end in a newline");
+ return NS_ERROR_UNEXPECTED;
+ }
+ while (buf < buf_end)
+ {
+ char *colon = PL_strnchr(buf, ':', buf_end - buf);
+ char *end;
+ char *value = 0;
+ struct message_header *header = 0;
+ struct message_header receivedBy;
+
+ if (!colon)
+ break;
+
+ end = colon;
+
+ switch (buf [0])
+ {
+ case 'B': case 'b':
+ if (!PL_strncasecmp ("BCC", buf, end - buf))
+ header = &m_bccList;
+ break;
+ case 'C': case 'c':
+ if (!PL_strncasecmp ("CC", buf, end - buf))
+ header = GetNextHeaderInAggregate(m_ccList);
+ else if (!PL_strncasecmp ("Content-Type", buf, end - buf))
+ header = &m_content_type;
+ break;
+ case 'D': case 'd':
+ if (!PL_strncasecmp ("Date", buf, end - buf))
+ header = &m_date;
+ else if (!PL_strncasecmp("Disposition-Notification-To", buf, end - buf))
+ header = &m_mdn_dnt;
+ else if (!PL_strncasecmp("Delivery-date", buf, end - buf))
+ header = &m_delivery_date;
+ break;
+ case 'F': case 'f':
+ if (!PL_strncasecmp ("From", buf, end - buf))
+ header = &m_from;
+ break;
+ case 'I' : case 'i':
+ if (!PL_strncasecmp ("In-Reply-To", buf, end - buf))
+ header = &m_in_reply_to;
+ break;
+ case 'M': case 'm':
+ if (!PL_strncasecmp ("Message-ID", buf, end - buf))
+ header = &m_message_id;
+ break;
+ case 'N': case 'n':
+ if (!PL_strncasecmp ("Newsgroups", buf, end - buf))
+ header = &m_newsgroups;
+ break;
+ case 'O': case 'o':
+ if (!PL_strncasecmp ("Original-Recipient", buf, end - buf))
+ header = &m_mdn_original_recipient;
+ break;
+ case 'R': case 'r':
+ if (!PL_strncasecmp ("References", buf, end - buf))
+ header = &m_references;
+ else if (!PL_strncasecmp ("Return-Path", buf, end - buf))
+ header = &m_return_path;
+ // treat conventional Return-Receipt-To as MDN
+ // Disposition-Notification-To
+ else if (!PL_strncasecmp ("Return-Receipt-To", buf, end - buf))
+ header = &m_mdn_dnt;
+ else if (!PL_strncasecmp("Reply-To", buf, end - buf))
+ header = &m_replyTo;
+ else if (!PL_strncasecmp("Received", buf, end - buf))
+ {
+ header = &receivedBy;
+ header->length = 0;
+ }
+ break;
+ case 'S': case 's':
+ if (!PL_strncasecmp ("Subject", buf, end - buf) && !m_subject.length)
+ header = &m_subject;
+ else if (!PL_strncasecmp ("Sender", buf, end - buf))
+ header = &m_sender;
+ else if (!PL_strncasecmp ("Status", buf, end - buf))
+ header = &m_status;
+ break;
+ case 'T': case 't':
+ if (!PL_strncasecmp ("To", buf, end - buf))
+ header = GetNextHeaderInAggregate(m_toList);
+ break;
+ case 'X':
+ if (X_MOZILLA_STATUS2_LEN == end - buf &&
+ !PL_strncasecmp(X_MOZILLA_STATUS2, buf, end - buf) &&
+ !m_IgnoreXMozillaStatus && !m_mozstatus2.length)
+ header = &m_mozstatus2;
+ else if ( X_MOZILLA_STATUS_LEN == end - buf &&
+ !PL_strncasecmp(X_MOZILLA_STATUS, buf, end - buf) && !m_IgnoreXMozillaStatus
+ && !m_mozstatus.length)
+ header = &m_mozstatus;
+ else if (!PL_strncasecmp(HEADER_X_MOZILLA_ACCOUNT_KEY, buf, end - buf)
+ && !m_account_key.length)
+ header = &m_account_key;
+ // we could very well care what the priority header was when we
+ // remember its value. If so, need to remember it here. Also,
+ // different priority headers can appear in the same message,
+ // but we only rememeber the last one that we see.
+ else if (!PL_strncasecmp("X-Priority", buf, end - buf)
+ || !PL_strncasecmp("Priority", buf, end - buf))
+ header = &m_priority;
+ else if (!PL_strncasecmp(HEADER_X_MOZILLA_KEYWORDS, buf, end - buf)
+ && !m_keywords.length)
+ header = &m_keywords;
+ break;
+ }
+ if (!header && m_customDBHeaders.Length())
+ {
+#ifdef MOZILLA_INTERNAL_API
+ nsDependentCSubstring headerStr(buf, end);
+#else
+ nsDependentCSubstring headerStr(buf, end - buf);
+#endif
+
+ ToLowerCase(headerStr);
+ size_t customHeaderIndex = m_customDBHeaders.IndexOf(headerStr);
+ if (customHeaderIndex != m_customDBHeaders.NoIndex)
+ header = & m_customDBHeaderValues[customHeaderIndex];
+ }
+
+ buf = colon + 1;
+ uint32_t writeOffset = 0; // number of characters replaced with a folded space
+
+SEARCH_NEWLINE:
+ // move past any non terminating characters, rewriting them if folding white space
+ // exists
+ while (buf < buf_end && *buf != '\r' && *buf != '\n')
+ {
+ if (writeOffset)
+ *(buf - writeOffset) = *buf;
+ buf++;
+ }
+
+ /* If "\r\n " or "\r\n\t" is next, that doesn't terminate the header. */
+ if ((buf + 2 < buf_end && (buf[0] == '\r' && buf[1] == '\n') &&
+ (buf[2] == ' ' || buf[2] == '\t')) ||
+ /* If "\r " or "\r\t" or "\n " or "\n\t" is next, that doesn't terminate
+ the header either. */
+ (buf + 1 < buf_end && (buf[0] == '\r' || buf[0] == '\n') &&
+ (buf[1] == ' ' || buf[1] == '\t')))
+ {
+ // locate the proper location for a folded space by eliminating any
+ // leading spaces before the end-of-line character
+ char* foldedSpace = buf;
+ while (*(foldedSpace - 1) == ' ' || *(foldedSpace - 1) == '\t')
+ foldedSpace--;
+
+ // put a single folded space character
+ *(foldedSpace - writeOffset) = ' ';
+ writeOffset += (buf - foldedSpace);
+ buf++;
+
+ // eliminate any additional white space
+ while (buf < buf_end &&
+ (*buf == '\n' || *buf == '\r' || *buf == ' ' || *buf == '\t'))
+ {
+ buf++;
+ writeOffset++;
+ }
+
+ // If we get here, the message headers ended in an empty line, like:
+ // To: blah blah blah<CR><LF> <CR><LF>[end of buffer]. The code below
+ // requires buf to land on a newline to properly null-terminate the
+ // string, so back up a tad so that it is pointing to one.
+ if (buf == buf_end)
+ {
+ --buf;
+ MOZ_ASSERT(*buf == '\n' || *buf == '\r',
+ "Header text should always end in a newline.");
+ }
+ goto SEARCH_NEWLINE;
+ }
+
+ if (header)
+ {
+ value = colon + 1;
+ // eliminate trailing blanks after the colon
+ while (value < (buf - writeOffset) && (*value == ' ' || *value == '\t'))
+ value++;
+
+ header->value = value;
+ header->length = buf - header->value - writeOffset;
+ if (header->length < 0)
+ header->length = 0;
+ }
+ if (*buf == '\r' || *buf == '\n')
+ {
+ char *last = buf - writeOffset;
+ char *saveBuf = buf;
+ if (*buf == '\r' && buf + 1 < buf_end && buf[1] == '\n')
+ buf++;
+ buf++;
+ // null terminate the left-over slop so we don't confuse msg filters.
+ *saveBuf = 0;
+ *last = 0; /* short-circuit const, and null-terminate header. */
+ }
+
+ if (header)
+ {
+ /* More const short-circuitry... */
+ /* strip trailing whitespace */
+ while (header->length > 0 &&
+ IS_SPACE (header->value [header->length - 1]))
+ ((char *) header->value) [--header->length] = 0;
+ if (header == &receivedBy)
+ {
+ if (m_receivedTime == 0)
+ {
+ // parse Received: header for date.
+ // We trust the first header as that is closest to recipient,
+ // and less likely to be spoofed.
+ nsAutoCString receivedHdr(header->value, header->length);
+ int32_t lastSemicolon = receivedHdr.RFindChar(';');
+ if (lastSemicolon != -1)
+ {
+ nsAutoCString receivedDate;
+ receivedDate = Substring(receivedHdr, lastSemicolon + 1);
+ receivedDate.Trim(" \t\b\r\n");
+ PRTime resultTime;
+ if (PR_ParseTimeString (receivedDate.get(), false, &resultTime) == PR_SUCCESS)
+ m_receivedTime = resultTime;
+ else
+ NS_WARNING("PR_ParseTimeString failed in ParseHeaders().");
+ }
+ }
+ // Someone might want the received header saved.
+ if (m_customDBHeaders.Length())
+ {
+ if (m_customDBHeaders.Contains(NS_LITERAL_CSTRING("received")))
+ {
+ if (!m_receivedValue.IsEmpty())
+ m_receivedValue.Append(' ');
+ m_receivedValue.Append(header->value, header->length);
+ }
+ }
+ }
+
+ MOZ_ASSERT(header->value[header->length] == 0,
+ "Non-null-terminated strings cause very, very bad problems");
+ }
+ }
+ return NS_OK;
+}
+
+nsresult nsParseMailMessageState::ParseEnvelope (const char *line, uint32_t line_size)
+{
+ const char *end;
+ char *s;
+
+ m_envelope.AppendBuffer(line, line_size);
+ end = m_envelope.GetBuffer() + line_size;
+ s = m_envelope.GetBuffer() + 5;
+
+ while (s < end && IS_SPACE (*s))
+ s++;
+ m_envelope_from.value = s;
+ while (s < end && !IS_SPACE (*s))
+ s++;
+ m_envelope_from.length = s - m_envelope_from.value;
+
+ while (s < end && IS_SPACE (*s))
+ s++;
+ m_envelope_date.value = s;
+ m_envelope_date.length = (uint16_t) (line_size - (s - m_envelope.GetBuffer()));
+
+ while (m_envelope_date.length > 0 &&
+ IS_SPACE (m_envelope_date.value [m_envelope_date.length - 1]))
+ m_envelope_date.length--;
+
+ /* #### short-circuit const */
+ ((char *) m_envelope_from.value) [m_envelope_from.length] = 0;
+ ((char *) m_envelope_date.value) [m_envelope_date.length] = 0;
+
+ return NS_OK;
+}
+
+nsresult nsParseMailMessageState::InternSubject (struct message_header *header)
+{
+ if (!header || header->length == 0)
+ {
+ m_newMsgHdr->SetSubject("");
+ return NS_OK;
+ }
+
+ const char *key = header->value;
+
+ uint32_t flags;
+ (void)m_newMsgHdr->GetFlags(&flags);
+ /* strip "Re: " */
+ /**
+ We trust the X-Mozilla-Status line to be the smartest in almost
+ all things. One exception, however, is the HAS_RE flag. Since
+ we just parsed the subject header anyway, we expect that parsing
+ to be smartest. (After all, what if someone just went in and
+ edited the subject line by hand?)
+ */
+ nsCString modifiedSubject;
+ if (NS_MsgStripRE(nsDependentCString(key), modifiedSubject))
+ flags |= nsMsgMessageFlags::HasRe;
+ else
+ flags &= ~nsMsgMessageFlags::HasRe;
+ m_newMsgHdr->SetFlags(flags); // this *does not* update the mozilla-status header in the local folder
+
+ // Condense the subject text into as few MIME-2 encoded words as possible.
+ m_newMsgHdr->SetSubject(modifiedSubject.IsEmpty() ? key : modifiedSubject.get());
+
+ return NS_OK;
+}
+
+// we've reached the end of the envelope, and need to turn all our accumulated message_headers
+// into a single nsIMsgDBHdr to store in a database.
+nsresult nsParseMailMessageState::FinalizeHeaders()
+{
+ nsresult rv;
+ struct message_header *sender;
+ struct message_header *recipient;
+ struct message_header *subject;
+ struct message_header *id;
+ struct message_header *inReplyTo;
+ struct message_header *replyTo;
+ struct message_header *references;
+ struct message_header *date;
+ struct message_header *deliveryDate;
+ struct message_header *statush;
+ struct message_header *mozstatus;
+ struct message_header *mozstatus2;
+ struct message_header *priority;
+ struct message_header *keywords;
+ struct message_header *account_key;
+ struct message_header *ccList;
+ struct message_header *bccList;
+ struct message_header *mdn_dnt;
+ struct message_header md5_header;
+ struct message_header *content_type;
+ char md5_data [50];
+
+ uint32_t flags = 0;
+ uint32_t delta = 0;
+ nsMsgPriorityValue priorityFlags = nsMsgPriority::notSet;
+ uint32_t labelFlags = 0;
+
+ if (!m_mailDB) // if we don't have a valid db, skip the header.
+ return NS_OK;
+
+ struct message_header to;
+ GetAggregateHeader (m_toList, &to);
+ struct message_header cc;
+ GetAggregateHeader (m_ccList, &cc);
+ // we don't aggregate bcc, as we only generate it locally,
+ // and we don't use multiple lines
+
+ sender = (m_from.length ? &m_from :
+ m_sender.length ? &m_sender :
+ m_envelope_from.length ? &m_envelope_from :
+ 0);
+ recipient = (to.length ? &to :
+ cc.length ? &cc :
+ m_newsgroups.length ? &m_newsgroups :
+ 0);
+ ccList = (cc.length ? &cc : 0);
+ bccList = (m_bccList.length ? &m_bccList : 0);
+ subject = (m_subject.length ? &m_subject : 0);
+ id = (m_message_id.length ? &m_message_id : 0);
+ references = (m_references.length ? &m_references : 0);
+ statush = (m_status.length ? &m_status : 0);
+ mozstatus = (m_mozstatus.length ? &m_mozstatus : 0);
+ mozstatus2 = (m_mozstatus2.length ? &m_mozstatus2 : 0);
+ date = (m_date.length ? &m_date :
+ m_envelope_date.length ? &m_envelope_date :
+ 0);
+ deliveryDate = (m_delivery_date.length ? &m_delivery_date : 0);
+ priority = (m_priority.length ? &m_priority : 0);
+ keywords = (m_keywords.length ? &m_keywords : 0);
+ mdn_dnt = (m_mdn_dnt.length ? &m_mdn_dnt : 0);
+ inReplyTo = (m_in_reply_to.length ? &m_in_reply_to : 0);
+ replyTo = (m_replyTo.length ? &m_replyTo : 0);
+ content_type = (m_content_type.length ? &m_content_type : 0);
+ account_key = (m_account_key.length ? &m_account_key :0);
+
+ if (mozstatus)
+ {
+ if (mozstatus->length == 4)
+ {
+ NS_ASSERTION(MsgIsHex(mozstatus->value, 4), "Expected 4 hex digits for flags.");
+ flags = MsgUnhex(mozstatus->value, 4);
+ // strip off and remember priority bits.
+ flags &= ~nsMsgMessageFlags::RuntimeOnly;
+ priorityFlags = (nsMsgPriorityValue) ((flags & nsMsgMessageFlags::Priorities) >> 13);
+ flags &= ~nsMsgMessageFlags::Priorities;
+ }
+ delta = (m_headerstartpos +
+ (mozstatus->value - m_headers.GetBuffer()) -
+ (2 + X_MOZILLA_STATUS_LEN) /* 2 extra bytes for ": ". */
+ ) - m_envelope_pos;
+ }
+
+ if (mozstatus2)
+ {
+ uint32_t flags2 = 0;
+ sscanf(mozstatus2->value, " %x ", &flags2);
+ flags |= flags2;
+ }
+
+ if (!(flags & nsMsgMessageFlags::Expunged)) // message was deleted, don't bother creating a hdr.
+ {
+ // We'll need the message id first to recover data from the backup database
+ nsAutoCString rawMsgId;
+ /* Take off <> around message ID. */
+ if (id)
+ {
+ if (id->length > 0 && id->value[0] == '<')
+ id->length--, id->value++;
+
+ NS_WARNING_ASSERTION(id->length > 0, "id->length failure in FinalizeHeaders().");
+
+ if (id->length > 0 && id->value[id->length - 1] == '>')
+ /* generate a new null-terminated string without the final > */
+ rawMsgId.Assign(id->value, id->length - 1);
+ else
+ rawMsgId.Assign(id->value);
+ }
+
+ /*
+ * Try to copy the data from the backup database, referencing the MessageID
+ * If that fails, just create a new header
+ */
+ nsCOMPtr<nsIMsgDBHdr> oldHeader;
+ nsresult ret = NS_OK;
+
+ if (m_backupMailDB && !rawMsgId.IsEmpty())
+ ret = m_backupMailDB->GetMsgHdrForMessageID(
+ rawMsgId.get(), getter_AddRefs(oldHeader));
+
+ // m_new_key is set in nsImapMailFolder::ParseAdoptedHeaderLine to be
+ // the UID of the message, so that the key can get created as UID. That of
+ // course is extremely confusing, and we really need to clean that up. We
+ // really should not conflate the meaning of envelope position, key, and
+ // UID.
+ if (NS_SUCCEEDED(ret) && oldHeader)
+ ret = m_mailDB->CopyHdrFromExistingHdr(m_new_key,
+ oldHeader, false, getter_AddRefs(m_newMsgHdr));
+ else if (!m_newMsgHdr)
+ {
+ // Should assert that this is not a local message
+ ret = m_mailDB->CreateNewHdr(m_new_key, getter_AddRefs(m_newMsgHdr));
+ }
+
+ if (NS_SUCCEEDED(ret) && m_newMsgHdr)
+ {
+ uint32_t origFlags;
+ (void)m_newMsgHdr->GetFlags(&origFlags);
+ if (origFlags & nsMsgMessageFlags::HasRe)
+ flags |= nsMsgMessageFlags::HasRe;
+ else
+ flags &= ~nsMsgMessageFlags::HasRe;
+
+ flags &= ~nsMsgMessageFlags::Offline; // don't keep nsMsgMessageFlags::Offline for local msgs
+ if (mdn_dnt && !(origFlags & nsMsgMessageFlags::Read) &&
+ !(origFlags & nsMsgMessageFlags::MDNReportSent) &&
+ !(flags & nsMsgMessageFlags::MDNReportSent))
+ flags |= nsMsgMessageFlags::MDNReportNeeded;
+
+ m_newMsgHdr->SetFlags(flags);
+ if (priorityFlags != nsMsgPriority::notSet)
+ m_newMsgHdr->SetPriority(priorityFlags);
+
+ // if we have a reply to header, and it's different from the from: header,
+ // set the "replyTo" attribute on the msg hdr.
+ if (replyTo && (!sender || replyTo->length != sender->length || strncmp(replyTo->value, sender->value, sender->length)))
+ m_newMsgHdr->SetStringProperty("replyTo", replyTo->value);
+ // convert the flag values (0xE000000) to label values (0-5)
+ if (mozstatus2) // only do this if we have a mozstatus2 header
+ {
+ labelFlags = ((flags & nsMsgMessageFlags::Labels) >> 25);
+ m_newMsgHdr->SetLabel(labelFlags);
+ }
+ if (delta < 0xffff)
+ { /* Only use if fits in 16 bits. */
+ m_newMsgHdr->SetStatusOffset((uint16_t) delta);
+ if (!m_IgnoreXMozillaStatus) { // imap doesn't care about X-MozillaStatus
+ uint32_t offset;
+ (void)m_newMsgHdr->GetStatusOffset(&offset);
+ NS_ASSERTION(offset < 10000, "invalid status offset"); /* ### Debugging hack */
+ }
+ }
+ if (sender)
+ m_newMsgHdr->SetAuthor(sender->value);
+ if (recipient == &m_newsgroups)
+ {
+ /* In the case where the recipient is a newsgroup, truncate the string
+ at the first comma. This is used only for presenting the thread list,
+ and newsgroup lines tend to be long and non-shared, and tend to bloat
+ the string table. So, by only showing the first newsgroup, we can
+ reduce memory and file usage at the expense of only showing the one
+ group in the summary list, and only being able to sort on the first
+ group rather than the whole list. It's worth it. */
+ char * ch;
+ ch = PL_strchr(recipient->value, ',');
+ if (ch)
+ {
+ /* generate a new string that terminates before the , */
+ nsAutoCString firstGroup;
+ firstGroup.Assign(recipient->value, ch - recipient->value);
+ m_newMsgHdr->SetRecipients(firstGroup.get());
+ }
+ m_newMsgHdr->SetRecipients(recipient->value);
+ }
+ else if (recipient)
+ {
+ m_newMsgHdr->SetRecipients(recipient->value);
+ }
+ if (ccList)
+ {
+ m_newMsgHdr->SetCcList(ccList->value);
+ }
+
+ if (bccList)
+ {
+ m_newMsgHdr->SetBccList(bccList->value);
+ }
+
+ rv = InternSubject (subject);
+ if (NS_SUCCEEDED(rv))
+ {
+ if (!id)
+ {
+ // what to do about this? we used to do a hash of all the headers...
+ nsAutoCString hash;
+ const char *md5_b64 = "dummy.message.id";
+ nsresult rv;
+ nsCOMPtr<nsICryptoHash> hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ if (NS_SUCCEEDED(hasher->Init(nsICryptoHash::MD5)) &&
+ NS_SUCCEEDED(hasher->Update((const uint8_t*) m_headers.GetBuffer(), m_headers.GetSize())) &&
+ NS_SUCCEEDED(hasher->Finish(true, hash)))
+ md5_b64 = hash.get();
+ }
+ PR_snprintf (md5_data, sizeof(md5_data), "<md5:%s>", md5_b64);
+ md5_header.value = md5_data;
+ md5_header.length = strlen(md5_data);
+ id = &md5_header;
+ }
+
+ if (!rawMsgId.IsEmpty())
+ m_newMsgHdr->SetMessageId(rawMsgId.get());
+ else
+ m_newMsgHdr->SetMessageId(id->value);
+ m_mailDB->UpdatePendingAttributes(m_newMsgHdr);
+
+ if (!mozstatus && statush)
+ {
+ /* Parse a little bit of the Berkeley Mail status header. */
+ for (const char *s = statush->value; *s; s++) {
+ uint32_t msgFlags = 0;
+ (void)m_newMsgHdr->GetFlags(&msgFlags);
+ switch (*s)
+ {
+ case 'R': case 'r':
+ m_newMsgHdr->SetFlags(msgFlags | nsMsgMessageFlags::Read);
+ break;
+ case 'D': case 'd':
+ /* msg->flags |= nsMsgMessageFlags::Expunged; ### Is this reasonable? */
+ break;
+ case 'N': case 'n':
+ case 'U': case 'u':
+ m_newMsgHdr->SetFlags(msgFlags & ~nsMsgMessageFlags::Read);
+ break;
+ default: // Should check for corrupt file.
+ NS_ERROR("Corrupt file. Should not happen.");
+ break;
+ }
+ }
+ }
+
+ if (account_key != nullptr)
+ m_newMsgHdr->SetAccountKey(account_key->value);
+ // use in-reply-to header as references, if there's no references header
+ if (references != nullptr)
+ m_newMsgHdr->SetReferences(references->value);
+ else if (inReplyTo != nullptr)
+ m_newMsgHdr->SetReferences(inReplyTo->value);
+
+ // 'Received' should be as reliable an indicator of the receipt
+ // date+time as possible, whilst always giving something *from
+ // the message*. It won't use PR_Now() under any circumstance.
+ // Therefore, the fall-thru order for 'Received' is:
+ // Received: -> Delivery-date: -> date
+ // 'Date' uses:
+ // date -> PR_Now()
+ //
+ // date is:
+ // Date: -> m_envelope_date
+
+ uint32_t rcvTimeSecs = 0;
+ if (date)
+ { // Date:
+ PRTime resultTime;
+ PRStatus timeStatus = PR_ParseTimeString (date->value, false, &resultTime);
+ if (PR_SUCCESS == timeStatus)
+ {
+ m_newMsgHdr->SetDate(resultTime);
+ PRTime2Seconds(resultTime, &rcvTimeSecs);
+ }
+ else
+ NS_WARNING("PR_ParseTimeString of date failed in FinalizeHeader().");
+ }
+ else
+ { // PR_Now()
+ // If there was some problem parsing the Date header *AND* we
+ // couldn't get a valid envelope date, use now as the time.
+ // PR_ParseTimeString won't touch resultTime unless it succeeds.
+ // This doesn't affect local (POP3) messages, because we use the envelope
+ // date if there's no Date: header, but it will affect IMAP msgs
+ // w/o a Date: hdr or Received: headers.
+ PRTime resultTime = PR_Now();
+ m_newMsgHdr->SetDate(resultTime);
+ }
+ if (m_receivedTime != 0)
+ { // Upgrade 'Received' to Received: ?
+ PRTime2Seconds(m_receivedTime, &rcvTimeSecs);
+ }
+ else if (deliveryDate)
+ { // Upgrade 'Received' to Delivery-date: ?
+ PRTime resultTime;
+ PRStatus timeStatus = PR_ParseTimeString (deliveryDate->value, false, &resultTime);
+ if (PR_SUCCESS == timeStatus)
+ PRTime2Seconds(resultTime, &rcvTimeSecs);
+ else // TODO/FIXME: We need to figure out what to do in this case!
+ NS_WARNING("PR_ParseTimeString of delivery date failed in FinalizeHeader().");
+ }
+ m_newMsgHdr->SetUint32Property("dateReceived", rcvTimeSecs);
+
+ if (priority)
+ m_newMsgHdr->SetPriorityString(priority->value);
+ else if (priorityFlags == nsMsgPriority::notSet)
+ m_newMsgHdr->SetPriority(nsMsgPriority::none);
+ if (keywords)
+ {
+ // When there are many keywords, some may not have been written
+ // to the message file, so add extra keywords from the backup
+ nsAutoCString oldKeywords;
+ m_newMsgHdr->GetStringProperty("keywords", getter_Copies(oldKeywords));
+ nsTArray<nsCString> newKeywordArray, oldKeywordArray;
+ ParseString(Substring(keywords->value, keywords->value + keywords->length), ' ', newKeywordArray);
+ ParseString(oldKeywords, ' ', oldKeywordArray);
+ for (uint32_t i = 0; i < oldKeywordArray.Length(); i++)
+ if (!newKeywordArray.Contains(oldKeywordArray[i]))
+ newKeywordArray.AppendElement(oldKeywordArray[i]);
+ nsAutoCString newKeywords;
+ for (uint32_t i = 0; i < newKeywordArray.Length(); i++)
+ {
+ if (i)
+ newKeywords.Append(" ");
+ newKeywords.Append(newKeywordArray[i]);
+ }
+ m_newMsgHdr->SetStringProperty("keywords", newKeywords.get());
+ }
+ for (uint32_t i = 0; i < m_customDBHeaders.Length(); i++)
+ {
+ if (m_customDBHeaderValues[i].length)
+ m_newMsgHdr->SetStringProperty(m_customDBHeaders[i].get(), m_customDBHeaderValues[i].value);
+ // The received header is accumulated separately
+ if (m_customDBHeaders[i].EqualsLiteral("received") && !m_receivedValue.IsEmpty())
+ m_newMsgHdr->SetStringProperty("received", m_receivedValue.get());
+ }
+ if (content_type)
+ {
+ char *substring = PL_strstr(content_type->value, "charset");
+ if (substring)
+ {
+ char *charset = PL_strchr (substring, '=');
+ if (charset)
+ {
+ charset++;
+ /* strip leading whitespace and double-quote */
+ while (*charset && (IS_SPACE (*charset) || '\"' == *charset))
+ charset++;
+ /* strip trailing whitespace and double-quote */
+ char *end = charset;
+ while (*end && !IS_SPACE (*end) && '\"' != *end && ';' != *end)
+ end++;
+ if (*charset)
+ {
+ if (*end != '\0') {
+ // if we're not at the very end of the line, we need
+ // to generate a new string without the trailing crud
+ nsAutoCString rawCharSet;
+ rawCharSet.Assign(charset, end - charset);
+ m_newMsgHdr->SetCharset(rawCharSet.get());
+ } else {
+ m_newMsgHdr->SetCharset(charset);
+ }
+ }
+ }
+ }
+ substring = PL_strcasestr(content_type->value, "multipart/mixed");
+ if (substring)
+ {
+ uint32_t newFlags;
+ m_newMsgHdr->OrFlags(nsMsgMessageFlags::Attachment, &newFlags);
+ }
+ }
+ }
+ }
+ else
+ {
+ NS_ASSERTION(false, "error creating message header");
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ else
+ rv = NS_OK;
+
+ //### why is this stuff const?
+ char *tmp = (char*) to.value;
+ PR_Free(tmp);
+ tmp = (char*) cc.value;
+ PR_Free(tmp);
+
+ return rv;
+}
+
+nsParseNewMailState::nsParseNewMailState()
+ : m_disableFilters(false)
+{
+ m_ibuffer = nullptr;
+ m_ibuffer_size = 0;
+ m_ibuffer_fp = 0;
+ m_numNotNewMessages = 0;
+ }
+
+NS_IMPL_ISUPPORTS_INHERITED(nsParseNewMailState, nsMsgMailboxParser, nsIMsgFilterHitNotify)
+
+nsresult
+nsParseNewMailState::Init(nsIMsgFolder *serverFolder, nsIMsgFolder *downloadFolder,
+ nsIMsgWindow *aMsgWindow, nsIMsgDBHdr *aHdr,
+ nsIOutputStream *aOutputStream)
+{
+ nsresult rv;
+ Clear();
+ m_rootFolder = serverFolder;
+ m_msgWindow = aMsgWindow;
+ m_downloadFolder = downloadFolder;
+
+ m_newMsgHdr = aHdr;
+ m_outputStream = aOutputStream;
+ // the new mail parser isn't going to get the stream input, it seems, so we can't use
+ // the OnStartRequest mechanism the mailbox parser uses. So, let's open the db right now.
+ nsCOMPtr<nsIMsgDBService> msgDBService = do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+ if (msgDBService && !m_mailDB)
+ rv = msgDBService->OpenFolderDB(downloadFolder, false,
+ getter_AddRefs(m_mailDB));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIMsgFolder> rootMsgFolder = do_QueryInterface(serverFolder, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = rootMsgFolder->GetServer(getter_AddRefs(server));
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = server->GetFilterList(aMsgWindow, getter_AddRefs(m_filterList));
+
+ if (m_filterList)
+ rv = server->ConfigureTemporaryFilters(m_filterList);
+ // check if this server defers to another server, in which case
+ // we'll use that server's filters as well.
+ nsCOMPtr <nsIMsgFolder> deferredToRootFolder;
+ server->GetRootMsgFolder(getter_AddRefs(deferredToRootFolder));
+ if (rootMsgFolder != deferredToRootFolder)
+ {
+ nsCOMPtr <nsIMsgIncomingServer> deferredToServer;
+ deferredToRootFolder->GetServer(getter_AddRefs(deferredToServer));
+ if (deferredToServer)
+ deferredToServer->GetFilterList(aMsgWindow, getter_AddRefs(m_deferredToServerFilterList));
+ }
+ }
+ m_disableFilters = false;
+ return NS_OK;
+}
+
+nsParseNewMailState::~nsParseNewMailState()
+{
+ if (m_mailDB)
+ m_mailDB->Close(true);
+ if (m_backupMailDB)
+ m_backupMailDB->ForceClosed();
+#ifdef DOING_JSFILTERS
+ JSFilter_cleanup();
+#endif
+}
+
+// not an IMETHOD so we don't need to do error checking or return an error.
+// We only have one caller.
+void nsParseNewMailState::GetMsgWindow(nsIMsgWindow **aMsgWindow)
+{
+ NS_IF_ADDREF(*aMsgWindow = m_msgWindow);
+}
+
+
+// This gets called for every message because libnet calls IncorporateBegin,
+// IncorporateWrite (once or more), and IncorporateComplete for every message.
+void nsParseNewMailState::DoneParsingFolder(nsresult status)
+{
+ /* End of file. Flush out any partial line remaining in the buffer. */
+ if (m_ibuffer_fp > 0)
+ {
+ ParseFolderLine(m_ibuffer, m_ibuffer_fp);
+ m_ibuffer_fp = 0;
+ }
+ PublishMsgHeader(nullptr);
+ if (m_mailDB) // finished parsing, so flush db folder info
+ UpdateDBFolderInfo();
+
+ /* We're done reading the folder - we don't need these things
+ any more. */
+ PR_FREEIF (m_ibuffer);
+ m_ibuffer_size = 0;
+ PR_FREEIF (m_obuffer);
+ m_obuffer_size = 0;
+}
+
+void nsParseNewMailState::OnNewMessage(nsIMsgWindow *msgWindow)
+{
+}
+
+int32_t nsParseNewMailState::PublishMsgHeader(nsIMsgWindow *msgWindow)
+{
+ bool moved = false;
+ FinishHeader();
+
+ if (m_newMsgHdr)
+ {
+ uint32_t newFlags, oldFlags;
+ m_newMsgHdr->GetFlags(&oldFlags);
+ if (!(oldFlags & nsMsgMessageFlags::Read)) // don't mark read messages as new.
+ m_newMsgHdr->OrFlags(nsMsgMessageFlags::New, &newFlags);
+
+ if (!m_disableFilters)
+ {
+ uint64_t msgOffset;
+ (void) m_newMsgHdr->GetMessageOffset(&msgOffset);
+ m_curHdrOffset = msgOffset;
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ nsresult rv = m_rootFolder->GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, 0);
+ int32_t duplicateAction;
+ server->GetIncomingDuplicateAction(&duplicateAction);
+ if (duplicateAction != nsIMsgIncomingServer::keepDups)
+ {
+ bool isDup;
+ server->IsNewHdrDuplicate(m_newMsgHdr, &isDup);
+ if (isDup)
+ {
+ // we want to do something similar to applying filter hits.
+ // if a dup is marked read, it shouldn't trigger biff.
+ // Same for deleting it or moving it to trash.
+ switch (duplicateAction)
+ {
+ case nsIMsgIncomingServer::deleteDups:
+ {
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ nsresult rv =
+ m_downloadFolder->GetMsgStore(getter_AddRefs(msgStore));
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = msgStore->DiscardNewMessage(m_outputStream, m_newMsgHdr);
+ if (NS_FAILED(rv))
+ m_rootFolder->ThrowAlertMsg("dupDeleteFolderTruncateFailed", msgWindow);
+ }
+ m_mailDB->RemoveHeaderMdbRow(m_newMsgHdr);
+ }
+ break;
+
+ case nsIMsgIncomingServer::moveDupsToTrash:
+ {
+ nsCOMPtr <nsIMsgFolder> trash;
+ GetTrashFolder(getter_AddRefs(trash));
+ if (trash) {
+ uint32_t newFlags;
+ bool msgMoved;
+ m_newMsgHdr->AndFlags(~nsMsgMessageFlags::New, &newFlags);
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ rv = m_downloadFolder->GetMsgStore(getter_AddRefs(msgStore));
+ if (NS_SUCCEEDED(rv))
+ rv = msgStore->MoveNewlyDownloadedMessage(m_newMsgHdr, trash, &msgMoved);
+ if (NS_SUCCEEDED(rv) && !msgMoved) {
+ rv = MoveIncorporatedMessage(m_newMsgHdr, m_mailDB, trash,
+ nullptr, msgWindow);
+ if (NS_SUCCEEDED(rv))
+ rv = m_mailDB->RemoveHeaderMdbRow(m_newMsgHdr);
+ }
+ if (NS_FAILED(rv))
+ NS_WARNING("moveDupsToTrash failed for some reason.");
+ }
+ }
+ break;
+ case nsIMsgIncomingServer::markDupsRead:
+ MarkFilteredMessageRead(m_newMsgHdr);
+ break;
+ }
+ int32_t numNewMessages;
+ m_downloadFolder->GetNumNewMessages(false, &numNewMessages);
+ m_downloadFolder->SetNumNewMessages(numNewMessages - 1);
+
+ m_newMsgHdr = nullptr;
+ return 0;
+ }
+ }
+
+ ApplyFilters(&moved, msgWindow, msgOffset);
+ }
+ if (!moved)
+ {
+ if (m_mailDB)
+ {
+ m_mailDB->AddNewHdrToDB(m_newMsgHdr, true);
+ nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ if (notifier)
+ notifier->NotifyMsgAdded(m_newMsgHdr);
+ // mark the header as not yet reported classified
+ nsMsgKey msgKey;
+ m_newMsgHdr->GetMessageKey(&msgKey);
+ m_downloadFolder->OrProcessingFlags(
+ msgKey, nsMsgProcessingFlags::NotReportedClassified);
+ }
+ } // if it was moved by imap filter, m_parseMsgState->m_newMsgHdr == nullptr
+ m_newMsgHdr = nullptr;
+ }
+ return 0;
+}
+
+// We've found the start of the next message, so finish this one off.
+NS_IMETHODIMP nsParseNewMailState::FinishHeader()
+{
+ if (m_newMsgHdr)
+ {
+ m_newMsgHdr->SetMessageSize(m_position - m_envelope_pos);
+ m_newMsgHdr->SetLineCount(m_body_lines);
+ }
+ return NS_OK;
+}
+
+nsresult nsParseNewMailState::GetTrashFolder(nsIMsgFolder **pTrashFolder)
+{
+ nsresult rv=NS_ERROR_UNEXPECTED;
+ if (!pTrashFolder)
+ return NS_ERROR_NULL_POINTER;
+
+ if (m_downloadFolder)
+ {
+ nsCOMPtr <nsIMsgIncomingServer> incomingServer;
+ m_downloadFolder->GetServer(getter_AddRefs(incomingServer));
+ nsCOMPtr <nsIMsgFolder> rootMsgFolder;
+ incomingServer->GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
+ if (rootMsgFolder)
+ {
+ rv = rootMsgFolder->GetFolderWithFlags(nsMsgFolderFlags::Trash, pTrashFolder);
+ if (!*pTrashFolder)
+ rv = NS_ERROR_FAILURE;
+ }
+ }
+ return rv;
+}
+
+void nsParseNewMailState::ApplyFilters(bool *pMoved, nsIMsgWindow *msgWindow, uint64_t msgOffset)
+{
+ m_msgMovedByFilter = m_msgCopiedByFilter = false;
+ m_curHdrOffset = msgOffset;
+
+ if (!m_disableFilters)
+ {
+ nsCOMPtr<nsIMsgDBHdr> msgHdr = m_newMsgHdr;
+ nsCOMPtr<nsIMsgFolder> downloadFolder = m_downloadFolder;
+ nsCOMPtr <nsIMsgFolder> rootMsgFolder = do_QueryInterface(m_rootFolder);
+ if (rootMsgFolder)
+ {
+ if (!downloadFolder)
+ rootMsgFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox,
+ getter_AddRefs(downloadFolder));
+ if (downloadFolder)
+ downloadFolder->GetURI(m_inboxUri);
+ char * headers = m_headers.GetBuffer();
+ uint32_t headersSize = m_headers.GetBufferPos();
+ if (m_filterList)
+ (void) m_filterList->
+ ApplyFiltersToHdr(nsMsgFilterType::InboxRule, msgHdr, downloadFolder,
+ m_mailDB, headers, headersSize, this, msgWindow);
+ if (!m_msgMovedByFilter && m_deferredToServerFilterList)
+ {
+ (void) m_deferredToServerFilterList->
+ ApplyFiltersToHdr(nsMsgFilterType::InboxRule, msgHdr, downloadFolder,
+ m_mailDB, headers, headersSize, this, msgWindow);
+ }
+ }
+ }
+ if (pMoved)
+ *pMoved = m_msgMovedByFilter;
+}
+
+NS_IMETHODIMP nsParseNewMailState::ApplyFilterHit(nsIMsgFilter *filter, nsIMsgWindow *msgWindow, bool *applyMore)
+{
+ NS_ENSURE_ARG_POINTER(filter);
+ NS_ENSURE_ARG_POINTER(applyMore);
+
+ uint32_t newFlags;
+ nsresult rv = NS_OK;
+
+ *applyMore = true;
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr = m_newMsgHdr;
+
+ nsCOMPtr<nsIArray> filterActionList;
+
+ rv = filter->GetSortedActionList(getter_AddRefs(filterActionList));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t numActions;
+ rv = filterActionList->GetLength(&numActions);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool loggingEnabled = false;
+ if (m_filterList && numActions)
+ m_filterList->GetLoggingEnabled(&loggingEnabled);
+
+ bool msgIsNew = true;
+ for (uint32_t actionIndex = 0; actionIndex < numActions && *applyMore; actionIndex++)
+ {
+ nsCOMPtr<nsIMsgRuleAction> filterAction;
+ rv = filterActionList->QueryElementAt(actionIndex, NS_GET_IID(nsIMsgRuleAction),
+ getter_AddRefs(filterAction));
+ if (NS_FAILED(rv) || !filterAction)
+ continue;
+
+ nsMsgRuleActionType actionType;
+ if (NS_SUCCEEDED(filterAction->GetType(&actionType)))
+ {
+ if (loggingEnabled)
+ (void)filter->LogRuleHit(filterAction, msgHdr);
+
+ nsCString actionTargetFolderUri;
+ if (actionType == nsMsgFilterAction::MoveToFolder ||
+ actionType == nsMsgFilterAction::CopyToFolder)
+ {
+
+ rv = filterAction->GetTargetFolderUri(actionTargetFolderUri);
+ if (NS_FAILED(rv) || actionTargetFolderUri.IsEmpty())
+ {
+ NS_ASSERTION(false, "actionTargetFolderUri is empty");
+ continue;
+ }
+ }
+ switch (actionType)
+ {
+ case nsMsgFilterAction::Delete:
+ {
+ nsCOMPtr <nsIMsgFolder> trash;
+ // set value to trash folder
+ rv = GetTrashFolder(getter_AddRefs(trash));
+ if (NS_SUCCEEDED(rv) && trash)
+ rv = trash->GetURI(actionTargetFolderUri);
+
+ msgHdr->OrFlags(nsMsgMessageFlags::Read, &newFlags); // mark read in trash.
+ msgIsNew = false;
+ }
+
+ // FALLTHROUGH
+ MOZ_FALLTHROUGH;
+ case nsMsgFilterAction::MoveToFolder:
+ // if moving to a different file, do it.
+ if (actionTargetFolderUri.get() && !m_inboxUri.Equals(actionTargetFolderUri,
+ nsCaseInsensitiveCStringComparator()))
+ {
+ nsresult err;
+ nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &err));
+ NS_ENSURE_SUCCESS(err, err);
+ nsCOMPtr<nsIRDFResource> res;
+ err = rdf->GetResource(actionTargetFolderUri, getter_AddRefs(res));
+ if (NS_FAILED(err))
+ return err;
+
+ nsCOMPtr<nsIMsgFolder> destIFolder(do_QueryInterface(res, &err));
+ if (NS_FAILED(err))
+ return err;
+ bool msgMoved = false;
+ // If we're moving to an imap folder, or this message has already
+ // has a pending copy action, use the imap coalescer so that
+ // we won't truncate the inbox before the copy fires.
+ if (m_msgCopiedByFilter ||
+ StringBeginsWith(actionTargetFolderUri, NS_LITERAL_CSTRING("imap:")))
+ {
+ if (!m_moveCoalescer)
+ m_moveCoalescer = new nsImapMoveCoalescer(m_downloadFolder, m_msgWindow);
+ NS_ENSURE_TRUE(m_moveCoalescer, NS_ERROR_OUT_OF_MEMORY);
+ nsMsgKey msgKey;
+ (void) msgHdr->GetMessageKey(&msgKey);
+ m_moveCoalescer->AddMove(destIFolder, msgKey);
+ err = NS_OK;
+ msgIsNew = false;
+ }
+ else
+ {
+ nsCOMPtr<nsIMsgPluggableStore> msgStore;
+ err = m_downloadFolder->GetMsgStore(getter_AddRefs(msgStore));
+ if (NS_SUCCEEDED(err))
+ err = msgStore->MoveNewlyDownloadedMessage(msgHdr, destIFolder, &msgMoved);
+ if (NS_SUCCEEDED(err) && !msgMoved)
+ err = MoveIncorporatedMessage(msgHdr, m_mailDB, destIFolder,
+ filter, msgWindow);
+ m_msgMovedByFilter = NS_SUCCEEDED(err);
+ if (!m_msgMovedByFilter /* == NS_FAILED(err) */)
+ {
+ // XXX: Invoke MSG_LOG_TO_CONSOLE once bug 1135265 lands.
+ if (loggingEnabled)
+ (void) filter->LogRuleHitFail(filterAction, msgHdr, err, "Move failed");
+ }
+ }
+ }
+ *applyMore = false;
+ break;
+
+ case nsMsgFilterAction::CopyToFolder:
+ {
+ nsCString uri;
+ rv = m_rootFolder->GetURI(uri);
+
+ if (!actionTargetFolderUri.IsEmpty() && !actionTargetFolderUri.Equals(uri))
+ {
+ nsCOMPtr<nsIMutableArray> messageArray(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ messageArray->AppendElement(msgHdr, false);
+
+ nsCOMPtr<nsIMsgFolder> dstFolder;
+ nsCOMPtr<nsIMsgCopyService> copyService;
+ rv = GetExistingFolder(actionTargetFolderUri,
+ getter_AddRefs(dstFolder));
+ if (NS_SUCCEEDED(rv)) {
+ copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
+ }
+ else {
+ // Let's show a more specific warning.
+ NS_WARNING("Target Folder does not exist.");
+ return rv;
+ }
+ if (NS_SUCCEEDED(rv))
+ rv = copyService->CopyMessages(m_downloadFolder, messageArray, dstFolder,
+ false, nullptr, msgWindow, false);
+
+ if (NS_FAILED(rv)) {
+ // XXX: Invoke MSG_LOG_TO_CONSOLE once bug 1135265 lands.
+ if (loggingEnabled)
+ (void) filter->LogRuleHitFail(filterAction, msgHdr, rv, "Copy failed");
+ }
+ else
+ m_msgCopiedByFilter = true;
+ }
+ }
+ break;
+ case nsMsgFilterAction::MarkRead:
+ msgIsNew = false;
+ MarkFilteredMessageRead(msgHdr);
+ break;
+ case nsMsgFilterAction::MarkUnread:
+ msgIsNew = true;
+ MarkFilteredMessageUnread(msgHdr);
+ break;
+ case nsMsgFilterAction::KillThread:
+ msgHdr->SetUint32Property("ProtoThreadFlags", nsMsgMessageFlags::Ignored);
+ break;
+ case nsMsgFilterAction::KillSubthread:
+ msgHdr->OrFlags(nsMsgMessageFlags::Ignored, &newFlags);
+ break;
+ case nsMsgFilterAction::WatchThread:
+ msgHdr->OrFlags(nsMsgMessageFlags::Watched, &newFlags);
+ break;
+ case nsMsgFilterAction::MarkFlagged:
+ {
+ nsCOMPtr<nsIMutableArray> messageArray(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ messageArray->AppendElement(msgHdr, false);
+ m_downloadFolder->MarkMessagesFlagged(messageArray, true);
+ }
+ break;
+ case nsMsgFilterAction::ChangePriority:
+ nsMsgPriorityValue filterPriority;
+ filterAction->GetPriority(&filterPriority);
+ msgHdr->SetPriority(filterPriority);
+ break;
+ case nsMsgFilterAction::AddTag:
+ {
+ nsCString keyword;
+ filterAction->GetStrValue(keyword);
+ nsCOMPtr<nsIMutableArray> messageArray(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ messageArray->AppendElement(msgHdr, false);
+ m_downloadFolder->AddKeywordsToMessages(messageArray, keyword);
+ break;
+ }
+ case nsMsgFilterAction::Label:
+ nsMsgLabelValue filterLabel;
+ filterAction->GetLabel(&filterLabel);
+ nsMsgKey msgKey;
+ msgHdr->GetMessageKey(&msgKey);
+ m_mailDB->SetLabel(msgKey, filterLabel);
+ break;
+ case nsMsgFilterAction::JunkScore:
+ {
+ nsAutoCString junkScoreStr;
+ int32_t junkScore;
+ filterAction->GetJunkScore(&junkScore);
+ junkScoreStr.AppendInt(junkScore);
+ if (junkScore == nsIJunkMailPlugin::IS_SPAM_SCORE)
+ msgIsNew = false;
+ nsMsgKey msgKey;
+ msgHdr->GetMessageKey(&msgKey);
+ msgHdr->SetStringProperty("junkscore", junkScoreStr.get());
+ msgHdr->SetStringProperty("junkscoreorigin", "filter");
+ break;
+ }
+ case nsMsgFilterAction::Forward:
+ {
+ nsCString forwardTo;
+ filterAction->GetStrValue(forwardTo);
+ m_forwardTo.AppendElement(forwardTo);
+ m_msgToForwardOrReply = msgHdr;
+ }
+ break;
+ case nsMsgFilterAction::Reply:
+ {
+ nsCString replyTemplateUri;
+ filterAction->GetStrValue(replyTemplateUri);
+ m_replyTemplateUri.AppendElement(replyTemplateUri);
+ m_msgToForwardOrReply = msgHdr;
+ m_ruleAction = filterAction;
+ m_filter = filter;
+ }
+ break;
+ case nsMsgFilterAction::DeleteFromPop3Server:
+ {
+ uint32_t flags = 0;
+ nsCOMPtr <nsIMsgFolder> downloadFolder;
+ msgHdr->GetFolder(getter_AddRefs(downloadFolder));
+ nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(downloadFolder);
+ msgHdr->GetFlags(&flags);
+ if (localFolder)
+ {
+ nsCOMPtr<nsIMutableArray> messages = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ messages->AppendElement(msgHdr, false);
+ // This action ignores the deleteMailLeftOnServer preference
+ localFolder->MarkMsgsOnPop3Server(messages, POP3_FORCE_DEL);
+
+ // If this is just a header, throw it away. It's useless now
+ // that the server copy is being deleted.
+ if (flags & nsMsgMessageFlags::Partial)
+ {
+ m_msgMovedByFilter = true;
+ msgIsNew = false;
+ }
+ }
+ }
+ break;
+ case nsMsgFilterAction::FetchBodyFromPop3Server:
+ {
+ uint32_t flags = 0;
+ nsCOMPtr <nsIMsgFolder> downloadFolder;
+ msgHdr->GetFolder(getter_AddRefs(downloadFolder));
+ nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(downloadFolder);
+ msgHdr->GetFlags(&flags);
+ if (localFolder && (flags & nsMsgMessageFlags::Partial))
+ {
+ nsCOMPtr<nsIMutableArray> messages = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ messages->AppendElement(msgHdr, false);
+ localFolder->MarkMsgsOnPop3Server(messages, POP3_FETCH_BODY);
+ // Don't add this header to the DB, we're going to replace it
+ // with the full message.
+ m_msgMovedByFilter = true;
+ msgIsNew = false;
+ // Don't do anything else in this filter, wait until we
+ // have the full message.
+ *applyMore = false;
+ }
+ }
+ break;
+
+ case nsMsgFilterAction::StopExecution:
+ {
+ // don't apply any more filters
+ *applyMore = false;
+ }
+ break;
+
+ case nsMsgFilterAction::Custom:
+ {
+ nsCOMPtr<nsIMsgFilterCustomAction> customAction;
+ rv = filterAction->GetCustomAction(getter_AddRefs(customAction));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString value;
+ filterAction->GetStrValue(value);
+
+ nsCOMPtr<nsIMutableArray> messageArray(
+ do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ NS_ENSURE_TRUE(messageArray, rv);
+ if (NS_SUCCEEDED(rv))
+ rv = messageArray->AppendElement(msgHdr, false);
+
+
+ if (NS_SUCCEEDED(rv))
+ rv = customAction->Apply(messageArray, value, nullptr,
+ nsMsgFilterType::InboxRule, msgWindow);
+ if (NS_FAILED(rv)) {
+ // XXX: Invoke MSG_LOG_TO_CONSOLE once bug 1135265 lands.
+ if (loggingEnabled)
+ (void) filter->LogRuleHitFail(filterAction, msgHdr, rv, "Copy failed");
+ }
+ }
+ break;
+
+
+ default:
+ // XXX should not be reached. Check in debug build.
+ NS_ERROR("This default should not be reached.");
+ break;
+ }
+ }
+ }
+ if (!msgIsNew)
+ {
+ int32_t numNewMessages;
+ m_downloadFolder->GetNumNewMessages(false, &numNewMessages);
+ if (numNewMessages > 0)
+ m_downloadFolder->SetNumNewMessages(numNewMessages - 1);
+ m_numNotNewMessages++;
+ }
+ return rv;
+}
+
+// this gets run in a second pass, after apply filters to a header.
+nsresult nsParseNewMailState::ApplyForwardAndReplyFilter(nsIMsgWindow *msgWindow)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr <nsIMsgIncomingServer> server;
+
+ uint32_t i;
+ uint32_t count = m_forwardTo.Length();
+ for (i = 0; i < count; i++)
+ {
+ if (!m_forwardTo[i].IsEmpty())
+ {
+ nsAutoString forwardStr;
+ CopyASCIItoUTF16(m_forwardTo[i], forwardStr);
+ rv = m_rootFolder->GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, rv);
+ {
+ nsCOMPtr<nsIMsgComposeService> compService =
+ do_GetService (NS_MSGCOMPOSESERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = compService->ForwardMessage(forwardStr, m_msgToForwardOrReply,
+ msgWindow, server,
+ nsIMsgComposeService::kForwardAsDefault);
+ }
+ }
+ }
+ m_forwardTo.Clear();
+
+ count = m_replyTemplateUri.Length();
+ for (i = 0; i < count; i++)
+ {
+ if (!m_replyTemplateUri[i].IsEmpty())
+ {
+ // copy this and truncate the original, so we don't accidentally re-use it on the next hdr.
+ rv = m_rootFolder->GetServer(getter_AddRefs(server));
+ if (server)
+ {
+ nsCOMPtr <nsIMsgComposeService> compService = do_GetService (NS_MSGCOMPOSESERVICE_CONTRACTID) ;
+ if (compService) {
+ rv = compService->ReplyWithTemplate(m_msgToForwardOrReply,
+ m_replyTemplateUri[i].get(),
+ msgWindow, server);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("ReplyWithTemplate failed");
+ if (rv == NS_ERROR_ABORT) {
+ m_filter->LogRuleHitFail(m_ruleAction, m_msgToForwardOrReply, rv,
+ "Sending reply aborted");
+ } else {
+ m_filter->LogRuleHitFail(m_ruleAction, m_msgToForwardOrReply, rv,
+ "Error sending reply");
+ }
+ }
+ }
+ }
+ }
+ }
+ m_replyTemplateUri.Clear();
+ m_msgToForwardOrReply = nullptr;
+ return rv;
+}
+
+void nsParseNewMailState::MarkFilteredMessageRead(nsIMsgDBHdr *msgHdr)
+{
+ nsCOMPtr<nsIMutableArray> messageArray(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ messageArray->AppendElement(msgHdr, false);
+ m_downloadFolder->MarkMessagesRead(messageArray, true);
+}
+
+void nsParseNewMailState::MarkFilteredMessageUnread(nsIMsgDBHdr *msgHdr)
+{
+ uint32_t newFlags;
+ if (m_mailDB)
+ {
+ nsMsgKey msgKey;
+ msgHdr->GetMessageKey(&msgKey);
+ m_mailDB->AddToNewList(msgKey);
+ }
+ else
+ {
+ msgHdr->OrFlags(nsMsgMessageFlags::New, &newFlags);
+ }
+ nsCOMPtr<nsIMutableArray> messageArray(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ messageArray->AppendElement(msgHdr, false);
+ m_downloadFolder->MarkMessagesRead(messageArray, false);
+}
+
+nsresult nsParseNewMailState::EndMsgDownload()
+{
+ if (m_moveCoalescer)
+ m_moveCoalescer->PlaybackMoves();
+
+ // need to do this for all folders that had messages filtered into them
+ uint32_t serverCount = m_filterTargetFolders.Count();
+ nsresult rv;
+ nsCOMPtr<nsIMsgMailSession> session =
+ do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && session) // don't use NS_ENSURE_SUCCESS here - we need to release semaphore below
+ {
+ for (uint32_t index = 0; index < serverCount; index++)
+ {
+ bool folderOpen;
+ session->IsFolderOpenInWindow(m_filterTargetFolders[index], &folderOpen);
+ if (!folderOpen)
+ {
+ uint32_t folderFlags;
+ m_filterTargetFolders[index]->GetFlags(&folderFlags);
+ if (! (folderFlags & (nsMsgFolderFlags::Trash | nsMsgFolderFlags::Inbox)))
+ {
+ bool filtersRun;
+ m_filterTargetFolders[index]->CallFilterPlugins(nullptr, &filtersRun);
+ if (!filtersRun)
+ m_filterTargetFolders[index]->SetMsgDatabase(nullptr);
+ }
+ }
+ }
+ }
+ m_filterTargetFolders.Clear();
+ return rv;
+}
+
+nsresult nsParseNewMailState::AppendMsgFromStream(nsIInputStream *fileStream,
+ nsIMsgDBHdr *aHdr,
+ uint32_t length,
+ nsIMsgFolder *destFolder)
+{
+ nsCOMPtr <nsISeekableStream> seekableStream = do_QueryInterface(fileStream);
+ nsCOMPtr<nsIMsgPluggableStore> store;
+ nsCOMPtr<nsIOutputStream> destOutputStream;
+ nsresult rv = destFolder->GetMsgStore(getter_AddRefs(store));
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool reusable;
+ rv = store->GetNewMsgOutputStream(destFolder, &aHdr, &reusable,
+ getter_AddRefs(destOutputStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!m_ibuffer)
+ {
+ m_ibuffer_size = FILE_IO_BUFFER_SIZE;
+ m_ibuffer = (char *) PR_Malloc(m_ibuffer_size);
+ NS_ASSERTION(m_ibuffer != nullptr, "couldn't get memory to move msg");
+ }
+ m_ibuffer_fp = 0;
+
+ while (length > 0 && m_ibuffer)
+ {
+ uint32_t nRead;
+ fileStream->Read (m_ibuffer, length > m_ibuffer_size ? m_ibuffer_size : length, &nRead);
+ if (nRead == 0)
+ break;
+
+ uint32_t bytesWritten;
+ // Check the number of bytes actually written to the stream.
+ destOutputStream->Write(m_ibuffer, nRead, &bytesWritten);
+ if (bytesWritten != nRead)
+ {
+ destOutputStream->Close();
+ return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
+ }
+
+ length -= nRead;
+ }
+
+ NS_ASSERTION(length == 0, "didn't read all of original message in filter move");
+
+ // non-reusable streams will get closed by the store.
+ if (reusable)
+ destOutputStream->Close();
+ return store->FinishNewMessage(destOutputStream, aHdr);
+}
+
+/*
+ * Moves message pointed to by mailHdr into folder destIFolder.
+ * After successful move mailHdr is no longer usable by the caller.
+ */
+nsresult nsParseNewMailState::MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr,
+ nsIMsgDatabase *sourceDB,
+ nsIMsgFolder *destIFolder,
+ nsIMsgFilter *filter,
+ nsIMsgWindow *msgWindow)
+{
+ NS_ENSURE_ARG_POINTER(destIFolder);
+ nsresult rv = NS_OK;
+
+ // check if the destination is a real folder (by checking for null parent)
+ // and if it can file messages (e.g., servers or news folders can't file messages).
+ // Or read only imap folders...
+ bool canFileMessages = true;
+ nsCOMPtr<nsIMsgFolder> parentFolder;
+ destIFolder->GetParent(getter_AddRefs(parentFolder));
+ if (parentFolder)
+ destIFolder->GetCanFileMessages(&canFileMessages);
+ if (!parentFolder || !canFileMessages)
+ {
+ if (filter)
+ {
+ filter->SetEnabled(false);
+ // we need to explicitly save the filter file.
+ if (m_filterList)
+ m_filterList->SaveToDefaultFile();
+ destIFolder->ThrowAlertMsg("filterDisabled", msgWindow);
+ }
+ return NS_MSG_NOT_A_MAIL_FOLDER;
+ }
+
+ uint32_t messageLength;
+ mailHdr->GetMessageSize(&messageLength);
+
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(destIFolder);
+ if (localFolder) {
+ bool destFolderTooBig = true;
+ rv = localFolder->WarnIfLocalFileTooBig(msgWindow, messageLength,
+ &destFolderTooBig);
+ if (NS_FAILED(rv) || destFolderTooBig)
+ return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
+ }
+
+ nsCOMPtr<nsISupports> myISupports =
+ do_QueryInterface(static_cast<nsIMsgParseMailMsgState*>(this));
+
+ // Make sure no one else is writing into this folder
+ if (NS_FAILED(rv = destIFolder->AcquireSemaphore (myISupports)))
+ {
+ destIFolder->ThrowAlertMsg("filterFolderDeniedLocked", msgWindow);
+ return rv;
+ }
+ nsCOMPtr<nsIInputStream> inputStream;
+ bool reusable;
+ rv = m_downloadFolder->GetMsgInputStream(mailHdr, &reusable, getter_AddRefs(inputStream));
+ if (!inputStream)
+ {
+ NS_ERROR("couldn't get source msg input stream in move filter");
+ destIFolder->ReleaseSemaphore (myISupports);
+ return NS_MSG_FOLDER_UNREADABLE; // ### dmb
+ }
+
+ nsCOMPtr<nsIMsgDatabase> destMailDB;
+
+ if (!localFolder)
+ return NS_MSG_POP_FILTER_TARGET_ERROR;
+
+ // don't force upgrade in place - open the db here before we start writing to the
+ // destination file because XP_Stat can return file size including bytes written...
+ rv = localFolder->GetDatabaseWOReparse(getter_AddRefs(destMailDB));
+ NS_WARNING_ASSERTION(destMailDB && NS_SUCCEEDED(rv),
+ "failed to open mail db parsing folder");
+ nsCOMPtr<nsIMsgDBHdr> newHdr;
+
+ if (destMailDB)
+ rv = destMailDB->CopyHdrFromExistingHdr(m_new_key, mailHdr, true,
+ getter_AddRefs(newHdr));
+ if (NS_SUCCEEDED(rv) && !newHdr)
+ rv = NS_ERROR_UNEXPECTED;
+
+ if (NS_FAILED(rv))
+ {
+ destIFolder->ThrowAlertMsg("filterFolderHdrAddFailed", msgWindow);
+ } else {
+ rv = AppendMsgFromStream(inputStream, newHdr, messageLength, destIFolder);
+ if (NS_FAILED(rv))
+ destIFolder->ThrowAlertMsg("filterFolderWriteFailed", msgWindow);
+ }
+
+ if (NS_FAILED(rv))
+ {
+ if (destMailDB)
+ destMailDB->Close(true);
+
+ destIFolder->ReleaseSemaphore(myISupports);
+
+ return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
+ }
+
+ bool movedMsgIsNew = false;
+ // if we have made it this far then the message has successfully been written to the new folder
+ // now add the header to the destMailDB.
+
+ uint32_t newFlags;
+ newHdr->GetFlags(&newFlags);
+ nsMsgKey msgKey;
+ newHdr->GetMessageKey(&msgKey);
+ if (!(newFlags & nsMsgMessageFlags::Read))
+ {
+ nsCString junkScoreStr;
+ (void) newHdr->GetStringProperty("junkscore", getter_Copies(junkScoreStr));
+ if (atoi(junkScoreStr.get()) == nsIJunkMailPlugin::IS_HAM_SCORE)
+ {
+ newHdr->OrFlags(nsMsgMessageFlags::New, &newFlags);
+ destMailDB->AddToNewList(msgKey);
+ movedMsgIsNew = true;
+ }
+ }
+ nsCOMPtr<nsIMsgFolderNotificationService> notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
+ if (notifier)
+ notifier->NotifyMsgAdded(newHdr);
+ // mark the header as not yet reported classified
+ destIFolder->OrProcessingFlags(
+ msgKey, nsMsgProcessingFlags::NotReportedClassified);
+ m_msgToForwardOrReply = newHdr;
+
+ if (movedMsgIsNew)
+ destIFolder->SetHasNewMessages(true);
+ if (!m_filterTargetFolders.Contains(destIFolder))
+ m_filterTargetFolders.AppendObject(destIFolder);
+
+ destIFolder->ReleaseSemaphore (myISupports);
+
+ (void) localFolder->RefreshSizeOnDisk();
+
+ // Notify the message was moved.
+ if (notifier) {
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsresult rv = mailHdr->GetFolder(getter_AddRefs(folder));
+ if (NS_SUCCEEDED(rv)) {
+ notifier->NotifyItemEvent(folder,
+ NS_LITERAL_CSTRING("UnincorporatedMessageMoved"),
+ newHdr);
+ } else {
+ NS_WARNING("Can't get folder for message that was moved.");
+ }
+ }
+
+ nsCOMPtr<nsIMsgPluggableStore> store;
+ rv = m_downloadFolder->GetMsgStore(getter_AddRefs(store));
+ if (store)
+ store->DiscardNewMessage(m_outputStream, mailHdr);
+ if (sourceDB)
+ sourceDB->RemoveHeaderMdbRow(mailHdr);
+
+ // update the folder size so we won't reparse.
+ UpdateDBFolderInfo(destMailDB);
+ destIFolder->UpdateSummaryTotals(true);
+
+ destMailDB->Commit(nsMsgDBCommitType::kLargeCommit);
+ return rv;
+}
diff --git a/mailnews/local/src/nsParseMailbox.h b/mailnews/local/src/nsParseMailbox.h
new file mode 100644
index 000000000..388f1e1b2
--- /dev/null
+++ b/mailnews/local/src/nsParseMailbox.h
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsParseMailbox_H
+#define nsParseMailbox_H
+
+#include "mozilla/Attributes.h"
+#include "nsIURI.h"
+#include "nsIMsgParseMailMsgState.h"
+#include "nsIStreamListener.h"
+#include "nsMsgLineBuffer.h"
+#include "nsIMsgDatabase.h"
+#include "nsIMsgHdr.h"
+#include "nsIMsgStatusFeedback.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "nsIDBChangeListener.h"
+#include "nsIWeakReference.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsIMsgWindow.h"
+#include "nsImapMoveCoalescer.h"
+#include "nsAutoPtr.h"
+#include "nsStringGlue.h"
+#include "nsIMsgFilterList.h"
+#include "nsIMsgFilter.h"
+#include "nsIMsgFilterHitNotify.h"
+#include "nsIMsgFolderNotificationService.h"
+#include "nsTArray.h"
+
+class nsByteArray;
+class nsOutputFileStream;
+class nsIOFileStream;
+class nsInputFileStream;
+class nsIMsgFilter;
+class MSG_FolderInfoMail;
+class nsIMsgFilterList;
+class nsIMsgFolder;
+
+/* Used for the various things that parse RFC822 headers...
+ */
+typedef struct message_header
+{
+ const char *value; /* The contents of a header (after ": ") */
+ int32_t length; /* The length of the data (it is not NULL-terminated.) */
+} message_header;
+
+// This object maintains the parse state for a single mail message.
+class nsParseMailMessageState : public nsIMsgParseMailMsgState, public nsIDBChangeListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMSGPARSEMAILMSGSTATE
+ NS_DECL_NSIDBCHANGELISTENER
+
+ nsParseMailMessageState();
+
+ void Init(uint64_t fileposition);
+ virtual nsresult ParseFolderLine(const char *line, uint32_t lineLength);
+ virtual nsresult StartNewEnvelope(const char *line, uint32_t lineLength);
+ nsresult ParseHeaders();
+ nsresult FinalizeHeaders();
+ nsresult ParseEnvelope (const char *line, uint32_t line_size);
+ nsresult InternSubject (struct message_header *header);
+
+ static bool IsEnvelopeLine(const char *buf, int32_t buf_size);
+
+ nsCOMPtr<nsIMsgDBHdr> m_newMsgHdr; /* current message header we're building */
+ nsCOMPtr<nsIMsgDatabase> m_mailDB;
+ nsCOMPtr<nsIMsgDatabase> m_backupMailDB;
+
+ nsMailboxParseState m_state;
+ int64_t m_position;
+ uint64_t m_envelope_pos;
+ uint64_t m_headerstartpos;
+ nsMsgKey m_new_key; // DB key for the new header.
+
+ nsByteArray m_headers;
+
+ nsByteArray m_envelope;
+
+ struct message_header m_message_id;
+ struct message_header m_references;
+ struct message_header m_date;
+ struct message_header m_delivery_date;
+ struct message_header m_from;
+ struct message_header m_sender;
+ struct message_header m_newsgroups;
+ struct message_header m_subject;
+ struct message_header m_status;
+ struct message_header m_mozstatus;
+ struct message_header m_mozstatus2;
+ struct message_header m_in_reply_to;
+ struct message_header m_replyTo;
+ struct message_header m_content_type;
+ struct message_header m_bccList;
+
+ // Support for having multiple To or Cc header lines in a message
+ nsTArray<struct message_header*> m_toList;
+ nsTArray<struct message_header*> m_ccList;
+ struct message_header *GetNextHeaderInAggregate (nsTArray<struct message_header*> &list);
+ void GetAggregateHeader (nsTArray<struct message_header*> &list, struct message_header *);
+ void ClearAggregateHeader (nsTArray<struct message_header*> &list);
+
+ struct message_header m_envelope_from;
+ struct message_header m_envelope_date;
+ struct message_header m_priority;
+ struct message_header m_account_key;
+ struct message_header m_keywords;
+ // Mdn support
+ struct message_header m_mdn_original_recipient;
+ struct message_header m_return_path;
+ struct message_header m_mdn_dnt; /* MDN Disposition-Notification-To: header */
+
+ PRTime m_receivedTime;
+ uint16_t m_body_lines;
+
+ bool m_IgnoreXMozillaStatus;
+
+ // this enables extensions to add the values of particular headers to
+ // the .msf file as properties of nsIMsgHdr. It is initialized from a
+ // pref, mailnews.customDBHeaders
+ nsTArray<nsCString> m_customDBHeaders;
+ struct message_header *m_customDBHeaderValues;
+ nsCString m_receivedValue; // accumulated received header
+protected:
+ virtual ~nsParseMailMessageState();
+};
+
+// This class is part of the mailbox parsing state machine
+class nsMsgMailboxParser : public nsIStreamListener, public nsParseMailMessageState, public nsMsgLineBuffer
+{
+public:
+ nsMsgMailboxParser(nsIMsgFolder *);
+ nsMsgMailboxParser();
+ nsresult Init();
+
+ bool IsRunningUrl() { return m_urlInProgress;} // returns true if we are currently running a url and false otherwise...
+ NS_DECL_ISUPPORTS_INHERITED
+
+ ////////////////////////////////////////////////////////////////////////////////////////
+ // we suppport the nsIStreamListener interface
+ ////////////////////////////////////////////////////////////////////////////////////////
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ void SetDB (nsIMsgDatabase *mailDB) {m_mailDB = mailDB; }
+
+ // message socket libnet callbacks, which come through folder pane
+ nsresult ProcessMailboxInputStream(nsIURI* aURL, nsIInputStream *aIStream, uint32_t aLength);
+
+ virtual void DoneParsingFolder(nsresult status);
+ virtual void AbortNewHeader();
+
+ // for nsMsgLineBuffer
+ virtual nsresult HandleLine(const char *line, uint32_t line_length) override;
+
+ void UpdateDBFolderInfo();
+ void UpdateDBFolderInfo(nsIMsgDatabase *mailDB);
+ void UpdateStatusText(const char* stringName);
+
+ // Update the progress bar based on what we know.
+ virtual void UpdateProgressPercent ();
+ virtual void OnNewMessage(nsIMsgWindow *msgWindow);
+
+protected:
+ virtual ~nsMsgMailboxParser();
+ nsCOMPtr<nsIMsgStatusFeedback> m_statusFeedback;
+
+ virtual int32_t PublishMsgHeader(nsIMsgWindow *msgWindow);
+ void FreeBuffers();
+
+ // data
+ nsString m_folderName;
+ nsCString m_inboxUri;
+ nsByteArray m_inputStream;
+ int32_t m_obuffer_size;
+ char *m_obuffer;
+ uint64_t m_graph_progress_total;
+ uint64_t m_graph_progress_received;
+ bool m_parsingDone;
+ PRTime m_startTime;
+private:
+ // the following flag is used to determine when a url is currently being run. It is cleared on calls
+ // to ::StopBinding and it is set whenever we call Load on a url
+ bool m_urlInProgress;
+ nsWeakPtr m_folder;
+ void ReleaseFolderLock();
+ nsresult AcquireFolderLock();
+
+};
+
+class nsParseNewMailState : public nsMsgMailboxParser
+, public nsIMsgFilterHitNotify
+{
+public:
+ nsParseNewMailState();
+ NS_DECL_ISUPPORTS_INHERITED
+
+ NS_IMETHOD FinishHeader() override;
+
+ nsresult Init(nsIMsgFolder *rootFolder, nsIMsgFolder *downloadFolder,
+ nsIMsgWindow *aMsgWindow, nsIMsgDBHdr *aHdr,
+ nsIOutputStream *aOutputStream);
+
+ virtual void DoneParsingFolder(nsresult status) override;
+
+ void DisableFilters() {m_disableFilters = true;}
+
+ NS_DECL_NSIMSGFILTERHITNOTIFY
+
+ nsOutputFileStream *GetLogFile();
+ virtual int32_t PublishMsgHeader(nsIMsgWindow *msgWindow) override;
+ void GetMsgWindow(nsIMsgWindow **aMsgWindow);
+ nsresult EndMsgDownload();
+
+ nsresult AppendMsgFromStream(nsIInputStream *fileStream, nsIMsgDBHdr *aHdr,
+ uint32_t length, nsIMsgFolder *destFolder);
+
+ virtual void ApplyFilters(bool *pMoved, nsIMsgWindow *msgWindow,
+ uint64_t msgOffset);
+ nsresult ApplyForwardAndReplyFilter(nsIMsgWindow *msgWindow);
+ virtual void OnNewMessage(nsIMsgWindow *msgWindow) override;
+
+ // this keeps track of how many messages we downloaded that
+ // aren't new - e.g., marked read, or moved to an other server.
+ int32_t m_numNotNewMessages;
+protected:
+ virtual ~nsParseNewMailState();
+ virtual nsresult GetTrashFolder(nsIMsgFolder **pTrashFolder);
+ virtual nsresult MoveIncorporatedMessage(nsIMsgDBHdr *mailHdr,
+ nsIMsgDatabase *sourceDB,
+ nsIMsgFolder *destIFolder,
+ nsIMsgFilter *filter,
+ nsIMsgWindow *msgWindow);
+ virtual void MarkFilteredMessageRead(nsIMsgDBHdr *msgHdr);
+ virtual void MarkFilteredMessageUnread(nsIMsgDBHdr *msgHdr);
+
+ nsCOMPtr <nsIMsgFilterList> m_filterList;
+ nsCOMPtr <nsIMsgFilterList> m_deferredToServerFilterList;
+ nsCOMPtr <nsIMsgFolder> m_rootFolder;
+ nsCOMPtr <nsIMsgWindow> m_msgWindow;
+ nsCOMPtr <nsIMsgFolder> m_downloadFolder;
+ nsCOMPtr<nsIOutputStream> m_outputStream;
+ nsCOMArray <nsIMsgFolder> m_filterTargetFolders;
+
+ RefPtr<nsImapMoveCoalescer> m_moveCoalescer;
+
+ bool m_msgMovedByFilter;
+ bool m_msgCopiedByFilter;
+ bool m_disableFilters;
+ uint32_t m_ibuffer_fp;
+ char *m_ibuffer;
+ uint32_t m_ibuffer_size;
+ // used for applying move filters, because in the case of using a temporary
+ // download file, the offset/key in the msg hdr is not right.
+ uint64_t m_curHdrOffset;
+
+ // we have to apply the reply/forward filters in a second pass, after
+ // msg quarantining and moving to other local folders, so we remember the
+ // info we'll need to apply them with these vars.
+ // these need to be arrays in case we have multiple reply/forward filters.
+ nsTArray<nsCString> m_forwardTo;
+ nsTArray<nsCString> m_replyTemplateUri;
+ nsCOMPtr<nsIMsgDBHdr> m_msgToForwardOrReply;
+ nsCOMPtr<nsIMsgFilter> m_filter;
+ nsCOMPtr<nsIMsgRuleAction> m_ruleAction;
+};
+
+#endif
diff --git a/mailnews/local/src/nsPop3IncomingServer.cpp b/mailnews/local/src/nsPop3IncomingServer.cpp
new file mode 100644
index 000000000..261d141a4
--- /dev/null
+++ b/mailnews/local/src/nsPop3IncomingServer.cpp
@@ -0,0 +1,744 @@
+/* -*- Mode: C++; tab-width: 4; 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 "prmem.h"
+#include "plstr.h"
+#include "prprf.h"
+
+#include "nsCOMPtr.h"
+#include "nsIStringBundle.h"
+#include "nsIPop3IncomingServer.h"
+#include "nsPop3IncomingServer.h"
+#include "nsIPop3Service.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgLocalCID.h"
+#include "nsMsgFolderFlags.h"
+#include "nsPop3Protocol.h"
+#include "nsAutoPtr.h"
+#include "nsMsgKeyArray.h"
+#include "nsIMsgLocalMailFolder.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIMutableArray.h"
+#include "nsMsgUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "mozilla/Likely.h"
+
+static NS_DEFINE_CID(kCPop3ServiceCID, NS_POP3SERVICE_CID);
+
+class nsPop3GetMailChainer final : public nsIUrlListener
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIURLLISTENER
+
+ nsPop3GetMailChainer();
+ nsresult GetNewMailForServers(nsIPop3IncomingServer** servers, uint32_t count,
+ nsIMsgWindow *msgWindow,
+ nsIMsgFolder *folderToDownloadTo, nsIUrlListener *listener);
+ nsresult RunNextGetNewMail();
+protected:
+ ~nsPop3GetMailChainer();
+ nsCOMPtr <nsIMsgFolder> m_folderToDownloadTo;
+ nsCOMPtr <nsIMsgWindow> m_downloadingMsgWindow;
+ nsCOMArray<nsIPop3IncomingServer> m_serversToGetNewMailFor;
+ nsCOMPtr <nsIUrlListener> m_listener;
+};
+
+
+
+NS_IMPL_ISUPPORTS_INHERITED(nsPop3IncomingServer,
+ nsMsgIncomingServer,
+ nsIPop3IncomingServer,
+ nsILocalMailIncomingServer)
+
+nsPop3IncomingServer::nsPop3IncomingServer()
+{
+ m_capabilityFlags =
+ POP3_AUTH_MECH_UNDEFINED |
+ POP3_HAS_AUTH_USER | // should be always there
+ POP3_GURL_UNDEFINED |
+ POP3_UIDL_UNDEFINED |
+ POP3_TOP_UNDEFINED |
+ POP3_XTND_XLST_UNDEFINED;
+
+ m_canHaveFilters = true;
+ m_authenticated = false;
+}
+
+nsPop3IncomingServer::~nsPop3IncomingServer()
+{
+}
+
+NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer,
+ LeaveMessagesOnServer,
+ "leave_on_server")
+
+NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer,
+ HeadersOnly,
+ "headers_only")
+
+NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer,
+ DeleteMailLeftOnServer,
+ "delete_mail_left_on_server")
+
+NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer,
+ DotFix,
+ "dot_fix")
+
+NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer,
+ DeleteByAgeFromServer,
+ "delete_by_age_from_server")
+
+NS_IMPL_SERVERPREF_INT(nsPop3IncomingServer,
+ NumDaysToLeaveOnServer,
+ "num_days_to_leave_on_server")
+
+
+NS_IMPL_SERVERPREF_BOOL(nsPop3IncomingServer,
+ DeferGetNewMail,
+ "defer_get_new_mail")
+
+NS_IMETHODIMP nsPop3IncomingServer::GetDeferredToAccount(nsACString& aRetVal)
+{
+ nsresult rv = GetCharValue("deferred_to_account", aRetVal);
+ if (aRetVal.IsEmpty())
+ return rv;
+ // We need to repair broken profiles that defer to hidden or invalid servers,
+ // so find out if the deferred to account has a valid non-hidden server, and
+ // if not, defer to the local folders inbox.
+ nsCOMPtr<nsIMsgAccountManager> acctMgr =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID);
+ bool invalidAccount = true;
+ if (acctMgr)
+ {
+ nsCOMPtr<nsIMsgAccount> account;
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = acctMgr->GetAccount(aRetVal, getter_AddRefs(account));
+ if (account)
+ {
+ account->GetIncomingServer(getter_AddRefs(server));
+ if (server)
+ server->GetHidden(&invalidAccount);
+ }
+ if (invalidAccount)
+ {
+ nsCOMPtr<nsIMsgIncomingServer> localServer;
+ nsCOMPtr<nsIMsgAccount> localAccount;
+
+ rv = acctMgr->GetLocalFoldersServer(getter_AddRefs(localServer));
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Try to copy any folders that have been stranded in the hidden account
+ // into the local folders account.
+ if (server)
+ {
+ nsCOMPtr<nsIMsgFolder> hiddenRootFolder;
+ nsCOMPtr<nsIMsgFolder> localFoldersRoot;
+ server->GetRootFolder(getter_AddRefs(hiddenRootFolder));
+ localServer->GetRootFolder(getter_AddRefs(localFoldersRoot));
+ if (hiddenRootFolder && localFoldersRoot)
+ {
+ // We're going to iterate over the folders in Local Folders-1,
+ // though I suspect only the Inbox will have messages. I don't
+ // think Sent Mail could end up here, but if any folders have
+ // messages, might as well copy them to the real Local Folders
+ // account.
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = hiddenRootFolder->GetSubFolders(getter_AddRefs(enumerator));
+ if (NS_SUCCEEDED(rv))
+ {
+ bool hasMore;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) &&
+ hasMore)
+ {
+ nsCOMPtr<nsISupports> item;
+ enumerator->GetNext(getter_AddRefs(item));
+ nsCOMPtr<nsIMsgFolder> subFolder(do_QueryInterface(item));
+ if (subFolder)
+ {
+ nsCOMPtr<nsIMsgDatabase> subFolderDB;
+ subFolder->GetMsgDatabase(getter_AddRefs(subFolderDB));
+ if (subFolderDB)
+ {
+ // Copy any messages in this sub-folder of the hidden
+ // account to the corresponding folder in Local Folders.
+ RefPtr<nsMsgKeyArray> keys = new nsMsgKeyArray;
+ rv = subFolderDB->ListAllKeys(keys);
+ nsCOMPtr<nsIMutableArray> hdrsToCopy(do_CreateInstance(NS_ARRAY_CONTRACTID));
+ MsgGetHeadersFromKeys(subFolderDB, keys->m_keys, hdrsToCopy);
+ uint32_t numHdrs = 0;
+ if (hdrsToCopy)
+ hdrsToCopy->GetLength(&numHdrs);
+ if (numHdrs)
+ {
+ // Look for a folder with the same name in Local Folders.
+ nsCOMPtr<nsIMsgFolder> dest;
+ nsString folderName;
+ subFolder->GetName(folderName);
+ localFoldersRoot->GetChildNamed(folderName,
+ getter_AddRefs(dest));
+ if (dest)
+ dest->CopyMessages(subFolder, hdrsToCopy, false,
+ nullptr, nullptr, false,false);
+ // Should we copy the folder if the dest doesn't exist?
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ rv = acctMgr->FindAccountForServer(localServer, getter_AddRefs(localAccount));
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!localAccount)
+ return NS_ERROR_NOT_AVAILABLE;
+
+ localAccount->GetKey(aRetVal);
+ // Can't call SetDeferredToAccount because it calls GetDeferredToAccount.
+ return SetCharValue("deferred_to_account", aRetVal);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsPop3IncomingServer::SetDeferredToAccount(const nsACString& aAccountKey)
+{
+ nsCString deferredToAccount;
+ GetDeferredToAccount(deferredToAccount);
+ m_rootMsgFolder = nullptr; // clear this so we'll recalculate it on demand.
+ //Notify listeners who listen to every folder
+
+ nsresult rv = SetCharValue("deferred_to_account", aAccountKey);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIFolderListener> folderListenerManager =
+ do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ // use GetRootFolder, because that returns the real
+ // root, not the deferred to root.
+ rv = GetRootFolder(getter_AddRefs(rootFolder));
+ if (rootFolder)
+ {
+ // if isDeferred state has changed, send notification
+ if (aAccountKey.IsEmpty() != deferredToAccount.IsEmpty())
+ {
+ nsCOMPtr <nsIAtom> deferAtom = MsgGetAtom("isDeferred");
+ nsCOMPtr <nsIAtom> canFileAtom = MsgGetAtom("CanFileMessages");
+ folderListenerManager->OnItemBoolPropertyChanged(rootFolder, deferAtom,
+ !deferredToAccount.IsEmpty(), deferredToAccount.IsEmpty());
+ folderListenerManager->OnItemBoolPropertyChanged(rootFolder, canFileAtom,
+ deferredToAccount.IsEmpty(), !deferredToAccount.IsEmpty());
+
+ // this hack causes the account manager ds to send notifications to the
+ // xul content builder that make the changed acct appear or disappear
+ // from the folder pane and related menus.
+ nsCOMPtr<nsIMsgAccountManager> acctMgr =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID);
+ if (acctMgr)
+ {
+ acctMgr->NotifyServerUnloaded(this);
+ acctMgr->NotifyServerLoaded(this);
+ // check if this newly deferred to account is the local folders account
+ // and needs to have a newly created INBOX.
+ if (!aAccountKey.IsEmpty())
+ {
+ nsCOMPtr <nsIMsgAccount> account;
+ acctMgr->GetAccount(aAccountKey, getter_AddRefs(account));
+ if (account)
+ {
+ nsCOMPtr <nsIMsgIncomingServer> server;
+ account->GetIncomingServer(getter_AddRefs(server));
+ if (server)
+ {
+ nsCOMPtr <nsILocalMailIncomingServer> incomingLocalServer = do_QueryInterface(server);
+ if (incomingLocalServer)
+ {
+ nsCOMPtr <nsIMsgFolder> rootFolder;
+ rv = server->GetRootFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ // this will fail if it already exists, which is fine.
+ rootFolder->CreateSubfolder(NS_LITERAL_STRING("Inbox"), nullptr);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+//NS_IMPL_GETSET(nsPop3IncomingServer, Authenticated, bool, m_authenticated);
+
+NS_IMETHODIMP nsPop3IncomingServer::GetAuthenticated(bool *aAuthenticated)
+{
+ NS_ENSURE_ARG_POINTER(aAuthenticated);
+ *aAuthenticated = m_authenticated;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3IncomingServer::SetAuthenticated(bool aAuthenticated)
+{
+ m_authenticated = aAuthenticated;
+ return NS_OK;
+}
+
+nsresult
+nsPop3IncomingServer::GetPop3CapabilityFlags(uint32_t *flags)
+{
+ *flags = m_capabilityFlags;
+ return NS_OK;
+}
+
+nsresult
+nsPop3IncomingServer::SetPop3CapabilityFlags(uint32_t flags)
+{
+ m_capabilityFlags = flags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3IncomingServer::GetRootMsgFolder(nsIMsgFolder **aRootMsgFolder)
+{
+ NS_ENSURE_ARG_POINTER(aRootMsgFolder);
+ nsresult rv = NS_OK;
+ if (!m_rootMsgFolder)
+ {
+ nsCString deferredToAccount;
+ GetDeferredToAccount(deferredToAccount);
+ if (deferredToAccount.IsEmpty())
+ {
+ rv = CreateRootFolder();
+ m_rootMsgFolder = m_rootFolder;
+ }
+ else
+ {
+ nsCOMPtr <nsIMsgAccountManager> accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ nsCOMPtr <nsIMsgAccount> account;
+ rv = accountManager->GetAccount(deferredToAccount, getter_AddRefs(account));
+ NS_ENSURE_SUCCESS(rv,rv);
+ if (account)
+ {
+ nsCOMPtr <nsIMsgIncomingServer> incomingServer;
+ rv = account->GetIncomingServer(getter_AddRefs(incomingServer));
+ NS_ENSURE_SUCCESS(rv,rv);
+ // make sure we're not deferred to ourself...
+ if (incomingServer && incomingServer != this)
+ rv = incomingServer->GetRootMsgFolder(getter_AddRefs(m_rootMsgFolder));
+ else
+ rv = NS_ERROR_FAILURE;
+ }
+ }
+ }
+
+ NS_IF_ADDREF(*aRootMsgFolder = m_rootMsgFolder);
+ return m_rootMsgFolder ? rv : NS_ERROR_FAILURE;
+}
+
+nsresult nsPop3IncomingServer::GetInbox(nsIMsgWindow *msgWindow, nsIMsgFolder **inbox)
+{
+ NS_ENSURE_ARG_POINTER(inbox);
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootMsgFolder(getter_AddRefs(rootFolder));
+ if(NS_SUCCEEDED(rv) && rootFolder)
+ {
+ rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox, inbox);
+ }
+
+ nsCOMPtr<nsIMsgLocalMailFolder> localInbox = do_QueryInterface(*inbox, &rv);
+ if (NS_SUCCEEDED(rv) && localInbox)
+ {
+ nsCOMPtr <nsIMsgDatabase> db;
+ rv = (*inbox)->GetMsgDatabase(getter_AddRefs(db));
+ if (NS_FAILED(rv))
+ {
+ (*inbox)->SetMsgDatabase(nullptr);
+ (void) localInbox->SetCheckForNewMessagesAfterParsing(true);
+ // this will cause a reparse of the mail folder.
+ localInbox->GetDatabaseWithReparse(nullptr, msgWindow, getter_AddRefs(db));
+ rv = NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsPop3IncomingServer::PerformBiff(nsIMsgWindow *aMsgWindow)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPop3Service> pop3Service(do_GetService(kCPop3ServiceCID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolder> inbox;
+ nsCOMPtr<nsIMsgFolder> rootMsgFolder;
+ nsCOMPtr<nsIUrlListener> urlListener;
+ rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder));
+ NS_ENSURE_TRUE(rootMsgFolder, NS_ERROR_FAILURE);
+
+ rootMsgFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox,
+ getter_AddRefs(inbox));
+ if (!inbox)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr <nsIMsgIncomingServer> server;
+ inbox->GetServer(getter_AddRefs(server));
+
+ server->SetPerformingBiff(true);
+
+ urlListener = do_QueryInterface(inbox);
+
+ bool downloadOnBiff = false;
+ rv = GetDownloadOnBiff(&downloadOnBiff);
+ if (downloadOnBiff)
+ {
+ nsCOMPtr <nsIMsgLocalMailFolder> localInbox = do_QueryInterface(inbox, &rv);
+ if (localInbox && NS_SUCCEEDED(rv))
+ {
+ bool valid = false;
+ nsCOMPtr <nsIMsgDatabase> db;
+ rv = inbox->GetMsgDatabase(getter_AddRefs(db));
+ if (NS_SUCCEEDED(rv) && db)
+ rv = db->GetSummaryValid(&valid);
+ if (NS_SUCCEEDED(rv) && valid)
+ rv = pop3Service->GetNewMail(aMsgWindow, urlListener, inbox, this, nullptr);
+ else
+ {
+ bool isLocked;
+ inbox->GetLocked(&isLocked);
+ if (!isLocked)
+ rv = localInbox->GetDatabaseWithReparse(urlListener, aMsgWindow, getter_AddRefs(db));
+ if (NS_SUCCEEDED(rv))
+ rv = localInbox->SetCheckForNewMessagesAfterParsing(true);
+ }
+ }
+ }
+ else
+ rv = pop3Service->CheckForNewMail(aMsgWindow, urlListener, inbox, this, nullptr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3IncomingServer::SetFlagsOnDefaultMailboxes()
+{
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(rootFolder, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // pop3 gets an inbox, but no queue (unsent messages)
+ localFolder->SetFlagsOnDefaultMailboxes(nsMsgFolderFlags::SpecialUse &
+ ~nsMsgFolderFlags::Queue);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP nsPop3IncomingServer::CreateDefaultMailboxes()
+{
+ nsresult rv = CreateLocalFolder(NS_LITERAL_STRING("Inbox"));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CreateLocalFolder(NS_LITERAL_STRING("Trash"));
+}
+
+// override this so we can say that deferred accounts can't have messages
+// filed to them, which will remove them as targets of all the move/copy
+// menu items.
+NS_IMETHODIMP
+nsPop3IncomingServer::GetCanFileMessagesOnServer(bool *aCanFileMessagesOnServer)
+{
+ NS_ENSURE_ARG_POINTER(aCanFileMessagesOnServer);
+
+ nsCString deferredToAccount;
+ GetDeferredToAccount(deferredToAccount);
+ *aCanFileMessagesOnServer = deferredToAccount.IsEmpty();
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsPop3IncomingServer::GetCanCreateFoldersOnServer(bool *aCanCreateFoldersOnServer)
+{
+ NS_ENSURE_ARG_POINTER(aCanCreateFoldersOnServer);
+
+ nsCString deferredToAccount;
+ GetDeferredToAccount(deferredToAccount);
+ *aCanCreateFoldersOnServer = deferredToAccount.IsEmpty();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3IncomingServer::VerifyLogon(nsIUrlListener *aUrlListener,
+ nsIMsgWindow *aMsgWindow, nsIURI **aURL)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPop3Service> pop3Service = do_GetService(kCPop3ServiceCID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return pop3Service->VerifyLogon(this, aUrlListener, aMsgWindow, aURL);
+}
+
+NS_IMETHODIMP nsPop3IncomingServer::DownloadMailFromServers(nsIPop3IncomingServer** aServers,
+ uint32_t aCount,
+ nsIMsgWindow *aMsgWindow,
+ nsIMsgFolder *aFolder,
+ nsIUrlListener *aUrlListener)
+{
+ nsPop3GetMailChainer *getMailChainer = new nsPop3GetMailChainer;
+ NS_ENSURE_TRUE(getMailChainer, NS_ERROR_OUT_OF_MEMORY);
+ getMailChainer->AddRef(); // this object owns itself and releases when done.
+ return getMailChainer->GetNewMailForServers(aServers, aCount, aMsgWindow, aFolder, aUrlListener);
+}
+
+NS_IMETHODIMP nsPop3IncomingServer::GetNewMail(nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aUrlListener,
+ nsIMsgFolder *aInbox,
+ nsIURI **aResult)
+{
+ nsresult rv;
+ nsCOMPtr<nsIPop3Service> pop3Service = do_GetService(kCPop3ServiceCID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return pop3Service->GetNewMail(aMsgWindow, aUrlListener, aInbox, this, aResult);
+}
+
+// user has clicked get new messages on this server. If other servers defer to this server,
+// we need to get new mail for them. But if this server defers to an other server,
+// I think we only get new mail for this server.
+NS_IMETHODIMP
+nsPop3IncomingServer::GetNewMessages(nsIMsgFolder *aFolder, nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aUrlListener)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIPop3Service> pop3Service = do_GetService(kCPop3ServiceCID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr <nsIMsgFolder> inbox;
+ rv = GetInbox(aMsgWindow, getter_AddRefs(inbox));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr <nsIURI> url;
+ nsCOMPtr <nsIMsgIncomingServer> server;
+ nsCOMArray<nsIPop3IncomingServer> deferredServers;
+ nsCString deferredToAccount;
+ GetDeferredToAccount(deferredToAccount);
+
+ if (deferredToAccount.IsEmpty())
+ {
+ aFolder->GetServer(getter_AddRefs(server));
+ GetDeferredServers(server, deferredServers);
+ }
+ if (deferredToAccount.IsEmpty() && !deferredServers.IsEmpty())
+ {
+ nsPop3GetMailChainer *getMailChainer = new nsPop3GetMailChainer;
+ NS_ENSURE_TRUE(getMailChainer, NS_ERROR_OUT_OF_MEMORY);
+ getMailChainer->AddRef(); // this object owns itself and releases when done.
+ deferredServers.InsertElementAt(0, this);
+ return getMailChainer->GetNewMailForServers(deferredServers.Elements(),
+ deferredServers.Length(), aMsgWindow, inbox, aUrlListener);
+ }
+ if (m_runningProtocol)
+ return NS_MSG_FOLDER_BUSY;
+
+ return pop3Service->GetNewMail(aMsgWindow, aUrlListener, inbox, this, getter_AddRefs(url));
+}
+
+NS_IMETHODIMP
+nsPop3IncomingServer::GetDownloadMessagesAtStartup(bool *getMessagesAtStartup)
+{
+ NS_ENSURE_ARG_POINTER(getMessagesAtStartup);
+ // GetMessages is not automatically done for pop servers at startup.
+ // We need to trigger that action. Return true.
+ *getMessagesAtStartup = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3IncomingServer::GetCanBeDefaultServer(bool *canBeDefaultServer)
+{
+ NS_ENSURE_ARG_POINTER(canBeDefaultServer);
+ *canBeDefaultServer = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3IncomingServer::GetCanSearchMessages(bool *canSearchMessages)
+{
+ // this will return false if this server is deferred, which is what we want.
+ return GetCanFileMessagesOnServer(canSearchMessages);
+}
+
+NS_IMETHODIMP
+nsPop3IncomingServer::CloseCachedConnections()
+{
+ nsCOMPtr<nsIRequest> channel = do_QueryInterface(m_runningProtocol);
+ if (channel)
+ channel->Cancel(NS_ERROR_ABORT);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3IncomingServer::GetOfflineSupportLevel(int32_t *aSupportLevel)
+{
+ NS_ENSURE_ARG_POINTER(aSupportLevel);
+
+ nsresult rv;
+ rv = GetIntValue("offline_support_level", aSupportLevel);
+ if (*aSupportLevel != OFFLINE_SUPPORT_LEVEL_UNDEFINED) return rv;
+
+ // set default value
+ *aSupportLevel = OFFLINE_SUPPORT_LEVEL_NONE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3IncomingServer::SetRunningProtocol(nsIPop3Protocol *aProtocol)
+{
+ NS_ASSERTION(!aProtocol || !m_runningProtocol, "overriding running protocol");
+ m_runningProtocol = aProtocol;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3IncomingServer::GetRunningProtocol(nsIPop3Protocol **aProtocol)
+{
+ NS_ENSURE_ARG_POINTER(aProtocol);
+ NS_IF_ADDREF(*aProtocol = m_runningProtocol);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3IncomingServer::AddUidlToMark(const char *aUidl, int32_t aMark)
+{
+ NS_ENSURE_ARG_POINTER(aUidl);
+
+ Pop3UidlEntry *uidlEntry = PR_NEWZAP(Pop3UidlEntry);
+ NS_ENSURE_TRUE(uidlEntry, NS_ERROR_OUT_OF_MEMORY);
+
+ uidlEntry->uidl = strdup(aUidl);
+ if (MOZ_UNLIKELY(!uidlEntry->uidl)) {
+ PR_Free(uidlEntry);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ uidlEntry->status = (aMark == POP3_DELETE) ? DELETE_CHAR :
+ (aMark == POP3_FETCH_BODY) ? FETCH_BODY : KEEP;
+ m_uidlsToMark.AppendElement(uidlEntry);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3IncomingServer::MarkMessages()
+{
+ nsresult rv;
+ if (m_runningProtocol)
+ rv = m_runningProtocol->MarkMessages(&m_uidlsToMark);
+ else
+ {
+ nsCString hostName;
+ nsCString userName;
+ nsCOMPtr<nsIFile> localPath;
+
+ GetLocalPath(getter_AddRefs(localPath));
+
+ GetHostName(hostName);
+ GetUsername(userName);
+ // do it all in one fell swoop
+ rv = nsPop3Protocol::MarkMsgForHost(hostName.get(), userName.get(), localPath, m_uidlsToMark);
+ }
+ uint32_t count = m_uidlsToMark.Length();
+ for (uint32_t i = 0; i < count; i++)
+ {
+ Pop3UidlEntry *ue = m_uidlsToMark[i];
+ PR_Free(ue->uidl);
+ PR_Free(ue);
+ }
+ m_uidlsToMark.Clear();
+ return rv;
+}
+
+NS_IMPL_ISUPPORTS(nsPop3GetMailChainer, nsIUrlListener)
+
+nsPop3GetMailChainer::nsPop3GetMailChainer()
+{
+}
+nsPop3GetMailChainer::~nsPop3GetMailChainer()
+{
+}
+
+nsresult nsPop3GetMailChainer::GetNewMailForServers(nsIPop3IncomingServer** servers,
+ uint32_t count, nsIMsgWindow *msgWindow,
+ nsIMsgFolder *folderToDownloadTo, nsIUrlListener *listener)
+{
+ NS_ENSURE_ARG_POINTER(folderToDownloadTo);
+
+ m_serversToGetNewMailFor.AppendElements(servers, count);
+ m_folderToDownloadTo = folderToDownloadTo;
+ m_downloadingMsgWindow = msgWindow;
+ m_listener = listener;
+ nsCOMPtr <nsIMsgDatabase> destFolderDB;
+
+ nsresult rv = folderToDownloadTo->GetMsgDatabase(getter_AddRefs(destFolderDB));
+ if (NS_FAILED(rv) || !destFolderDB)
+ {
+ nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(folderToDownloadTo);
+ if (localFolder)
+ {
+ localFolder->GetDatabaseWithReparse(this, msgWindow, getter_AddRefs(destFolderDB));
+ return NS_OK;
+ }
+ }
+ return RunNextGetNewMail();
+}
+
+NS_IMETHODIMP
+nsPop3GetMailChainer::OnStartRunningUrl(nsIURI *url)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3GetMailChainer::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode)
+{
+ return RunNextGetNewMail();
+}
+
+nsresult nsPop3GetMailChainer::RunNextGetNewMail()
+{
+ nsresult rv;
+ uint32_t numServersLeft = m_serversToGetNewMailFor.Count();
+
+ for (; numServersLeft > 0;)
+ {
+ nsCOMPtr<nsIPop3IncomingServer> popServer(m_serversToGetNewMailFor[0]);
+ m_serversToGetNewMailFor.RemoveObjectAt(0);
+ numServersLeft--;
+ if (popServer)
+ {
+ bool deferGetNewMail = false;
+ nsCOMPtr <nsIMsgIncomingServer> downloadingToServer;
+ m_folderToDownloadTo->GetServer(getter_AddRefs(downloadingToServer));
+ popServer->GetDeferGetNewMail(&deferGetNewMail);
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(popServer);
+ nsCOMPtr<nsIPop3Protocol> protocol;
+ popServer->GetRunningProtocol(getter_AddRefs(protocol));
+ if ((deferGetNewMail || downloadingToServer == server) && !protocol)
+ {
+ // have to call routine that just gets mail for one server,
+ // and ignores deferred servers...
+ if (server)
+ {
+ nsCOMPtr <nsIURI> url;
+ nsCOMPtr<nsIPop3Service> pop3Service = do_GetService(kCPop3ServiceCID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+ return pop3Service->GetNewMail(m_downloadingMsgWindow, this, m_folderToDownloadTo, popServer, getter_AddRefs(url));
+ }
+ }
+ }
+ }
+ rv = m_listener ? m_listener->OnStopRunningUrl(nullptr, NS_OK) : NS_OK;
+ Release(); // release ref to ourself.
+ return rv;
+}
diff --git a/mailnews/local/src/nsPop3IncomingServer.h b/mailnews/local/src/nsPop3IncomingServer.h
new file mode 100644
index 000000000..3c98f3bd2
--- /dev/null
+++ b/mailnews/local/src/nsPop3IncomingServer.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef __nsPop3IncomingServer_h
+#define __nsPop3IncomingServer_h
+
+#include "mozilla/Attributes.h"
+#include "msgCore.h"
+#include "nsIPop3IncomingServer.h"
+#include "nsILocalMailIncomingServer.h"
+#include "nsMsgIncomingServer.h"
+#include "nsIPop3Protocol.h"
+#include "nsIMsgWindow.h"
+#include "nsMailboxServer.h"
+
+/* get some implementation from nsMsgIncomingServer */
+class nsPop3IncomingServer : public nsMailboxServer,
+ public nsIPop3IncomingServer,
+ public nsILocalMailIncomingServer
+
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIPOP3INCOMINGSERVER
+ NS_DECL_NSILOCALMAILINCOMINGSERVER
+
+ nsPop3IncomingServer();
+
+ NS_IMETHOD PerformBiff(nsIMsgWindow *aMsgWindow) override;
+ NS_IMETHOD GetDownloadMessagesAtStartup(bool *getMessages) override;
+ NS_IMETHOD GetCanBeDefaultServer(bool *canBeDefaultServer) override;
+ NS_IMETHOD GetCanSearchMessages(bool *canSearchMessages) override;
+ NS_IMETHOD GetOfflineSupportLevel(int32_t *aSupportLevel) override;
+ NS_IMETHOD CloseCachedConnections() override;
+ NS_IMETHOD GetRootMsgFolder(nsIMsgFolder **aRootMsgFolder) override;
+ NS_IMETHOD GetCanFileMessagesOnServer(bool *aCanFileMessagesOnServer) override;
+ NS_IMETHOD GetCanCreateFoldersOnServer(bool *aCanCreateFoldersOnServer) override;
+ NS_IMETHOD VerifyLogon(nsIUrlListener *aUrlListener, nsIMsgWindow *aMsgWindow,
+ nsIURI **aURL) override;
+ NS_IMETHOD GetNewMessages(nsIMsgFolder *aFolder, nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aUrlListener) override;
+
+protected:
+ virtual ~nsPop3IncomingServer();
+ nsresult GetInbox(nsIMsgWindow *msgWindow, nsIMsgFolder **inbox);
+
+private:
+ uint32_t m_capabilityFlags;
+ bool m_authenticated;
+ nsCOMPtr <nsIPop3Protocol> m_runningProtocol;
+ nsCOMPtr <nsIMsgFolder> m_rootMsgFolder;
+ nsTArray<Pop3UidlEntry*> m_uidlsToMark;
+};
+
+#endif
diff --git a/mailnews/local/src/nsPop3Protocol.cpp b/mailnews/local/src/nsPop3Protocol.cpp
new file mode 100644
index 000000000..825f45ab3
--- /dev/null
+++ b/mailnews/local/src/nsPop3Protocol.cpp
@@ -0,0 +1,4176 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This Original Code has been modified by IBM Corporation. Modifications made by IBM
+ * described herein are Copyright (c) International Business Machines Corporation, 2000.
+ * Modifications to Mozilla code or documentation identified per MPL Section 3.3
+ *
+ * Jason Eager <jce2@po.cwru.edu>
+ *
+ * Date Modified by Description of modification
+ * 04/20/2000 IBM Corp. OS/2 VisualAge build.
+ * 06/07/2000 Jason Eager Added check for out of disk space
+ */
+
+#include "nscore.h"
+#include "msgCore.h" // precompiled header...
+#include "nsNetUtil.h"
+#include "nspr.h"
+#include "plbase64.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsISafeOutputStream.h"
+#include "nsPop3Protocol.h"
+#include "MailNewsTypes.h"
+#include "nsStringGlue.h"
+#include "nsIPrompt.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsIMsgPluggableStore.h"
+#include "nsTextFormatter.h"
+#include "nsCOMPtr.h"
+#include "nsIMsgWindow.h"
+#include "nsIMsgFolder.h" // TO include biffState enum. Change to bool later...
+#include "nsIMsgLocalMailFolder.h"
+#include "nsIDocShell.h"
+#include "nsMsgUtils.h"
+#include "nsISocketTransport.h"
+#include "nsISSLSocketControl.h"
+#include "nsILineInputStream.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsMsgMessageFlags.h"
+#include "nsMsgBaseCID.h"
+#include "nsIProxyInfo.h"
+#include "nsCRT.h"
+#include "mozilla/Services.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Attributes.h"
+
+using namespace mozilla;
+
+PRLogModuleInfo *POP3LOGMODULE = nullptr;
+#define POP3LOG(str) "%s: [this=%p] " str, POP3LOGMODULE->name, this
+
+static int
+net_pop3_remove_messages_marked_delete(PLHashEntry* he,
+ int msgindex,
+ void *arg)
+{
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ return (uidlEntry->status == DELETE_CHAR)
+ ? HT_ENUMERATE_REMOVE : HT_ENUMERATE_NEXT;
+}
+
+uint32_t TimeInSecondsFromPRTime(PRTime prTime)
+{
+ return (uint32_t)(prTime / PR_USEC_PER_SEC);
+}
+
+static void
+put_hash(PLHashTable* table, const char* key, char value, uint32_t dateReceived)
+{
+ // don't put not used slots or empty uid into hash
+ if (key && *key)
+ {
+ Pop3UidlEntry* tmp = PR_NEWZAP(Pop3UidlEntry);
+ if (tmp)
+ {
+ tmp->uidl = PL_strdup(key);
+ if (tmp->uidl)
+ {
+ tmp->dateReceived = dateReceived;
+ tmp->status = value;
+ PL_HashTableAdd(table, (const void *)tmp->uidl, (void*) tmp);
+ }
+ else
+ PR_Free(tmp);
+ }
+ }
+}
+
+static int
+net_pop3_copy_hash_entries(PLHashEntry* he, int msgindex, void *arg)
+{
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ put_hash((PLHashTable *) arg, uidlEntry->uidl, uidlEntry->status, uidlEntry->dateReceived);
+ return HT_ENUMERATE_NEXT;
+}
+
+static void *
+AllocUidlTable(void * /* pool */, size_t size)
+{
+ return PR_MALLOC(size);
+}
+
+static void
+FreeUidlTable(void * /* pool */, void *item)
+{
+ PR_Free(item);
+}
+
+static PLHashEntry *
+AllocUidlInfo(void *pool, const void *key)
+{
+ return PR_NEWZAP(PLHashEntry);
+}
+
+static void
+FreeUidlInfo(void * /* pool */, PLHashEntry *he, unsigned flag)
+{
+ if (flag == HT_FREE_ENTRY)
+ {
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ if (uidlEntry)
+ {
+ PR_Free(uidlEntry->uidl);
+ PR_Free(uidlEntry);
+ }
+ PR_Free(he);
+ }
+}
+
+static PLHashAllocOps gHashAllocOps = {
+ AllocUidlTable, FreeUidlTable,
+ AllocUidlInfo, FreeUidlInfo
+};
+
+
+static Pop3UidlHost*
+net_pop3_load_state(const char* searchhost,
+ const char* searchuser,
+ nsIFile *mailDirectory)
+{
+ Pop3UidlHost* result = nullptr;
+ Pop3UidlHost* current = nullptr;
+ Pop3UidlHost* tmp;
+
+ result = PR_NEWZAP(Pop3UidlHost);
+ if (!result)
+ return nullptr;
+ result->host = PL_strdup(searchhost);
+ result->user = PL_strdup(searchuser);
+ result->hash = PL_NewHashTable(20, PL_HashString, PL_CompareStrings, PL_CompareValues, &gHashAllocOps, nullptr);
+
+ if (!result->host || !result->user || !result->hash)
+ {
+ PR_Free(result->host);
+ PR_Free(result->user);
+ if (result->hash)
+ PL_HashTableDestroy(result->hash);
+ PR_Free(result);
+ return nullptr;
+ }
+
+ nsCOMPtr <nsIFile> popState;
+ mailDirectory->Clone(getter_AddRefs(popState));
+ if (!popState)
+ return nullptr;
+ popState->AppendNative(NS_LITERAL_CSTRING("popstate.dat"));
+
+ nsCOMPtr<nsIInputStream> fileStream;
+ nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(fileStream), popState);
+ NS_ENSURE_SUCCESS(rv, result);
+
+ nsCOMPtr<nsILineInputStream> lineInputStream(do_QueryInterface(fileStream, &rv));
+ NS_ENSURE_SUCCESS(rv, result);
+
+ bool more = true;
+ nsCString line;
+
+ while (more && NS_SUCCEEDED(rv))
+ {
+ lineInputStream->ReadLine(line, &more);
+ if (line.IsEmpty())
+ continue;
+ char firstChar = line.CharAt(0);
+ if (firstChar == '#')
+ continue;
+ if (firstChar == '*') {
+ /* It's a host&user line. */
+ current = nullptr;
+ char *lineBuf = line.BeginWriting() + 1; // ok because we know the line isn't empty
+ char *host = NS_strtok(" \t\r\n", &lineBuf);
+ /* without space to also get realnames - see bug 225332 */
+ char *user = NS_strtok("\t\r\n", &lineBuf);
+ if (!host || !user)
+ continue;
+ for (tmp = result ; tmp ; tmp = tmp->next)
+ {
+ if (!strcmp(host, tmp->host) && !strcmp(user, tmp->user))
+ {
+ current = tmp;
+ break;
+ }
+ }
+ if (!current)
+ {
+ current = PR_NEWZAP(Pop3UidlHost);
+ if (current)
+ {
+ current->host = strdup(host);
+ current->user = strdup(user);
+ current->hash = PL_NewHashTable(20, PL_HashString, PL_CompareStrings, PL_CompareValues, &gHashAllocOps, nullptr);
+ if (!current->host || !current->user || !current->hash)
+ {
+ PR_Free(current->host);
+ PR_Free(current->user);
+ if (current->hash)
+ PL_HashTableDestroy(current->hash);
+ PR_Free(current);
+ }
+ else
+ {
+ current->next = result->next;
+ result->next = current;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* It's a line with a UIDL on it. */
+ if (current)
+ {
+ for (int32_t pos = line.FindChar('\t'); pos != -1; pos = line.FindChar('\t', pos))
+ line.Replace(pos, 1, ' ');
+
+ nsTArray<nsCString> lineElems;
+ ParseString(line, ' ', lineElems);
+ if (lineElems.Length() < 2)
+ continue;
+ nsCString *flags = &lineElems[0];
+ nsCString *uidl = &lineElems[1];
+ uint32_t dateReceived = TimeInSecondsFromPRTime(PR_Now()); // if we don't find a date str, assume now.
+ if (lineElems.Length() > 2)
+ dateReceived = atoi(lineElems[2].get());
+ if (!flags->IsEmpty() && !uidl->IsEmpty())
+ {
+ char flag = flags->CharAt(0);
+ if ((flag == KEEP) || (flag == DELETE_CHAR) ||
+ (flag == TOO_BIG) || (flag == FETCH_BODY))
+ {
+ put_hash(current->hash, uidl->get(), flag, dateReceived);
+ }
+ else
+ {
+ NS_ASSERTION(false, "invalid flag in popstate.dat");
+ }
+ }
+ }
+ }
+ }
+ fileStream->Close();
+
+ return result;
+}
+
+static int
+hash_clear_mapper(PLHashEntry* he, int msgindex, void* arg)
+{
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ PR_Free(uidlEntry->uidl);
+ PR_Free(uidlEntry);
+ he->value = nullptr;
+
+ return HT_ENUMERATE_REMOVE;
+}
+
+static int
+hash_empty_mapper(PLHashEntry* he, int msgindex, void* arg)
+{
+ *((bool*) arg) = false;
+ return HT_ENUMERATE_STOP;
+}
+
+static bool
+hash_empty(PLHashTable* hash)
+{
+ bool result = true;
+ PL_HashTableEnumerateEntries(hash, hash_empty_mapper, (void *)&result);
+ return result;
+}
+
+
+static int
+net_pop3_write_mapper(PLHashEntry* he, int msgindex, void* arg)
+{
+ nsIOutputStream* file = (nsIOutputStream*) arg;
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ NS_ASSERTION((uidlEntry->status == KEEP) ||
+ (uidlEntry->status == DELETE_CHAR) ||
+ (uidlEntry->status == FETCH_BODY) ||
+ (uidlEntry->status == TOO_BIG), "invalid status");
+ char* tmpBuffer = PR_smprintf("%c %s %d" MSG_LINEBREAK, uidlEntry->status, (char*)
+ uidlEntry->uidl, uidlEntry->dateReceived);
+ PR_ASSERT(tmpBuffer);
+ uint32_t numBytesWritten;
+ file->Write(tmpBuffer, strlen(tmpBuffer), &numBytesWritten);
+ PR_Free(tmpBuffer);
+ return HT_ENUMERATE_NEXT;
+}
+
+static int
+net_pop3_delete_old_msgs_mapper(PLHashEntry* he, int msgindex, void* arg)
+{
+ PRTime cutOffDate = (PRTime) arg;
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) he->value;
+ if (uidlEntry->dateReceived < cutOffDate)
+ uidlEntry->status = DELETE_CHAR; // mark for deletion
+ return HT_ENUMERATE_NEXT;
+}
+
+static void
+net_pop3_write_state(Pop3UidlHost* host, nsIFile *mailDirectory)
+{
+ int32_t len = 0;
+ nsCOMPtr <nsIFile> popState;
+
+ mailDirectory->Clone(getter_AddRefs(popState));
+ if (!popState)
+ return;
+ popState->AppendNative(NS_LITERAL_CSTRING("popstate.dat"));
+
+ nsCOMPtr<nsIOutputStream> fileOutputStream;
+ nsresult rv = MsgNewSafeBufferedFileOutputStream(getter_AddRefs(fileOutputStream), popState, -1, 00600);
+ if (NS_FAILED(rv))
+ return;
+
+ const char tmpBuffer[] =
+ "# POP3 State File" MSG_LINEBREAK
+ "# This is a generated file! Do not edit." MSG_LINEBREAK
+ MSG_LINEBREAK;
+
+ uint32_t numBytesWritten;
+ fileOutputStream->Write(tmpBuffer, strlen(tmpBuffer), &numBytesWritten);
+
+ for (; host && (len >= 0); host = host->next)
+ {
+ if (!hash_empty(host->hash))
+ {
+ fileOutputStream->Write("*", 1, &numBytesWritten);
+ fileOutputStream->Write(host->host, strlen(host->host), &numBytesWritten);
+ fileOutputStream->Write(" ", 1, &numBytesWritten);
+ fileOutputStream->Write(host->user, strlen(host->user), &numBytesWritten);
+ fileOutputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &numBytesWritten);
+ PL_HashTableEnumerateEntries(host->hash, net_pop3_write_mapper, (void *)fileOutputStream);
+ }
+ }
+ nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(fileOutputStream);
+ NS_ASSERTION(safeStream, "expected a safe output stream!");
+ if (safeStream) {
+ rv = safeStream->Finish();
+ if (NS_FAILED(rv)) {
+ NS_WARNING("failed to save pop state! possible data loss");
+ }
+ }
+}
+
+static void
+net_pop3_free_state(Pop3UidlHost* host)
+{
+ Pop3UidlHost* h;
+ while (host)
+ {
+ h = host->next;
+ PR_Free(host->host);
+ PR_Free(host->user);
+ PL_HashTableDestroy(host->hash);
+ PR_Free(host);
+ host = h;
+ }
+}
+
+/*
+Look for a specific UIDL string in our hash tables, if we have it then we need
+to mark the message for deletion so that it can be deleted later. If the uidl of the
+message is not found, then the message was downloaded completely and already deleted
+from the server. So this only applies to messages kept on the server or too big
+for download. */
+/* static */
+void nsPop3Protocol::MarkMsgInHashTable(PLHashTable *hashTable, const Pop3UidlEntry *uidlE, bool *changed)
+{
+ if (uidlE->uidl)
+ {
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(hashTable, uidlE->uidl);
+ if (uidlEntry)
+ {
+ if (uidlEntry->status != uidlE->status)
+ {
+ uidlEntry->status = uidlE->status;
+ *changed = true;
+ }
+ }
+ }
+}
+
+/* static */
+nsresult
+nsPop3Protocol::MarkMsgForHost(const char *hostName, const char *userName,
+ nsIFile *mailDirectory,
+ nsTArray<Pop3UidlEntry*> &UIDLArray)
+{
+ if (!hostName || !userName || !mailDirectory)
+ return NS_ERROR_NULL_POINTER;
+
+ Pop3UidlHost *uidlHost = net_pop3_load_state(hostName, userName, mailDirectory);
+ if (!uidlHost)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ bool changed = false;
+
+ uint32_t count = UIDLArray.Length();
+ for (uint32_t i = 0; i < count; i++)
+ {
+ MarkMsgInHashTable(uidlHost->hash, UIDLArray[i], &changed);
+ }
+
+ if (changed)
+ net_pop3_write_state(uidlHost, mailDirectory);
+ net_pop3_free_state(uidlHost);
+ return NS_OK;
+}
+
+
+
+NS_IMPL_ADDREF_INHERITED(nsPop3Protocol, nsMsgProtocol)
+NS_IMPL_RELEASE_INHERITED(nsPop3Protocol, nsMsgProtocol)
+
+
+
+NS_INTERFACE_MAP_BEGIN(nsPop3Protocol)
+ NS_INTERFACE_MAP_ENTRY(nsIPop3Protocol)
+ NS_INTERFACE_MAP_ENTRY(nsIMsgAsyncPromptListener)
+NS_INTERFACE_MAP_END_INHERITING(nsMsgProtocol)
+
+// nsPop3Protocol class implementation
+
+nsPop3Protocol::nsPop3Protocol(nsIURI* aURL)
+: nsMsgProtocol(aURL),
+ m_bytesInMsgReceived(0),
+ m_totalFolderSize(0),
+ m_totalDownloadSize(0),
+ m_totalBytesReceived(0),
+ m_lineStreamBuffer(nullptr),
+ m_pop3ConData(nullptr)
+{
+}
+
+nsresult nsPop3Protocol::Initialize(nsIURI * aURL)
+{
+ nsresult rv = NS_OK;
+ if (!POP3LOGMODULE)
+ POP3LOGMODULE = PR_NewLogModule("POP3");
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Initialize()")));
+
+ m_pop3ConData = (Pop3ConData *)PR_NEWZAP(Pop3ConData);
+ if(!m_pop3ConData)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ m_totalBytesReceived = 0;
+ m_bytesInMsgReceived = 0;
+ m_totalFolderSize = 0;
+ m_totalDownloadSize = 0;
+ m_totalBytesReceived = 0;
+ m_tlsEnabled = false;
+ m_socketType = nsMsgSocketType::trySTARTTLS;
+ m_prefAuthMethods = POP3_AUTH_MECH_UNDEFINED;
+ m_failedAuthMethods = 0;
+ m_password_already_sent = false;
+ m_currentAuthMethod = POP3_AUTH_MECH_UNDEFINED;
+ m_needToRerunUrl = false;
+
+ if (aURL)
+ {
+ // extract out message feedback if there is any.
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(aURL);
+ if (mailnewsUrl)
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ mailnewsUrl->GetServer(getter_AddRefs(server));
+ NS_ENSURE_TRUE(server, NS_MSG_INVALID_OR_MISSING_SERVER);
+
+ rv = server->GetSocketType(&m_socketType);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ int32_t authMethod = 0;
+ rv = server->GetAuthMethod(&authMethod);
+ NS_ENSURE_SUCCESS(rv,rv);
+ InitPrefAuthMethods(authMethod);
+
+ m_pop3Server = do_QueryInterface(server);
+ if (m_pop3Server)
+ m_pop3Server->GetPop3CapabilityFlags(&m_pop3ConData->capability_flags);
+ }
+
+ m_url = do_QueryInterface(aURL);
+
+ // When we are making a secure connection, we need to make sure that we
+ // pass an interface requestor down to the socket transport so that PSM can
+ // retrieve a nsIPrompt instance if needed.
+ nsCOMPtr<nsIInterfaceRequestor> ir;
+ if (m_socketType != nsMsgSocketType::plain)
+ {
+ nsCOMPtr<nsIMsgWindow> msgwin;
+ mailnewsUrl->GetMsgWindow(getter_AddRefs(msgwin));
+ if (!msgwin)
+ GetTopmostMsgWindow(getter_AddRefs(msgwin));
+ if (msgwin)
+ {
+ nsCOMPtr<nsIDocShell> docshell;
+ msgwin->GetRootDocShell(getter_AddRefs(docshell));
+ ir = do_QueryInterface(docshell);
+ nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
+ msgwin->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
+ if (notificationCallbacks)
+ {
+ nsCOMPtr<nsIInterfaceRequestor> aggregrateIR;
+ MsgNewInterfaceRequestorAggregation(notificationCallbacks, ir, getter_AddRefs(aggregrateIR));
+ ir = aggregrateIR;
+ }
+ }
+ }
+
+ int32_t port = 0;
+ nsCString hostName;
+ aURL->GetPort(&port);
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server)
+ server->GetRealHostName(hostName);
+
+ nsCOMPtr<nsIProxyInfo> proxyInfo;
+ rv = MsgExamineForProxy(this, getter_AddRefs(proxyInfo));
+ if (NS_FAILED(rv)) proxyInfo = nullptr;
+
+ const char *connectionType = nullptr;
+ if (m_socketType == nsMsgSocketType::SSL)
+ connectionType = "ssl";
+ else if (m_socketType == nsMsgSocketType::trySTARTTLS ||
+ m_socketType == nsMsgSocketType::alwaysSTARTTLS)
+ connectionType = "starttls";
+
+ rv = OpenNetworkSocketWithInfo(hostName.get(), port, connectionType, proxyInfo, ir);
+ if (NS_FAILED(rv) && m_socketType == nsMsgSocketType::trySTARTTLS)
+ {
+ m_socketType = nsMsgSocketType::plain;
+ rv = OpenNetworkSocketWithInfo(hostName.get(), port, nullptr, proxyInfo, ir);
+ }
+
+ if(NS_FAILED(rv))
+ return rv;
+ } // if we got a url...
+
+ m_lineStreamBuffer = new nsMsgLineStreamBuffer(OUTPUT_BUFFER_SIZE, true);
+ if(!m_lineStreamBuffer)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
+ return bundleService->CreateBundle("chrome://messenger/locale/localMsgs.properties", getter_AddRefs(mLocalBundle));
+}
+
+nsPop3Protocol::~nsPop3Protocol()
+{
+ Cleanup();
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("~nsPop3Protocol()")));
+}
+
+void nsPop3Protocol::Cleanup()
+{
+ if (m_pop3ConData->newuidl)
+ {
+ PL_HashTableDestroy(m_pop3ConData->newuidl);
+ m_pop3ConData->newuidl = nullptr;
+ }
+
+ net_pop3_free_state(m_pop3ConData->uidlinfo);
+
+ FreeMsgInfo();
+ PR_Free(m_pop3ConData->only_uidl);
+ PR_Free(m_pop3ConData);
+
+ delete m_lineStreamBuffer;
+ m_lineStreamBuffer = nullptr;
+}
+
+void nsPop3Protocol::SetCapFlag(uint32_t flag)
+{
+ m_pop3ConData->capability_flags |= flag;
+}
+
+void nsPop3Protocol::ClearCapFlag(uint32_t flag)
+{
+ m_pop3ConData->capability_flags &= ~flag;
+}
+
+bool nsPop3Protocol::TestCapFlag(uint32_t flag)
+{
+ return m_pop3ConData->capability_flags & flag;
+}
+
+uint32_t nsPop3Protocol::GetCapFlags()
+{
+ return m_pop3ConData->capability_flags;
+}
+
+nsresult nsPop3Protocol::FormatCounterString(const nsString &stringName,
+ uint32_t count1,
+ uint32_t count2,
+ nsString &resultString)
+{
+ nsAutoString count1String;
+ count1String.AppendInt(count1);
+
+ nsAutoString count2String;
+ count2String.AppendInt(count2);
+
+ const char16_t *formatStrings[] = {
+ count1String.get(),
+ count2String.get()
+ };
+
+ return mLocalBundle->FormatStringFromName(stringName.get(),
+ formatStrings, 2,
+ getter_Copies(resultString));
+}
+
+void nsPop3Protocol::UpdateStatus(const char16_t *aStatusName)
+{
+ nsString statusMessage;
+ mLocalBundle->GetStringFromName(aStatusName,
+ getter_Copies(statusMessage));
+ UpdateStatusWithString(statusMessage.get());
+}
+
+void nsPop3Protocol::UpdateStatusWithString(const char16_t *aStatusString)
+{
+ if (mProgressEventSink)
+ {
+ mozilla::DebugOnly<nsresult> rv =
+ mProgressEventSink->OnStatus(this, m_channelContext,
+ NS_OK, aStatusString); // XXX i18n message
+ NS_ASSERTION(NS_SUCCEEDED(rv), "dropping error result");
+ }
+}
+
+void nsPop3Protocol::UpdateProgressPercent(int64_t totalDone, int64_t total)
+{
+ if (mProgressEventSink)
+ mProgressEventSink->OnProgress(this, m_channelContext, totalDone, total);
+}
+
+// note: SetUsername() expects an unescaped string
+// do not pass in an escaped string
+void nsPop3Protocol::SetUsername(const char* name)
+{
+ NS_ASSERTION(name, "no name specified!");
+ if (name)
+ m_username = name;
+}
+
+nsresult nsPop3Protocol::RerunUrl()
+{
+ nsCOMPtr<nsIURI> url = do_QueryInterface(m_url);
+ ClearFlag(POP3_PASSWORD_FAILED);
+ m_pop3Server->SetRunningProtocol(nullptr);
+ Cleanup();
+ return LoadUrl(url, nullptr);
+}
+
+Pop3StatesEnum nsPop3Protocol::GetNextPasswordObtainState()
+{
+ switch (m_pop3ConData->next_state)
+ {
+ case POP3_OBTAIN_PASSWORD_EARLY:
+ return POP3_FINISH_OBTAIN_PASSWORD_EARLY;
+ case POP3_SEND_USERNAME:
+ case POP3_OBTAIN_PASSWORD_BEFORE_USERNAME:
+ return POP3_FINISH_OBTAIN_PASSWORD_BEFORE_USERNAME;
+ case POP3_SEND_PASSWORD:
+ case POP3_OBTAIN_PASSWORD_BEFORE_PASSWORD:
+ return POP3_FINISH_OBTAIN_PASSWORD_BEFORE_PASSWORD;
+ default:
+ // Should never get here.
+ NS_NOTREACHED("Invalid next_state in GetNextPasswordObtainState");
+ }
+ return POP3_ERROR_DONE;
+}
+
+nsresult nsPop3Protocol::StartGetAsyncPassword(Pop3StatesEnum aNextState)
+{
+ nsresult rv;
+
+ // Try and avoid going async if possible - if we haven't got into a password
+ // failure state and the server has a password stored for this session, then
+ // use it.
+ if (!TestFlag(POP3_PASSWORD_FAILED))
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server =
+ do_QueryInterface(m_pop3Server, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = server->GetPassword(m_passwordResult);
+ if (NS_SUCCEEDED(rv) && !m_passwordResult.IsEmpty())
+ {
+ m_pop3ConData->next_state = GetNextPasswordObtainState();
+ return NS_OK;
+ }
+ }
+
+ // We're now going to need to do something that will end up with us either
+ // poking the login manger or prompting the user. We need to ensure we only
+ // do one prompt at a time (and loging manager could cause a master password
+ // prompt), so we need to use the async prompter.
+ nsCOMPtr<nsIMsgAsyncPrompter> asyncPrompter =
+ do_GetService(NS_MSGASYNCPROMPTER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ m_pop3ConData->next_state = aNextState;
+
+ // Although we're not actually pausing for a read, we'll do so anyway to let
+ // the async prompt run. Once it is our turn again we'll call back into
+ // ProcessProtocolState.
+ m_pop3ConData->pause_for_read = true;
+
+ nsCString server("unknown");
+ m_url->GetPrePath(server);
+
+ rv = asyncPrompter->QueueAsyncAuthPrompt(server, false, this);
+ // Explict NS_ENSURE_SUCCESS for debug purposes as errors tend to get
+ // hidden.
+ NS_ENSURE_SUCCESS(rv, rv);
+ return rv;
+}
+
+NS_IMETHODIMP nsPop3Protocol::OnPromptStart(bool *aResult)
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("OnPromptStart()")));
+
+ *aResult = false;
+
+ nsresult rv;
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString passwordResult;
+
+ // pass the failed password into the password prompt so that
+ // it will be pre-filled, in case it failed because of a
+ // server problem and not because it was wrong.
+ if (!m_lastPasswordSent.IsEmpty())
+ passwordResult = m_lastPasswordSent;
+
+ // Set up some items that we're going to need for the prompting.
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ if (mailnewsUrl)
+ mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
+
+ nsCString userName;
+ server->GetRealUsername(userName);
+
+ nsCString hostName;
+ server->GetRealHostName(hostName);
+
+ nsString passwordPrompt;
+ NS_ConvertUTF8toUTF16 userNameUTF16(userName);
+ NS_ConvertUTF8toUTF16 hostNameUTF16(hostName);
+ const char16_t* passwordParams[] = { userNameUTF16.get(),
+ hostNameUTF16.get() };
+
+ // if the last prompt got us a bad password then show a special dialog
+ if (TestFlag(POP3_PASSWORD_FAILED))
+ {
+ // Biff case (no msgWindow) shouldn't cause prompts or passwords to get forgotten at all
+ // TODO shouldn't we skip the new password prompt below as well for biff? Just exit here?
+ if (msgWindow)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Warning,
+ (POP3LOG("POP: ask user what to do (after password failed): new password, retry or cancel")));
+
+ int32_t buttonPressed = 0;
+ if (NS_SUCCEEDED(MsgPromptLoginFailed(msgWindow, hostName,
+ &buttonPressed)))
+ {
+ if (buttonPressed == 1) // Cancel button
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Warning, (POP3LOG("cancel button pressed")));
+ // Abort quickly and stop trying for now.
+
+ // If we haven't actually connected yet (i.e. we're doing an early
+ // attempt to get the username/password but we've previously failed
+ // for some reason), then skip straight to POP3_FREE as it isn't an
+ // error in this connection, and just ends up with us closing the
+ // socket and saying we've aborted the bind. Otherwise, pretend this
+ // is an error and move on.
+ m_pop3ConData->next_state =
+ m_pop3ConData->next_state == POP3_OBTAIN_PASSWORD_EARLY ?
+ POP3_FREE : POP3_ERROR_DONE;
+
+ // Clear the password we're going to return to force failure in
+ // the get mail instance.
+ passwordResult.Truncate();
+
+ // We also have to clear the password failed flag, otherwise we'll
+ // automatically try again.
+ ClearFlag(POP3_PASSWORD_FAILED);
+
+ // As we're async, calling ProcessProtocolState gets things going
+ // again.
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+ return NS_OK;
+ }
+ else if (buttonPressed == 2) // "New password" button
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Warning, (POP3LOG("new password button pressed")));
+ // Forget the stored password
+ // and we'll prompt for a new one next time around.
+ rv = server->ForgetPassword();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // try all methods again with new password
+ ResetAuthMethods();
+ // ... apart from GSSAPI, which doesn't care about passwords
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_GSSAPI);
+ if (m_needToRerunUrl)
+ return RerunUrl();
+ }
+ else if (buttonPressed == 0) // "Retry" button
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Warning, (POP3LOG("retry button pressed")));
+ // try all methods again, including GSSAPI
+ ResetAuthMethods();
+ ClearFlag(POP3_PASSWORD_FAILED|POP3_AUTH_FAILURE);
+
+ if (m_needToRerunUrl)
+ return RerunUrl();
+
+ // It is a bit strange that we're going onto the next state that
+ // would essentially send the password. However in resetting the
+ // auth methods above, we're setting up SendUsername, SendPassword
+ // and friends to abort and return to the POP3_SEND_CAPA state.
+ // Hence we can do this safely.
+ m_pop3ConData->next_state = GetNextPasswordObtainState();
+ // As we're async, calling ProcessProtocolState gets things going
+ // again.
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+ return NS_OK;
+ }
+ }
+ }
+ mLocalBundle->FormatStringFromName(
+ u"pop3PreviouslyEnteredPasswordIsInvalidPrompt",
+ passwordParams, 2, getter_Copies(passwordPrompt));
+ }
+ else
+ // Otherwise this is the first time we've asked about the server's
+ // password so show a first time prompt.
+ mLocalBundle->FormatStringFromName(
+ u"pop3EnterPasswordPrompt",
+ passwordParams, 2, getter_Copies(passwordPrompt));
+
+ nsString passwordTitle;
+ mLocalBundle->GetStringFromName(
+ u"pop3EnterPasswordPromptTitle",
+ getter_Copies(passwordTitle));
+
+ // Now go and get the password.
+ if (!passwordPrompt.IsEmpty() && !passwordTitle.IsEmpty())
+ rv = server->GetPasswordWithUI(passwordPrompt, passwordTitle,
+ msgWindow, passwordResult);
+ ClearFlag(POP3_PASSWORD_FAILED|POP3_AUTH_FAILURE);
+
+ // If it failed or the user cancelled the prompt, just abort the
+ // connection.
+ if (NS_FAILED(rv) ||
+ rv == NS_MSG_PASSWORD_PROMPT_CANCELLED)
+ {
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ m_passwordResult.Truncate();
+ *aResult = false;
+ }
+ else
+ {
+ m_passwordResult = passwordResult;
+ m_pop3ConData->next_state = GetNextPasswordObtainState();
+ *aResult = true;
+ }
+ // Because this was done asynchronously, now call back into
+ // ProcessProtocolState to get the protocol going again.
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Protocol::OnPromptAuthAvailable()
+{
+ NS_NOTREACHED("Did not expect to get POP3 protocol queuing up auth "
+ "connections for same server");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Protocol::OnPromptCanceled()
+{
+ // A prompt was cancelled, so just abort out the connection
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ // As we're async, calling ProcessProtocolState gets things going again.
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Protocol::OnTransportStatus(nsITransport *aTransport, nsresult aStatus, int64_t aProgress, int64_t aProgressMax)
+{
+ return nsMsgProtocol::OnTransportStatus(aTransport, aStatus, aProgress, aProgressMax);
+}
+
+// stop binding is a "notification" informing us that the stream associated with aURL is going away.
+NS_IMETHODIMP nsPop3Protocol::OnStopRequest(nsIRequest *aRequest, nsISupports * aContext, nsresult aStatus)
+{
+ // If the server dropped the connection, m_socketIsOpen will be true, before
+ // we call nsMsgProtocol::OnStopRequest. The call will force a close socket,
+ // but we still want to go through the state machine one more time to cleanup
+ // the protocol object.
+ if (m_socketIsOpen)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> msgUrl = do_QueryInterface(m_url);
+
+ // Check if the connection was dropped before getting back an auth error.
+ // If we got the auth error, the next state would be
+ // POP3_OBTAIN_PASSWORD_EARLY.
+ if ((m_pop3ConData->next_state_after_response == POP3_NEXT_AUTH_STEP ||
+ m_pop3ConData->next_state_after_response == POP3_AUTH_LOGIN_RESPONSE) &&
+ m_pop3ConData->next_state != POP3_OBTAIN_PASSWORD_EARLY)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("dropped connection before auth error")));
+ SetFlag(POP3_AUTH_FAILURE);
+ m_pop3ConData->command_succeeded = false;
+ m_needToRerunUrl = true;
+ m_pop3ConData->next_state = POP3_NEXT_AUTH_STEP;
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+ }
+ // We can't call nsMsgProtocol::OnStopRequest because it calls SetUrlState,
+ // which notifies the URLListeners, but we need to do a bit of cleanup
+ // before running the url again.
+ CloseSocket();
+ if (m_loadGroup)
+ m_loadGroup->RemoveRequest(static_cast<nsIRequest *>(this), nullptr, aStatus);
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ ProcessProtocolState(nullptr, nullptr, 0, 0);
+
+ if (NS_FAILED(aStatus) && aStatus != NS_BINDING_ABORTED)
+ nsMsgProtocol::ShowAlertMessage(msgUrl, aStatus);
+
+ return NS_OK;
+ }
+ nsresult rv = nsMsgProtocol::OnStopRequest(aRequest, aContext, aStatus);
+
+ // turn off the server busy flag on stop request - we know we're done, right?
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("Clearing server busy in nsPop3Protocol::OnStopRequest")));
+ server->SetServerBusy(false); // the server is not busy
+ }
+ if(m_pop3ConData->list_done)
+ CommitState(true);
+ if (NS_FAILED(aStatus) && aStatus != NS_BINDING_ABORTED)
+ Abort();
+ return rv;
+}
+
+void nsPop3Protocol::Abort()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Abort")));
+
+ if(m_pop3ConData->msg_closure)
+ {
+ m_nsIPop3Sink->IncorporateAbort(m_pop3ConData->only_uidl != nullptr);
+ m_pop3ConData->msg_closure = nullptr;
+ }
+ // need this to close the stream on the inbox.
+ m_nsIPop3Sink->AbortMailDelivery(this);
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("Clearing running protocol in nsPop3Protocol::Abort()")));
+ m_pop3Server->SetRunningProtocol(nullptr);
+}
+
+NS_IMETHODIMP nsPop3Protocol::Cancel(nsresult status) // handle stop button
+{
+ Abort();
+ return nsMsgProtocol::Cancel(NS_BINDING_ABORTED);
+}
+
+
+nsresult nsPop3Protocol::LoadUrl(nsIURI* aURL, nsISupports * /* aConsumer */)
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("LoadUrl()")));
+
+ nsresult rv = Initialize(aURL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (aURL)
+ m_url = do_QueryInterface(aURL);
+ else
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIURL> url = do_QueryInterface(aURL, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ int32_t port;
+ rv = url->GetPort(&port);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_CheckPortSafety(port, "pop");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString queryPart;
+ rv = url->GetQuery(queryPart);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get the url spect");
+
+ m_pop3ConData->only_check_for_new_mail = (PL_strcasestr(queryPart.get(), "check") != nullptr);
+ m_pop3ConData->verify_logon = (PL_strcasestr(queryPart.get(), "verifyLogon") != nullptr);
+ m_pop3ConData->get_url = (PL_strcasestr(queryPart.get(), "gurl") != nullptr);
+
+ bool deleteByAgeFromServer = false;
+ int32_t numDaysToLeaveOnServer = -1;
+ if (!m_pop3ConData->verify_logon)
+ {
+ // Pick up pref setting regarding leave messages on server, message size limit
+
+ m_pop3Server->GetLeaveMessagesOnServer(&m_pop3ConData->leave_on_server);
+ m_pop3Server->GetHeadersOnly(&m_pop3ConData->headers_only);
+ bool limitMessageSize = false;
+
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server)
+ {
+ // size limits are superseded by headers_only mode
+ if (!m_pop3ConData->headers_only)
+ {
+ server->GetLimitOfflineMessageSize(&limitMessageSize);
+ if (limitMessageSize)
+ {
+ int32_t max_size = 0; // default size
+ server->GetMaxMessageSize(&max_size);
+ m_pop3ConData->size_limit = (max_size) ? max_size * 1024 : 50 * 1024;
+ }
+ }
+ m_pop3Server->GetDeleteByAgeFromServer(&deleteByAgeFromServer);
+ if (deleteByAgeFromServer)
+ m_pop3Server->GetNumDaysToLeaveOnServer(&numDaysToLeaveOnServer);
+ }
+ }
+
+ // UIDL stuff
+ nsCOMPtr<nsIPop3URL> pop3Url = do_QueryInterface(m_url);
+ if (pop3Url)
+ pop3Url->GetPop3Sink(getter_AddRefs(m_nsIPop3Sink));
+
+ nsCOMPtr<nsIFile> mailDirectory;
+
+ nsCString hostName;
+ nsCString userName;
+
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server)
+ {
+ rv = server->GetLocalPath(getter_AddRefs(mailDirectory));
+ NS_ENSURE_SUCCESS(rv, rv);
+ server->SetServerBusy(true); // the server is now busy
+ server->GetHostName(hostName);
+ server->GetUsername(userName);
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
+ (POP3LOG("Connecting to server %s:%d"), hostName.get(), port));
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("Setting server busy in nsPop3Protocol::LoadUrl()")));
+ }
+
+ if (!m_pop3ConData->verify_logon)
+ m_pop3ConData->uidlinfo = net_pop3_load_state(hostName.get(), userName.get(), mailDirectory);
+
+ m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_NoMail;
+
+ if (m_pop3ConData->uidlinfo && numDaysToLeaveOnServer > 0)
+ {
+ uint32_t nowInSeconds = TimeInSecondsFromPRTime(PR_Now());
+ uint32_t cutOffDay = nowInSeconds - (60 * 60 * 24 * numDaysToLeaveOnServer);
+
+ PL_HashTableEnumerateEntries(m_pop3ConData->uidlinfo->hash, net_pop3_delete_old_msgs_mapper, (void *)(uintptr_t) cutOffDay);
+ }
+ const char* uidl = PL_strcasestr(queryPart.get(), "uidl=");
+ PR_FREEIF(m_pop3ConData->only_uidl);
+
+ if (uidl)
+ {
+ uidl += 5;
+ nsCString unescapedData;
+ MsgUnescapeString(nsDependentCString(uidl), 0, unescapedData);
+ m_pop3ConData->only_uidl = PL_strdup(unescapedData.get());
+
+ mSuppressListenerNotifications = true; // suppress on start and on stop because this url won't have any content to display
+ }
+
+ m_pop3ConData->next_state = POP3_START_CONNECT;
+ m_pop3ConData->next_state_after_response = POP3_FINISH_CONNECT;
+ if (NS_SUCCEEDED(rv))
+ {
+ m_pop3Server->SetRunningProtocol(this);
+ return nsMsgProtocol::LoadUrl(aURL);
+ }
+ else
+ return rv;
+}
+
+void
+nsPop3Protocol::FreeMsgInfo()
+{
+ int i;
+ if (m_pop3ConData->msg_info)
+ {
+ for (i=0 ; i<m_pop3ConData->number_of_messages ; i++)
+ {
+ if (m_pop3ConData->msg_info[i].uidl)
+ PR_Free(m_pop3ConData->msg_info[i].uidl);
+ m_pop3ConData->msg_info[i].uidl = nullptr;
+ }
+ PR_Free(m_pop3ConData->msg_info);
+ m_pop3ConData->msg_info = nullptr;
+ }
+}
+
+int32_t
+nsPop3Protocol::WaitForStartOfConnectionResponse(nsIInputStream* aInputStream,
+ uint32_t length)
+{
+ char * line = nullptr;
+ uint32_t line_length = 0;
+ bool pauseForMoreData = false;
+ nsresult rv;
+ line = m_lineStreamBuffer->ReadNextLine(aInputStream, line_length, pauseForMoreData, &rv);
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+ if (NS_FAILED(rv))
+ return -1;
+
+ if(pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true; /* pause */
+ PR_Free(line);
+ return(line_length);
+ }
+
+ if(*line == '+')
+ {
+ m_pop3ConData->command_succeeded = true;
+ if(PL_strlen(line) > 4)
+ m_commandResponse = line + 4;
+ else
+ m_commandResponse = line;
+
+ if (m_prefAuthMethods & POP3_HAS_AUTH_APOP)
+ {
+ if (NS_SUCCEEDED(GetApopTimestamp()))
+ SetCapFlag(POP3_HAS_AUTH_APOP);
+ }
+ else
+ ClearCapFlag(POP3_HAS_AUTH_APOP);
+
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ m_pop3ConData->pause_for_read = false; /* don't pause */
+ }
+
+ PR_Free(line);
+ return(1); /* everything ok */
+}
+
+int32_t
+nsPop3Protocol::WaitForResponse(nsIInputStream* inputStream, uint32_t length)
+{
+ char * line;
+ uint32_t ln = 0;
+ bool pauseForMoreData = false;
+ nsresult rv;
+ line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if(pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true; /* pause */
+
+ PR_Free(line);
+ return(ln);
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ if(*line == '+')
+ {
+ m_pop3ConData->command_succeeded = true;
+ if(PL_strlen(line) > 4)
+ {
+ if(!PL_strncasecmp(line, "+OK", 3))
+ m_commandResponse = line + 4;
+ else // challenge answer to AUTH CRAM-MD5 and LOGIN username/password
+ m_commandResponse = line + 2;
+ }
+ else
+ m_commandResponse = line;
+ }
+ else
+ {
+ m_pop3ConData->command_succeeded = false;
+ if(PL_strlen(line) > 5)
+ m_commandResponse = line + 5;
+ else
+ m_commandResponse = line;
+
+ // search for the response codes (RFC 2449, chapter 8 and RFC 3206)
+ if(TestCapFlag(POP3_HAS_RESP_CODES | POP3_HAS_AUTH_RESP_CODE))
+ {
+ // code for authentication failure due to the user's credentials
+ if(m_commandResponse.Find("[AUTH", true) >= 0)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("setting auth failure")));
+ SetFlag(POP3_AUTH_FAILURE);
+ }
+
+ // codes for failures due to other reasons
+ if(m_commandResponse.Find("[LOGIN-DELAY", true) >= 0 ||
+ m_commandResponse.Find("[IN-USE", true) >= 0 ||
+ m_commandResponse.Find("[SYS", true) >= 0)
+ SetFlag(POP3_STOPLOGIN);
+
+ // remove the codes from the response string presented to the user
+ int32_t i = m_commandResponse.FindChar(']');
+ if(i >= 0)
+ m_commandResponse.Cut(0, i + 2);
+ }
+ }
+
+ m_pop3ConData->next_state = m_pop3ConData->next_state_after_response;
+ m_pop3ConData->pause_for_read = false; /* don't pause */
+
+ PR_Free(line);
+ return(1); /* everything ok */
+}
+
+int32_t
+nsPop3Protocol::Error(const char* err_code,
+ const char16_t **params,
+ uint32_t length)
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("ERROR: %s"), err_code));
+
+ // the error code is just the resource name for the error string...
+ // so print out that error message!
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ nsString accountName;
+ nsresult rv = server->GetPrettyName(accountName);
+ NS_ENSURE_SUCCESS(rv, -1);
+ const char16_t *titleParams[] = { accountName.get() };
+ nsString dialogTitle;
+ mLocalBundle->FormatStringFromName(
+ u"pop3ErrorDialogTitle",
+ titleParams, 1, getter_Copies(dialogTitle));
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
+ // we handle "pop3TmpDownloadError" earlier...
+ if (strcmp(err_code, "pop3TmpDownloadError") && NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ nsCOMPtr<nsIPrompt> dialog;
+ rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow)); //it is ok to have null msgWindow, for example when biffing
+ if (NS_SUCCEEDED(rv) && msgWindow)
+ {
+ rv = msgWindow->GetPromptDialog(getter_AddRefs(dialog));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsString alertString;
+ // Format the alert string if parameter list isn't empty
+ if (params)
+ mLocalBundle->FormatStringFromName(NS_ConvertASCIItoUTF16(err_code).get(),
+ params, length, getter_Copies(alertString));
+ else
+ mLocalBundle->GetStringFromName(NS_ConvertASCIItoUTF16(err_code).get(),
+ getter_Copies(alertString));
+ if (m_pop3ConData->command_succeeded) //not a server error message
+ dialog->Alert(dialogTitle.get(), alertString.get());
+ else
+ {
+ nsString serverSaidPrefix;
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ nsCString hostName;
+ // Fomat string with hostname.
+ if (server)
+ rv = server->GetRealHostName(hostName);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoString hostStr;
+ CopyASCIItoUTF16(hostName, hostStr);
+ const char16_t *params[] = { hostStr.get() };
+ mLocalBundle->FormatStringFromName(
+ u"pop3ServerSaid",
+ params, 1, getter_Copies(serverSaidPrefix));
+ }
+
+ nsAutoString message(alertString);
+ message.AppendLiteral(" ");
+ message.Append(serverSaidPrefix);
+ message.AppendLiteral(" ");
+ message.Append(NS_ConvertASCIItoUTF16(m_commandResponse));
+ dialog->Alert(dialogTitle.get(), message.get());
+ }
+ }
+ }
+ }
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ m_pop3ConData->pause_for_read = false;
+ return -1;
+}
+
+int32_t nsPop3Protocol::Pop3SendData(const char * dataBuffer, bool aSuppressLogging)
+{
+ // remove any leftover bytes in the line buffer
+ // this can happen if the last message line doesn't end with a (CR)LF
+ // or a server sent two reply lines
+ m_lineStreamBuffer->ClearBuffer();
+
+ nsresult result = nsMsgProtocol::SendData(dataBuffer);
+
+ if (!aSuppressLogging)
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("SEND: %s"), dataBuffer));
+ else
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
+ (POP3LOG("Logging suppressed for this command (it probably contained authentication information)")));
+
+ if (NS_SUCCEEDED(result))
+ {
+ m_pop3ConData->pause_for_read = true;
+ m_pop3ConData->next_state = POP3_WAIT_FOR_RESPONSE;
+ return 0;
+ }
+
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("Pop3SendData failed: %lx"), result));
+ return -1;
+}
+
+/*
+ * POP3 AUTH extension
+ */
+
+int32_t nsPop3Protocol::SendAuth()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendAuth()")));
+
+ if(!m_pop3ConData->command_succeeded)
+ return Error("pop3ServerError");
+
+ nsAutoCString command("AUTH" CRLF);
+
+ m_pop3ConData->next_state_after_response = POP3_AUTH_RESPONSE;
+ return Pop3SendData(command.get());
+}
+
+int32_t nsPop3Protocol::AuthResponse(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ char * line;
+ uint32_t ln = 0;
+ nsresult rv;
+
+ if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
+ {
+ ClearCapFlag(POP3_AUTH_MECH_UNDEFINED);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ if (!m_pop3ConData->command_succeeded)
+ {
+ /* AUTH command not implemented
+ * so no secure mechanisms available
+ */
+ m_pop3ConData->command_succeeded = true;
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ m_pop3ConData->next_state = POP3_SEND_CAPA;
+ return 0;
+ }
+
+ bool pauseForMoreData = false;
+ line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if(pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true; /* pause */
+ PR_Free(line);
+ return(0);
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ if (!PL_strcmp(line, "."))
+ {
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+
+ // now that we've read all the AUTH responses, go for it
+ m_pop3ConData->next_state = POP3_SEND_CAPA;
+ m_pop3ConData->pause_for_read = false; /* don't pause */
+ }
+ else if (!PL_strcasecmp (line, "CRAM-MD5"))
+ SetCapFlag(POP3_HAS_AUTH_CRAM_MD5);
+ else if (!PL_strcasecmp (line, "NTLM"))
+ SetCapFlag(POP3_HAS_AUTH_NTLM);
+ else if (!PL_strcasecmp (line, "MSN"))
+ SetCapFlag(POP3_HAS_AUTH_NTLM|POP3_HAS_AUTH_MSN);
+ else if (!PL_strcasecmp (line, "GSSAPI"))
+ SetCapFlag(POP3_HAS_AUTH_GSSAPI);
+ else if (!PL_strcasecmp (line, "PLAIN"))
+ SetCapFlag(POP3_HAS_AUTH_PLAIN);
+ else if (!PL_strcasecmp (line, "LOGIN"))
+ SetCapFlag(POP3_HAS_AUTH_LOGIN);
+
+ PR_Free(line);
+ return 0;
+}
+
+/*
+ * POP3 CAPA extension, see RFC 2449, chapter 5
+ */
+
+int32_t nsPop3Protocol::SendCapa()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendCapa()")));
+ if(!m_pop3ConData->command_succeeded)
+ return Error("pop3ServerError");
+
+ nsAutoCString command("CAPA" CRLF);
+
+ m_pop3ConData->next_state_after_response = POP3_CAPA_RESPONSE;
+ return Pop3SendData(command.get());
+}
+
+int32_t nsPop3Protocol::CapaResponse(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ char * line;
+ uint32_t ln = 0;
+
+ if (!m_pop3ConData->command_succeeded)
+ {
+ /* CAPA command not implemented */
+ m_pop3ConData->command_succeeded = true;
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ return 0;
+ }
+
+ bool pauseForMoreData = false;
+ nsresult rv;
+ line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if(pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true; /* pause */
+ PR_Free(line);
+ return(0);
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ if (!PL_strcmp(line, "."))
+ {
+ // now that we've read all the CAPA responses, go for it
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ m_pop3ConData->pause_for_read = false; /* don't pause */
+ }
+ else
+ if (!PL_strcasecmp(line, "XSENDER"))
+ {
+ SetCapFlag(POP3_HAS_XSENDER);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+ else
+ // see RFC 2449, chapter 6.4
+ if (!PL_strcasecmp(line, "RESP-CODES"))
+ {
+ SetCapFlag(POP3_HAS_RESP_CODES);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+ else
+ // see RFC 3206, chapter 6
+ if (!PL_strcasecmp(line, "AUTH-RESP-CODE"))
+ {
+ SetCapFlag(POP3_HAS_AUTH_RESP_CODE);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+ else
+ // see RFC 2595, chapter 4
+ if (!PL_strcasecmp(line, "STLS"))
+ {
+ SetCapFlag(POP3_HAS_STLS);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+ else
+ // see RFC 2449, chapter 6.3
+ if (!PL_strncasecmp(line, "SASL", 4) && strlen(line) > 6)
+ {
+ nsAutoCString responseLine;
+ responseLine.Assign(line + 5);
+
+ if (responseLine.Find("PLAIN", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_PLAIN);
+
+ if (responseLine.Find("LOGIN", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_LOGIN);
+
+ if (responseLine.Find("GSSAPI", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_GSSAPI);
+
+ if (responseLine.Find("CRAM-MD5", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_CRAM_MD5);
+
+ if (responseLine.Find("NTLM", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_NTLM);
+
+ if (responseLine.Find("MSN", CaseInsensitiveCompare) >= 0)
+ SetCapFlag(POP3_HAS_AUTH_NTLM|POP3_HAS_AUTH_MSN);
+
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ PR_Free(line);
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Capability entry processed")));
+ return 0;
+}
+
+int32_t nsPop3Protocol::SendTLSResponse()
+{
+ // only tear down our existing connection and open a new one if we received
+ // a +OK response from the pop server after we issued the STLS command
+ nsresult rv = NS_OK;
+ if (m_pop3ConData->command_succeeded)
+ {
+ nsCOMPtr<nsISupports> secInfo;
+ nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(m_transport, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ rv = strans->GetSecurityInfo(getter_AddRefs(secInfo));
+
+ if (NS_SUCCEEDED(rv) && secInfo)
+ {
+ nsCOMPtr<nsISSLSocketControl> sslControl = do_QueryInterface(secInfo, &rv);
+
+ if (NS_SUCCEEDED(rv) && sslControl)
+ rv = sslControl->StartTLS();
+ }
+
+ if (NS_SUCCEEDED(rv))
+ {
+ m_pop3ConData->next_state = POP3_SEND_AUTH;
+ m_tlsEnabled = true;
+
+ // certain capabilities like POP3_HAS_AUTH_APOP should be
+ // preserved across the connections.
+ uint32_t preservedCapFlags = m_pop3ConData->capability_flags & POP3_HAS_AUTH_APOP;
+ m_pop3ConData->capability_flags = // resetting the flags
+ POP3_AUTH_MECH_UNDEFINED |
+ POP3_HAS_AUTH_USER | // should be always there
+ POP3_GURL_UNDEFINED |
+ POP3_UIDL_UNDEFINED |
+ POP3_TOP_UNDEFINED |
+ POP3_XTND_XLST_UNDEFINED |
+ preservedCapFlags;
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ return 0;
+ }
+ }
+
+ ClearFlag(POP3_HAS_STLS);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+
+ return (NS_SUCCEEDED(rv) ? 0 : -1);
+}
+
+void nsPop3Protocol::InitPrefAuthMethods(int32_t authMethodPrefValue)
+{
+ // for m_prefAuthMethods, using the same flags as server capablities.
+ switch (authMethodPrefValue)
+ {
+ case nsMsgAuthMethod::none:
+ m_prefAuthMethods = POP3_HAS_AUTH_NONE;
+ break;
+ case nsMsgAuthMethod::old:
+ m_prefAuthMethods = POP3_HAS_AUTH_USER;
+ break;
+ case nsMsgAuthMethod::passwordCleartext:
+ m_prefAuthMethods = POP3_HAS_AUTH_USER |
+ POP3_HAS_AUTH_LOGIN | POP3_HAS_AUTH_PLAIN;
+ break;
+ case nsMsgAuthMethod::passwordEncrypted:
+ m_prefAuthMethods = POP3_HAS_AUTH_CRAM_MD5 |
+ POP3_HAS_AUTH_APOP;
+ break;
+ case nsMsgAuthMethod::NTLM:
+ m_prefAuthMethods = POP3_HAS_AUTH_NTLM | POP3_HAS_AUTH_MSN;
+ break;
+ case nsMsgAuthMethod::GSSAPI:
+ m_prefAuthMethods = POP3_HAS_AUTH_GSSAPI;
+ break;
+ case nsMsgAuthMethod::secure:
+ m_prefAuthMethods = POP3_HAS_AUTH_APOP |
+ POP3_HAS_AUTH_CRAM_MD5 | POP3_HAS_AUTH_GSSAPI |
+ POP3_HAS_AUTH_NTLM | POP3_HAS_AUTH_MSN;
+ break;
+ default:
+ NS_ASSERTION(false, "POP: authMethod pref invalid");
+ // TODO log to error console
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
+ (POP3LOG("POP: bad pref authMethod = %d\n"), authMethodPrefValue));
+ // fall to any
+ MOZ_FALLTHROUGH;
+ case nsMsgAuthMethod::anything:
+ m_prefAuthMethods = POP3_HAS_AUTH_USER |
+ POP3_HAS_AUTH_LOGIN | POP3_HAS_AUTH_PLAIN |
+ POP3_HAS_AUTH_CRAM_MD5 | POP3_HAS_AUTH_APOP |
+ POP3_HAS_AUTH_GSSAPI |
+ POP3_HAS_AUTH_NTLM | POP3_HAS_AUTH_MSN;
+ // TODO needed?
+ break;
+ }
+ NS_ASSERTION(m_prefAuthMethods != POP3_AUTH_MECH_UNDEFINED,
+ "POP: InitPrefAuthMethods() didn't work");
+}
+
+/**
+ * Changes m_currentAuthMethod to pick the best one
+ * which is allowed by server and prefs and not marked failed.
+ * The order of preference and trying of auth methods is encoded here.
+ */
+nsresult nsPop3Protocol::ChooseAuthMethod()
+{
+ int32_t availCaps = GetCapFlags() & m_prefAuthMethods & ~m_failedAuthMethods;
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("POP auth: server caps 0x%X, pref 0x%X, failed 0x%X, avail caps 0x%X"),
+ GetCapFlags(), m_prefAuthMethods, m_failedAuthMethods, availCaps));
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("(GSSAPI = 0x%X, CRAM = 0x%X, APOP = 0x%X, NTLM = 0x%X, "
+ "MSN = 0x%X, PLAIN = 0x%X, LOGIN = 0x%X, USER/PASS = 0x%X)"),
+ POP3_HAS_AUTH_GSSAPI, POP3_HAS_AUTH_CRAM_MD5, POP3_HAS_AUTH_APOP,
+ POP3_HAS_AUTH_NTLM, POP3_HAS_AUTH_MSN, POP3_HAS_AUTH_PLAIN,
+ POP3_HAS_AUTH_LOGIN, POP3_HAS_AUTH_USER));
+
+ if (POP3_HAS_AUTH_GSSAPI & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_GSSAPI;
+ else if (POP3_HAS_AUTH_CRAM_MD5 & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_CRAM_MD5;
+ else if (POP3_HAS_AUTH_APOP & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_APOP;
+ else if (POP3_HAS_AUTH_NTLM & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_NTLM;
+ else if (POP3_HAS_AUTH_MSN & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_MSN;
+ else if (POP3_HAS_AUTH_PLAIN & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_PLAIN;
+ else if (POP3_HAS_AUTH_LOGIN & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_LOGIN;
+ else if (POP3_HAS_AUTH_USER & availCaps)
+ m_currentAuthMethod = POP3_HAS_AUTH_USER;
+ else
+ {
+ // there are no matching login schemes at all, per server and prefs
+ m_currentAuthMethod = POP3_AUTH_MECH_UNDEFINED;
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("no auth method remaining")));
+ return NS_ERROR_FAILURE;
+ }
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("trying auth method 0x%X"), m_currentAuthMethod));
+ return NS_OK;
+}
+
+void nsPop3Protocol::MarkAuthMethodAsFailed(int32_t failedAuthMethod)
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("marking auth method 0x%X failed"), failedAuthMethod));
+ m_failedAuthMethods |= failedAuthMethod;
+}
+
+/**
+ * Start over, trying all auth methods again
+ */
+void nsPop3Protocol::ResetAuthMethods()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("resetting (failed) auth methods")));
+ m_currentAuthMethod = POP3_AUTH_MECH_UNDEFINED;
+ m_failedAuthMethods = 0;
+}
+
+/**
+ * state POP3_PROCESS_AUTH
+ * Called when we should try to authenticate to the server.
+ * Also called when one auth method fails and we want to try and start
+ * the next best auth method.
+ */
+int32_t nsPop3Protocol::ProcessAuth()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("ProcessAuth()")));
+
+ // Try to upgrade to STARTTLS -- TODO move into its own function
+ if (!m_tlsEnabled)
+ {
+ if(TestCapFlag(POP3_HAS_STLS))
+ {
+ if (m_socketType == nsMsgSocketType::trySTARTTLS ||
+ m_socketType == nsMsgSocketType::alwaysSTARTTLS)
+ {
+ nsAutoCString command("STLS" CRLF);
+
+ m_pop3ConData->next_state_after_response = POP3_TLS_RESPONSE;
+ return Pop3SendData(command.get());
+ }
+ }
+ else if (m_socketType == nsMsgSocketType::alwaysSTARTTLS)
+ {
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ return Error("nsErrorCouldNotConnectViaTls");
+ }
+ }
+
+ m_password_already_sent = false;
+
+ nsresult rv = ChooseAuthMethod();
+ if (NS_FAILED(rv))
+ {
+ // Pref doesn't match server. Now, find an appropriate error msg.
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("ProcessAuth() early exit because no auth methods")));
+
+ // AuthGSSAPI* falls in here in case of an auth failure.
+ // If Kerberos was the only method, assume that
+ // the user is just not logged in yet, and show an appropriate error.
+ if (m_prefAuthMethods == POP3_HAS_AUTH_GSSAPI &&
+ m_failedAuthMethods == POP3_HAS_AUTH_GSSAPI)
+ return Error("pop3GssapiFailure");
+
+ // pref has plaintext pw & server claims to support encrypted pw
+ if (m_prefAuthMethods == (POP3_HAS_AUTH_USER | POP3_HAS_AUTH_LOGIN |
+ POP3_HAS_AUTH_PLAIN) &&
+ GetCapFlags() & (POP3_HAS_AUTH_CRAM_MD5 | POP3_HAS_AUTH_APOP))
+ // tell user to change to encrypted pw
+ return Error("pop3AuthChangePlainToEncrypt");
+ // pref has encrypted pw & server claims to support plaintext pw
+ else if (m_prefAuthMethods == (POP3_HAS_AUTH_CRAM_MD5 |
+ POP3_HAS_AUTH_APOP) &&
+ GetCapFlags() & (POP3_HAS_AUTH_USER | POP3_HAS_AUTH_LOGIN |
+ POP3_HAS_AUTH_PLAIN))
+ {
+ // have SSL
+ if (m_socketType == nsMsgSocketType::SSL ||
+ m_socketType == nsMsgSocketType::alwaysSTARTTLS)
+ // tell user to change to plaintext pw
+ return Error("pop3AuthChangeEncryptToPlainSSL");
+ else
+ // tell user to change to plaintext pw, with big warning
+ return Error("pop3AuthChangeEncryptToPlainNoSSL");
+ }
+ else
+ // just "change auth method"
+ return Error("pop3AuthMechNotSupported");
+ }
+
+ switch (m_currentAuthMethod)
+ {
+ case POP3_HAS_AUTH_GSSAPI:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP GSSAPI")));
+ m_pop3ConData->next_state = POP3_AUTH_GSSAPI;
+ break;
+ case POP3_HAS_AUTH_APOP:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP APOP")));
+ m_pop3ConData->next_state = POP3_SEND_PASSWORD;
+ break;
+ case POP3_HAS_AUTH_CRAM_MD5:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP CRAM")));
+ MOZ_FALLTHROUGH;
+ case POP3_HAS_AUTH_PLAIN:
+ case POP3_HAS_AUTH_USER:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP username")));
+ m_pop3ConData->next_state = POP3_SEND_USERNAME;
+ break;
+ case POP3_HAS_AUTH_LOGIN:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP AUTH=LOGIN")));
+ m_pop3ConData->next_state = POP3_AUTH_LOGIN;
+ break;
+ case POP3_HAS_AUTH_NTLM:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP NTLM")));
+ m_pop3ConData->next_state = POP3_AUTH_NTLM;
+ break;
+ case POP3_HAS_AUTH_NONE:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("POP no auth")));
+ m_pop3ConData->command_succeeded = true;
+ m_pop3ConData->next_state = POP3_NEXT_AUTH_STEP;
+ break;
+ default:
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
+ (POP3LOG("POP: m_currentAuthMethod has unknown value")));
+ return Error("pop3AuthMechNotSupported");
+ }
+
+ m_pop3ConData->pause_for_read = false;
+
+ return 0;
+}
+
+/**
+ * state POP3_NEXT_AUTH_STEP
+ * This is called when we finished one auth step (e.g. sending username
+ * or password are separate steps, similarly for AUTH LOGIN, NTLM etc.)
+ * and want to proceed to the next one.
+ */
+int32_t nsPop3Protocol::NextAuthStep()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("NextAuthStep()")));
+ if (m_pop3ConData->command_succeeded)
+ {
+ if (m_password_already_sent || // (also true for GSSAPI)
+ m_currentAuthMethod == POP3_HAS_AUTH_NONE)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("login succeeded")));
+ m_nsIPop3Sink->SetUserAuthenticated(true);
+ ClearFlag(POP3_PASSWORD_FAILED);
+ if (m_pop3ConData->verify_logon)
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ else
+ m_pop3ConData->next_state = (m_pop3ConData->get_url)
+ ? POP3_SEND_GURL : POP3_SEND_STAT;
+ }
+ else
+ m_pop3ConData->next_state = POP3_SEND_PASSWORD;
+ }
+ else
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("command did not succeed")));
+ // response code received shows that login failed not because of
+ // wrong credential -> stop login without retry or pw dialog, only alert
+ // parameter list -> user
+ nsCString userName;
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ nsresult rv = server->GetRealUsername(userName);
+ NS_ENSURE_SUCCESS(rv, -1);
+ NS_ConvertUTF8toUTF16 userNameUTF16(userName);
+ const char16_t* params[] = { userNameUTF16.get() };
+ if (TestFlag(POP3_STOPLOGIN))
+ {
+ if (m_password_already_sent)
+ return Error("pop3PasswordFailed", params, 1);
+
+ return Error("pop3UsernameFailure");
+ }
+ // response code received shows that server is certain about the
+ // credential was wrong -> no fallback, show alert and pw dialog
+ if (TestFlag(POP3_AUTH_FAILURE))
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("auth failure, setting password failed")));
+ if (m_password_already_sent)
+ Error("pop3PasswordFailed", params, 1);
+ else
+ Error("pop3UsernameFailure");
+ SetFlag(POP3_PASSWORD_FAILED);
+ ClearFlag(POP3_AUTH_FAILURE);
+ return 0;
+ }
+
+ // We have no certain response code -> fallback and try again.
+ // Mark the auth method failed, to use a different method next round.
+ MarkAuthMethodAsFailed(m_currentAuthMethod);
+
+ if (m_currentAuthMethod == POP3_HAS_AUTH_USER &&
+ !m_password_already_sent)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("USER username failed")));
+ // if USER auth method failed before sending the password,
+ // the username was wrong.
+ // no fallback but return error
+ return Error("pop3UsernameFailure");
+ }
+
+ // If we have no auth method left, ask user to try with new password
+ rv = ChooseAuthMethod();
+ if (NS_FAILED(rv))
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
+ (POP3LOG("POP: no auth methods remaining, setting password failure")));
+ /* Sever the connection and go back to the `read password' state,
+ which, upon success, will re-open the connection. Set a flag
+ which causes the prompt to be different that time (to indicate
+ that the old password was bogus.)
+
+ But if we're just checking for new mail (biff) then don't bother
+ prompting the user for a password: just fail silently.
+ */
+ SetFlag(POP3_PASSWORD_FAILED);
+ Error("pop3PasswordFailed", params, 1);
+ return 0;
+ }
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("still have some auth methods to try")));
+
+ // TODO needed?
+ //m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+
+ m_pop3ConData->command_succeeded = true;
+
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ }
+
+ if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
+ {
+ ClearCapFlag(POP3_AUTH_MECH_UNDEFINED);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ m_pop3ConData->pause_for_read = false;
+
+ return 0;
+}
+
+// LOGIN consists of three steps not two as USER/PASS or CRAM-MD5,
+// so we've to start here and continue in SendUsername if the server
+// responds + to "AUTH LOGIN"
+int32_t nsPop3Protocol::AuthLogin()
+{
+ nsAutoCString command("AUTH LOGIN" CRLF);
+ m_pop3ConData->next_state_after_response = POP3_AUTH_LOGIN_RESPONSE;
+ m_pop3ConData->pause_for_read = true;
+
+ return Pop3SendData(command.get());
+}
+
+int32_t nsPop3Protocol::AuthLoginResponse()
+{
+ // need the test to be here instead in NextAuthStep() to
+ // differentiate between command AUTH LOGIN failed and
+ // sending username using LOGIN mechanism failed.
+ if (!m_pop3ConData->command_succeeded)
+ {
+ // we failed with LOGIN, remove it
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_LOGIN);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ }
+ else
+ m_pop3ConData->next_state = POP3_SEND_USERNAME;
+
+ m_pop3ConData->pause_for_read = false;
+
+ return 0;
+}
+
+// NTLM, like LOGIN consists of three steps not two as USER/PASS or CRAM-MD5,
+// so we've to start here and continue in SendUsername if the server
+// responds + to "AUTH NTLM"
+int32_t nsPop3Protocol::AuthNtlm()
+{
+ nsAutoCString command (m_currentAuthMethod == POP3_HAS_AUTH_MSN
+ ? "AUTH MSN" CRLF : "AUTH NTLM" CRLF);
+ m_pop3ConData->next_state_after_response = POP3_AUTH_NTLM_RESPONSE;
+ m_pop3ConData->pause_for_read = true;
+
+ return Pop3SendData(command.get());
+}
+
+int32_t nsPop3Protocol::AuthNtlmResponse()
+{
+ // need the test to be here instead in NextAuthStep() to
+ // differentiate between command AUTH NTLM failed and
+ // sending username using NTLM mechanism failed.
+ if (!m_pop3ConData->command_succeeded)
+ {
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_NTLM);
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_MSN);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ }
+ else
+ m_pop3ConData->next_state = POP3_SEND_USERNAME;
+
+ m_pop3ConData->pause_for_read = false;
+
+ return 0;
+}
+
+int32_t nsPop3Protocol::AuthGSSAPI()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("AuthGSSAPI()")));
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server) {
+ nsAutoCString cmd;
+ nsAutoCString service("pop@");
+ nsCString hostName;
+ nsresult rv;
+ server->GetRealHostName(hostName);
+ service.Append(hostName);
+ rv = DoGSSAPIStep1(service.get(), m_username.get(), cmd);
+ if (NS_SUCCEEDED(rv)) {
+ m_GSSAPICache.Assign(cmd);
+ m_pop3ConData->next_state_after_response = POP3_AUTH_GSSAPI_FIRST;
+ m_pop3ConData->pause_for_read = true;
+ return Pop3SendData("AUTH GSSAPI" CRLF);
+ }
+ }
+
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_GSSAPI);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ m_pop3ConData->pause_for_read = false;
+ return 0;
+}
+
+int32_t nsPop3Protocol::AuthGSSAPIResponse(bool first)
+{
+ if (!m_pop3ConData->command_succeeded)
+ {
+ if (first)
+ m_GSSAPICache.Truncate();
+ MarkAuthMethodAsFailed(POP3_HAS_AUTH_GSSAPI);
+ m_pop3ConData->next_state = POP3_PROCESS_AUTH;
+ m_pop3ConData->pause_for_read = false;
+ return 0;
+ }
+
+ int32_t result;
+
+ m_pop3ConData->next_state_after_response = POP3_AUTH_GSSAPI_STEP;
+ m_pop3ConData->pause_for_read = true;
+
+ if (first) {
+ m_GSSAPICache += CRLF;
+ result = Pop3SendData(m_GSSAPICache.get());
+ m_GSSAPICache.Truncate();
+ }
+ else {
+ nsAutoCString cmd;
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("GSSAPI step 2")));
+ nsresult rv = DoGSSAPIStep2(m_commandResponse, cmd);
+ if (NS_FAILED(rv))
+ cmd = "*";
+ if (rv == NS_SUCCESS_AUTH_FINISHED) {
+ m_pop3ConData->next_state_after_response = POP3_NEXT_AUTH_STEP;
+ m_password_already_sent = true;
+ }
+ cmd += CRLF;
+ result = Pop3SendData(cmd.get());
+ }
+
+ return result;
+}
+
+int32_t nsPop3Protocol::SendUsername()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendUsername()")));
+ if(m_username.IsEmpty())
+ return Error("pop3UsernameUndefined");
+
+ // <copied from="SendPassword()">
+ // Needed for NTLM
+
+ // The POP3_SEND_PASSWORD/POP3_WAIT_SEND_PASSWORD states have already
+ // got the password - they will have cancelled if necessary.
+ // If the password is still empty here, don't try to go on.
+ if (m_passwordResult.IsEmpty())
+ {
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ return Error("pop3PasswordUndefined");
+ }
+ // </copied>
+
+ nsAutoCString cmd;
+
+ if (m_currentAuthMethod == POP3_HAS_AUTH_NTLM)
+ (void) DoNtlmStep1(m_username.get(), m_passwordResult.get(), cmd);
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_CRAM_MD5)
+ cmd = "AUTH CRAM-MD5";
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_PLAIN)
+ cmd = "AUTH PLAIN";
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_LOGIN)
+ {
+ char *base64Str = PL_Base64Encode(m_username.get(), m_username.Length(), nullptr);
+ cmd = base64Str;
+ PR_Free(base64Str);
+ }
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_USER)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("USER login")));
+ cmd = "USER ";
+ cmd += m_username;
+ }
+ else
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
+ (POP3LOG("In nsPop3Protocol::SendUsername(), m_currentAuthMethod is 0x%X, "
+ "but that is unexpected"), m_currentAuthMethod));
+ return Error("pop3AuthInternalError");
+ }
+
+ cmd += CRLF;
+
+ m_pop3ConData->next_state_after_response = POP3_NEXT_AUTH_STEP;
+
+ m_pop3ConData->pause_for_read = true;
+
+ return Pop3SendData(cmd.get());
+}
+
+int32_t nsPop3Protocol::SendPassword()
+{
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("SendPassword()")));
+ if (m_username.IsEmpty())
+ return Error("pop3UsernameUndefined");
+
+ // <copied to="SendUsername()">
+ // Needed here, too, because APOP skips SendUsername()
+ // The POP3_SEND_PASSWORD/POP3_WAIT_SEND_PASSWORD states have already
+ // got the password - they will have cancelled if necessary.
+ // If the password is still empty here, don't try to go on.
+ if (m_passwordResult.IsEmpty())
+ {
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ return Error("pop3PasswordUndefined");
+ }
+ // </copied>
+
+ nsAutoCString cmd;
+ nsresult rv;
+
+ if (m_currentAuthMethod == POP3_HAS_AUTH_NTLM)
+ rv = DoNtlmStep2(m_commandResponse, cmd);
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_CRAM_MD5)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("CRAM login")));
+ char buffer[512]; // TODO nsAutoCString
+ unsigned char digest[DIGEST_LENGTH];
+
+ char *decodedChallenge = PL_Base64Decode(m_commandResponse.get(),
+ m_commandResponse.Length(), nullptr);
+
+ if (decodedChallenge)
+ rv = MSGCramMD5(decodedChallenge, strlen(decodedChallenge),
+ m_passwordResult.get(), m_passwordResult.Length(), digest);
+ else
+ rv = NS_ERROR_NULL_POINTER;
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString encodedDigest;
+ char hexVal[8];
+
+ for (uint32_t j = 0; j < 16; j++)
+ {
+ PR_snprintf (hexVal,8, "%.2x", 0x0ff & (unsigned short)digest[j]);
+ encodedDigest.Append(hexVal);
+ }
+
+ PR_snprintf(buffer, sizeof(buffer), "%s %s", m_username.get(),
+ encodedDigest.get());
+ char *base64Str = PL_Base64Encode(buffer, strlen(buffer), nullptr);
+ cmd = base64Str;
+ PR_Free(base64Str);
+ }
+
+ if (NS_FAILED(rv))
+ cmd = "*";
+ }
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_APOP)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("APOP login")));
+ char buffer[512];
+ unsigned char digest[DIGEST_LENGTH];
+
+ rv = MSGApopMD5(m_ApopTimestamp.get(), m_ApopTimestamp.Length(),
+ m_passwordResult.get(), m_passwordResult.Length(), digest);
+
+ if (NS_SUCCEEDED(rv))
+ {
+ nsAutoCString encodedDigest;
+ char hexVal[8];
+
+ for (uint32_t j=0; j<16; j++)
+ {
+ PR_snprintf (hexVal,8, "%.2x", 0x0ff & (unsigned short)digest[j]);
+ encodedDigest.Append(hexVal);
+ }
+
+ PR_snprintf(buffer, sizeof(buffer), "APOP %s %s", m_username.get(),
+ encodedDigest.get());
+ cmd = buffer;
+ }
+
+ if (NS_FAILED(rv))
+ cmd = "*";
+ }
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_PLAIN)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("PLAIN login")));
+ // workaround for IPswitch's IMail server software
+ // this server goes into LOGIN mode even if we send "AUTH PLAIN"
+ // "VXNlc" is the beginning of the base64 encoded prompt ("Username:") for LOGIN
+ if (StringBeginsWith(m_commandResponse, NS_LITERAL_CSTRING("VXNlc")))
+ {
+ // disable PLAIN and enable LOGIN (in case it's not already enabled)
+ ClearCapFlag(POP3_HAS_AUTH_PLAIN);
+ SetCapFlag(POP3_HAS_AUTH_LOGIN);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+
+ // reenter authentication again at LOGIN response handler
+ m_pop3ConData->next_state = POP3_AUTH_LOGIN_RESPONSE;
+ m_pop3ConData->pause_for_read = false;
+ return 0;
+ }
+
+ char plain_string[512]; // TODO nsCString
+ int len = 1; /* first <NUL> char */
+ memset(plain_string, 0, 512);
+ PR_snprintf(&plain_string[1], 510, "%s", m_username.get());
+ len += m_username.Length();
+ len++; /* second <NUL> char */
+ PR_snprintf(&plain_string[len], 511-len, "%s", m_passwordResult.get());
+ len += m_passwordResult.Length();
+
+ char *base64Str = PL_Base64Encode(plain_string, len, nullptr);
+ cmd = base64Str;
+ PR_Free(base64Str);
+ }
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_LOGIN)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("LOGIN password")));
+ char * base64Str =
+ PL_Base64Encode(m_passwordResult.get(), m_passwordResult.Length(),
+ nullptr);
+ cmd = base64Str;
+ PR_Free(base64Str);
+ }
+ else if (m_currentAuthMethod == POP3_HAS_AUTH_USER)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("PASS password")));
+ cmd = "PASS ";
+ cmd += m_passwordResult;
+ }
+ else
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Error,
+ (POP3LOG("In nsPop3Protocol::SendPassword(), m_currentAuthMethod is %X, "
+ "but that is unexpected"), m_currentAuthMethod));
+ return Error("pop3AuthInternalError");
+ }
+
+ cmd += CRLF;
+
+ // TODO needed?
+ //m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+
+ m_pop3ConData->next_state_after_response = POP3_NEXT_AUTH_STEP;
+
+ m_pop3ConData->pause_for_read = true;
+
+ m_password_already_sent = true;
+ m_lastPasswordSent = m_passwordResult;
+ return Pop3SendData(cmd.get(), true);
+}
+
+int32_t nsPop3Protocol::SendStatOrGurl(bool sendStat)
+{
+ nsAutoCString cmd;
+ if (sendStat)
+ {
+ cmd = "STAT" CRLF;
+ m_pop3ConData->next_state_after_response = POP3_GET_STAT;
+ }
+ else
+ {
+ cmd = "GURL" CRLF;
+ m_pop3ConData->next_state_after_response = POP3_GURL_RESPONSE;
+ }
+ return Pop3SendData(cmd.get());
+}
+
+
+int32_t
+nsPop3Protocol::SendStat()
+{
+ return SendStatOrGurl(true);
+}
+
+
+int32_t
+nsPop3Protocol::GetStat()
+{
+ // check stat response
+ if (!m_pop3ConData->command_succeeded)
+ return Error("pop3StatFail");
+
+ /* stat response looks like: %d %d
+ * The first number is the number of articles
+ * The second number is the number of bytes
+ *
+ * grab the first and second arg of stat response
+ */
+ nsCString oldStr (m_commandResponse);
+ char *newStr = oldStr.BeginWriting();
+ char *num = NS_strtok(" ", &newStr); // msg num
+ if (num)
+ {
+ m_pop3ConData->number_of_messages = atol(num); // bytes
+ num = NS_strtok(" ", &newStr);
+ m_commandResponse = newStr;
+ if (num)
+ m_totalFolderSize = nsCRT::atoll(num); //we always initialize m_totalFolderSize to 0
+ }
+ else
+ m_pop3ConData->number_of_messages = 0;
+
+ m_pop3ConData->really_new_messages = 0;
+ m_pop3ConData->real_new_counter = 1;
+
+ m_totalDownloadSize = -1; // Means we need to calculate it, later.
+
+ if (m_pop3ConData->number_of_messages <= 0)
+ {
+ // We're all done. We know we have no mail.
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ PL_HashTableEnumerateEntries(m_pop3ConData->uidlinfo->hash, hash_clear_mapper, nullptr);
+ // Hack - use nsPop3Sink to wipe out any stale Partial messages
+ m_nsIPop3Sink->BeginMailDelivery(false, nullptr, nullptr);
+ m_nsIPop3Sink->AbortMailDelivery(this);
+ return(0);
+ }
+
+ /* We're just checking for new mail, and we're not playing any games that
+ involve keeping messages on the server. Therefore, we now know enough
+ to finish up. If we had no messages, that would have been handled
+ above; therefore, we know we have some new messages.
+ */
+ if (m_pop3ConData->only_check_for_new_mail && !m_pop3ConData->leave_on_server)
+ {
+ m_nsIPop3Sink->SetBiffStateAndUpdateFE(nsIMsgFolder::nsMsgBiffState_NewMail,
+ m_pop3ConData->number_of_messages,
+ true);
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ return(0);
+ }
+
+
+ if (!m_pop3ConData->only_check_for_new_mail)
+ {
+ /* The following was added to prevent the loss of Data when we try and
+ write to somewhere we don't have write access error to (See bug 62480)
+ (Note: This is only a temp hack until the underlying XPCOM is fixed
+ to return errors) */
+
+ nsresult rv;
+ nsCOMPtr <nsIMsgWindow> msgWindow;
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url);
+ if (mailnewsUrl)
+ rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
+// NS_ASSERTION(NS_SUCCEEDED(rv) && msgWindow, "no msg window");
+
+ rv = m_nsIPop3Sink->BeginMailDelivery(m_pop3ConData->only_uidl != nullptr, msgWindow,
+ &m_pop3ConData->msg_del_started);
+ if (NS_FAILED(rv))
+ {
+ m_nsIPop3Sink->AbortMailDelivery(this);
+ if (rv == NS_MSG_FOLDER_BUSY) {
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ nsString accountName;
+ rv = server->GetPrettyName(accountName);
+ NS_ENSURE_SUCCESS(rv, -1);
+
+ const char16_t *params[] = { accountName.get() };
+ return Error("pop3ServerBusy", params, 1);
+ }
+
+ return Error("pop3MessageWriteError");
+ }
+
+ if(!m_pop3ConData->msg_del_started)
+ return Error("pop3MessageWriteError");
+ }
+
+ m_pop3ConData->next_state = POP3_SEND_LIST;
+ return 0;
+}
+
+
+
+int32_t
+nsPop3Protocol::SendGurl()
+{
+ if (m_pop3ConData->capability_flags == POP3_CAPABILITY_UNDEFINED ||
+ TestCapFlag(POP3_GURL_UNDEFINED | POP3_HAS_GURL))
+ return SendStatOrGurl(false);
+ else
+ return -1;
+}
+
+
+int32_t
+nsPop3Protocol::GurlResponse()
+{
+ ClearCapFlag(POP3_GURL_UNDEFINED);
+
+ if (m_pop3ConData->command_succeeded)
+ {
+ SetCapFlag(POP3_HAS_GURL);
+ if (m_nsIPop3Sink)
+ m_nsIPop3Sink->SetMailAccountURL(m_commandResponse);
+ }
+ else
+ {
+ ClearCapFlag(POP3_HAS_GURL);
+ }
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+
+ return 0;
+}
+
+int32_t nsPop3Protocol::SendList()
+{
+ // check for server returning number of messages that will cause the calculation
+ // of the size of the block for msg_info to
+ // overflow a 32 bit int, in turn causing us to allocate a block of memory much
+ // smaller than we think we're allocating, and
+ // potentially allowing the server to make us overwrite memory outside our heap
+ // block.
+
+ if (m_pop3ConData->number_of_messages > (int) (0xFFFFF000 / sizeof(Pop3MsgInfo)))
+ return MK_OUT_OF_MEMORY;
+
+
+ m_pop3ConData->msg_info = (Pop3MsgInfo *)
+ PR_CALLOC(sizeof(Pop3MsgInfo) * m_pop3ConData->number_of_messages);
+ if (!m_pop3ConData->msg_info)
+ return(MK_OUT_OF_MEMORY);
+ m_pop3ConData->next_state_after_response = POP3_GET_LIST;
+ m_listpos = 0;
+ return Pop3SendData("LIST" CRLF);
+}
+
+
+
+int32_t
+nsPop3Protocol::GetList(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ /* check list response
+ * This will get called multiple times
+ * but it's alright since command_succeeded
+ * will remain constant
+ */
+ if(!m_pop3ConData->command_succeeded)
+ return Error("pop3ListFailure");
+
+ uint32_t ln = 0;
+ bool pauseForMoreData = false;
+ nsresult rv;
+ char *line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if (pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true;
+ PR_Free(line);
+ return(ln);
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ /* parse the line returned from the list command
+ * it looks like
+ * #msg_number #bytes
+ *
+ * list data is terminated by a ".CRLF" line
+ */
+ if (!PL_strcmp(line, "."))
+ {
+ // limit the list if fewer entries than given in STAT response
+ if(m_listpos < m_pop3ConData->number_of_messages)
+ m_pop3ConData->number_of_messages = m_listpos;
+ m_pop3ConData->next_state = POP3_SEND_UIDL_LIST;
+ m_pop3ConData->pause_for_read = false;
+ PR_Free(line);
+ return(0);
+ }
+
+ char *newStr = line;
+ char *token = NS_strtok(" ", &newStr);
+ if (token)
+ {
+ int32_t msg_num = atol(token);
+
+ if (++m_listpos <= m_pop3ConData->number_of_messages)
+ {
+ token = NS_strtok(" ", &newStr);
+ if (token)
+ {
+ m_pop3ConData->msg_info[m_listpos-1].size = atol(token);
+ m_pop3ConData->msg_info[m_listpos-1].msgnum = msg_num;
+ }
+ }
+ }
+
+ PR_Free(line);
+ return(0);
+}
+
+
+/* UIDL and XTND are both unsupported for this mail server.
+ If not enabled any advanced features, we're able to live
+ without them. We're simply downloading and deleting everything
+ on the server.
+
+ Advanced features are:
+ *'Keep Mail on Server' with aging or deletion support
+ *'Fetch Headers Only'
+ *'Limit Message Size'
+ *only download a specific UID
+
+ These require knowledge of of all messages UID's on the server at
+ least when it comes to deleting deleting messages on server that
+ have been deleted on client or vice versa. TOP doesn't help here
+ without generating huge traffic and is mostly not supported at all
+ if the server lacks UIDL and XTND XLST.
+
+ In other cases the user has to join the 20th century.
+ Tell the user this, and refuse to download any messages until
+ they've gone into preferences and turned off any of the above
+ prefs.
+*/
+int32_t nsPop3Protocol::HandleNoUidListAvailable()
+{
+ m_pop3ConData->pause_for_read = false;
+
+ if(!m_pop3ConData->leave_on_server &&
+ !m_pop3ConData->headers_only &&
+ m_pop3ConData->size_limit <= 0 &&
+ !m_pop3ConData->only_uidl)
+ {
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ return 0;
+ }
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ nsCString hostName;
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ nsresult rv = server->GetRealHostName(hostName);
+ NS_ENSURE_SUCCESS(rv, -1);
+ NS_ConvertASCIItoUTF16 hostNameUnicode(hostName);
+ const char16_t *params[] = { hostNameUnicode.get() };
+ return Error("pop3ServerDoesNotSupportUidlEtc", params, 1);
+}
+
+
+/* km
+ *
+ * net_pop3_send_xtnd_xlst_msgid
+ *
+ * Process state: POP3_SEND_XTND_XLST_MSGID
+ *
+ * If we get here then UIDL is not supported by the mail server.
+ * Some mail servers support a similar command:
+ *
+ * XTND XLST Message-Id
+ *
+ * Here is a sample transaction from a QUALCOMM server
+
+ >>XTND XLST Message-Id
+ <<+OK xlst command accepted; headers coming.
+ <<1 Message-ID: <3117E4DC.2699@netscape.invalid>
+ <<2 Message-Id: <199602062335.PAA19215@lemon.example.com>
+
+ * This function will send the xtnd command and put us into the
+ * POP3_GET_XTND_XLST_MSGID state
+ *
+*/
+int32_t nsPop3Protocol::SendXtndXlstMsgid()
+{
+ if (TestCapFlag(POP3_HAS_XTND_XLST | POP3_XTND_XLST_UNDEFINED))
+ {
+ m_pop3ConData->next_state_after_response = POP3_GET_XTND_XLST_MSGID;
+ m_pop3ConData->pause_for_read = true;
+ m_listpos = 0;
+ return Pop3SendData("XTND XLST Message-Id" CRLF);
+ }
+ else
+ return HandleNoUidListAvailable();
+}
+
+
+/* km
+ *
+ * net_pop3_get_xtnd_xlst_msgid
+ *
+ * This code was created from the net_pop3_get_uidl_list boiler plate.
+ * The difference is that the XTND reply strings have one more token per
+ * string than the UIDL reply strings do.
+ *
+ */
+
+int32_t
+nsPop3Protocol::GetXtndXlstMsgid(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ /* check list response
+ * This will get called multiple times
+ * but it's alright since command_succeeded
+ * will remain constant
+ */
+ ClearCapFlag(POP3_XTND_XLST_UNDEFINED);
+
+ if (!m_pop3ConData->command_succeeded)
+ {
+ ClearCapFlag(POP3_HAS_XTND_XLST);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ HandleNoUidListAvailable();
+ return(0);
+ }
+ else
+ {
+ SetCapFlag(POP3_HAS_XTND_XLST);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ uint32_t ln = 0;
+ bool pauseForMoreData = false;
+ nsresult rv;
+ char *line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if (pauseForMoreData || !line)
+ {
+ m_pop3ConData->pause_for_read = true;
+ PR_Free(line);
+ return ln;
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ /* parse the line returned from the list command
+ * it looks like
+ * 1 Message-ID: <3117E4DC.2699@example.com>
+ *
+ * list data is terminated by a ".CRLF" line
+ */
+ if (!PL_strcmp(line, "."))
+ {
+ // limit the list if fewer entries than given in STAT response
+ if(m_listpos < m_pop3ConData->number_of_messages)
+ m_pop3ConData->number_of_messages = m_listpos;
+ m_pop3ConData->list_done = true;
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ m_pop3ConData->pause_for_read = false;
+ PR_Free(line);
+ return(0);
+ }
+
+ char *newStr = line;
+ char *token = NS_strtok(" ", &newStr); // msg num
+ if (token)
+ {
+ int32_t msg_num = atol(token);
+ if (++m_listpos <= m_pop3ConData->number_of_messages)
+ {
+ NS_strtok(" ", &newStr); // eat message ID token
+ const char *uid = NS_strtok(" ", &newStr); // not really a UID but a unique token -km
+ if (!uid)
+ /* This is bad. The server didn't give us a UIDL for this message.
+ I've seen this happen when somehow the mail spool has a message
+ that contains a header that reads "X-UIDL: \n". But how that got
+ there, I have no idea; must be a server bug. Or something. */
+ uid = "";
+
+ // seeking right entry, but try the one that should it be first
+ int32_t i;
+ if(m_pop3ConData->msg_info[m_listpos - 1].msgnum == msg_num)
+ i = m_listpos - 1;
+ else
+ for(i = 0; i < m_pop3ConData->number_of_messages &&
+ m_pop3ConData->msg_info[i].msgnum != msg_num; i++)
+ ;
+
+ // only if found a matching slot
+ if (i < m_pop3ConData->number_of_messages)
+ {
+ // to protect us from memory leak in case of getting a msg num twice
+ m_pop3ConData->msg_info[i].uidl = PL_strdup(uid);
+ if (!m_pop3ConData->msg_info[i].uidl)
+ {
+ PR_Free(line);
+ return MK_OUT_OF_MEMORY;
+ }
+ }
+ }
+ }
+
+ PR_Free(line);
+ return(0);
+}
+
+
+int32_t nsPop3Protocol::SendUidlList()
+{
+ if (TestCapFlag(POP3_HAS_UIDL | POP3_UIDL_UNDEFINED))
+ {
+ m_pop3ConData->next_state_after_response = POP3_GET_UIDL_LIST;
+ m_pop3ConData->pause_for_read = true;
+ m_listpos = 0;
+ return Pop3SendData("UIDL" CRLF);
+ }
+ else
+ return SendXtndXlstMsgid();
+}
+
+
+int32_t nsPop3Protocol::GetUidlList(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ /* check list response
+ * This will get called multiple times
+ * but it's alright since command_succeeded
+ * will remain constant
+ */
+ ClearCapFlag(POP3_UIDL_UNDEFINED);
+
+ if (!m_pop3ConData->command_succeeded)
+ {
+ m_pop3ConData->next_state = POP3_SEND_XTND_XLST_MSGID;
+ m_pop3ConData->pause_for_read = false;
+ ClearCapFlag(POP3_HAS_UIDL);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ return(0);
+ }
+ else
+ {
+ SetCapFlag(POP3_HAS_UIDL);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ uint32_t ln = 0;
+ bool pauseForMoreData = false;
+ nsresult rv;
+ char *line = m_lineStreamBuffer->ReadNextLine(inputStream, ln, pauseForMoreData, &rv);
+ if (NS_FAILED(rv))
+ return -1;
+
+ if (pauseForMoreData || !line)
+ {
+ PR_Free(line);
+ m_pop3ConData->pause_for_read = true;
+ return ln;
+ }
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+
+ /* parse the line returned from the list command
+ * it looks like
+ * #msg_number uidl
+ *
+ * list data is terminated by a ".CRLF" line
+ */
+ if (!PL_strcmp(line, "."))
+ {
+ // limit the list if fewer entries than given in STAT response
+ if (m_listpos < m_pop3ConData->number_of_messages)
+ m_pop3ConData->number_of_messages = m_listpos;
+ m_pop3ConData->list_done = true;
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ m_pop3ConData->pause_for_read = false;
+ PR_Free(line);
+ return(0);
+ }
+
+ char *newStr = line;
+ char *token = NS_strtok(" ", &newStr); // msg num
+ if (token)
+ {
+ int32_t msg_num = atol(token);
+ if (++m_listpos <= m_pop3ConData->number_of_messages)
+ {
+ const char *uid = NS_strtok(" ", &newStr); // UID
+ if (!uid)
+ /* This is bad. The server didn't give us a UIDL for this message.
+ I've seen this happen when somehow the mail spool has a message
+ that contains a header that reads "X-UIDL: \n". But how that got
+ there, I have no idea; must be a server bug. Or something. */
+ uid = "";
+
+ // seeking right entry, but try the one that should it be first
+ int32_t i;
+ if(m_pop3ConData->msg_info[m_listpos - 1].msgnum == msg_num)
+ i = m_listpos - 1;
+ else
+ for(i = 0; i < m_pop3ConData->number_of_messages &&
+ m_pop3ConData->msg_info[i].msgnum != msg_num; i++)
+ ;
+
+ // only if found a matching slot
+ if (i < m_pop3ConData->number_of_messages)
+ {
+ // to protect us from memory leak in case of getting a msg num twice
+ m_pop3ConData->msg_info[i].uidl = PL_strdup(uid);
+ if (!m_pop3ConData->msg_info[i].uidl)
+ {
+ PR_Free(line);
+ return MK_OUT_OF_MEMORY;
+ }
+ }
+ }
+ }
+ PR_Free(line);
+ return(0);
+}
+
+
+
+/* this function decides if we are going to do a
+ * normal RETR or a TOP. The first time, it also decides the total number
+ * of bytes we're probably going to get.
+ */
+int32_t nsPop3Protocol::GetMsg()
+{
+ int32_t popstateTimestamp = TimeInSecondsFromPRTime(PR_Now());
+
+ if (m_pop3ConData->last_accessed_msg >= m_pop3ConData->number_of_messages)
+ {
+ /* Oh, gee, we're all done. */
+ if(m_pop3ConData->msg_del_started)
+ {
+ if (!m_pop3ConData->only_uidl)
+ {
+ if (m_pop3ConData->only_check_for_new_mail)
+ m_nsIPop3Sink->SetBiffStateAndUpdateFE(m_pop3ConData->biffstate, m_pop3ConData->really_new_messages, true);
+ /* update old style biff */
+ else
+ m_nsIPop3Sink->SetBiffStateAndUpdateFE(nsIMsgFolder::nsMsgBiffState_NewMail, m_pop3ConData->really_new_messages, false);
+ }
+ m_nsIPop3Sink->EndMailDelivery(this);
+ }
+
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ return 0;
+ }
+
+ if (m_totalDownloadSize < 0)
+ {
+ /* First time. Figure out how many bytes we're about to get.
+ If we didn't get any message info, then we are going to get
+ everything, and it's easy. Otherwise, if we only want one
+ uidl, than that's the only one we'll get. Otherwise, go
+ through each message info, decide if we're going to get that
+ message, and add the number of bytes for it. When a message is too
+ large (per user's preferences) only add the size we are supposed
+ to get. */
+ m_pop3ConData->really_new_messages = 0;
+ m_pop3ConData->real_new_counter = 1;
+ if (m_pop3ConData->msg_info)
+ {
+ m_totalDownloadSize = 0;
+ for (int32_t i = 0; i < m_pop3ConData->number_of_messages; i++)
+ {
+ if (m_pop3ConData->only_uidl)
+ {
+ if (m_pop3ConData->msg_info[i].uidl &&
+ !PL_strcmp(m_pop3ConData->msg_info[i].uidl, m_pop3ConData->only_uidl))
+ {
+ m_totalDownloadSize = m_pop3ConData->msg_info[i].size;
+ m_pop3ConData->really_new_messages = 1;
+ // we are only getting one message
+ m_pop3ConData->real_new_counter = 1;
+ break;
+ }
+ continue;
+ }
+
+ char c = 0;
+ popstateTimestamp = TimeInSecondsFromPRTime(PR_Now());
+ if (m_pop3ConData->msg_info[i].uidl)
+ {
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->uidlinfo->hash,
+ m_pop3ConData->msg_info[i].uidl);
+ if (uidlEntry)
+ {
+ c = uidlEntry->status;
+ popstateTimestamp = uidlEntry->dateReceived;
+ }
+ }
+ if ((c == KEEP) && !m_pop3ConData->leave_on_server)
+ { /* This message has been downloaded but kept on server, we
+ * no longer want to keep it there */
+ if (!m_pop3ConData->newuidl)
+ {
+ m_pop3ConData->newuidl = PL_NewHashTable(20, PL_HashString, PL_CompareStrings,
+ PL_CompareValues, &gHashAllocOps, nullptr);
+ if (!m_pop3ConData->newuidl)
+ return MK_OUT_OF_MEMORY;
+ }
+ c = DELETE_CHAR;
+ // Mark message to be deleted in new table
+ put_hash(m_pop3ConData->newuidl,
+ m_pop3ConData->msg_info[i].uidl, DELETE_CHAR, popstateTimestamp);
+ // and old one too
+ put_hash(m_pop3ConData->uidlinfo->hash,
+ m_pop3ConData->msg_info[i].uidl, DELETE_CHAR, popstateTimestamp);
+ }
+ if ((c != KEEP) && (c != DELETE_CHAR) && (c != TOO_BIG))
+ { // message left on server
+ m_totalDownloadSize += m_pop3ConData->msg_info[i].size;
+ m_pop3ConData->really_new_messages++;
+ // a message we will really download
+ }
+ }
+ }
+ else
+ {
+ m_totalDownloadSize = m_totalFolderSize;
+ }
+ if (m_pop3ConData->only_check_for_new_mail)
+ {
+ if (m_totalDownloadSize > 0)
+ {
+ m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_NewMail;
+ m_nsIPop3Sink->SetBiffStateAndUpdateFE(nsIMsgFolder::nsMsgBiffState_NewMail, m_pop3ConData->really_new_messages, true);
+ }
+ m_pop3ConData->next_state = POP3_SEND_QUIT;
+ return(0);
+ }
+ /* get the amount of available space on the drive
+ * and make sure there is enough
+ */
+ if (m_totalDownloadSize > 0) // skip all this if there aren't any messages
+ {
+ nsCOMPtr<nsIMsgFolder> folder;
+
+ // Get the current mailbox folder
+ NS_ENSURE_TRUE(m_nsIPop3Sink, -1);
+ nsresult rv = m_nsIPop3Sink->GetFolder(getter_AddRefs(folder));
+ if (NS_FAILED(rv))
+ return -1;
+
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
+ if (NS_FAILED(rv) || !mailnewsUrl)
+ return -1;
+
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ (void)mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
+
+ bool spaceNotAvailable = true;
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder(do_QueryInterface(folder, &rv));
+ if (NS_FAILED(rv) || !localFolder)
+ return -1;
+
+ // check if we have a reasonable amount of space left
+ rv = localFolder->WarnIfLocalFileTooBig(msgWindow, m_totalDownloadSize, &spaceNotAvailable);
+ if (NS_FAILED(rv) || spaceNotAvailable) {
+#ifdef DEBUG
+ printf("Not enough disk space! Raising error!\n");
+#endif
+ return -1;
+ }
+
+ // Here we know how many messages we're going to download, so let
+ // the pop3 sink know.
+ rv = m_nsIPop3Sink->SetMsgsToDownload(m_pop3ConData->really_new_messages);
+ }
+ }
+
+ /* Look at this message, and decide whether to ignore it, get it, just get
+ the TOP of it, or delete it. */
+
+ // if this is a message we've seen for the first time, we won't find it in
+ // m_pop3ConData-uidlinfo->hash. By default, we retrieve messages, unless they have a status,
+ // or are too big, in which case we figure out what to do.
+ if (m_prefAuthMethods != POP3_HAS_AUTH_USER && TestCapFlag(POP3_HAS_XSENDER))
+ m_pop3ConData->next_state = POP3_SEND_XSENDER;
+ else
+ m_pop3ConData->next_state = POP3_SEND_RETR;
+ m_pop3ConData->truncating_cur_msg = false;
+ m_pop3ConData->pause_for_read = false;
+ if (m_pop3ConData->msg_info)
+ {
+ Pop3MsgInfo* info = m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg;
+ if (m_pop3ConData->only_uidl)
+ {
+ if (info->uidl == NULL || PL_strcmp(info->uidl, m_pop3ConData->only_uidl))
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ else
+ m_pop3ConData->next_state = POP3_SEND_RETR;
+ }
+ else
+ {
+ char c = 0;
+ if (!m_pop3ConData->newuidl)
+ {
+ m_pop3ConData->newuidl = PL_NewHashTable(20, PL_HashString, PL_CompareStrings, PL_CompareValues, &gHashAllocOps, nullptr);
+ if (!m_pop3ConData->newuidl)
+ return MK_OUT_OF_MEMORY;
+ }
+ if (info->uidl)
+ {
+ Pop3UidlEntry *uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->uidlinfo->hash, info->uidl);
+ if (uidlEntry)
+ {
+ c = uidlEntry->status;
+ popstateTimestamp = uidlEntry->dateReceived;
+ }
+ }
+ if (c == DELETE_CHAR)
+ {
+ m_pop3ConData->next_state = POP3_SEND_DELE;
+ }
+ else if (c == KEEP)
+ {
+ // this is a message we've already downloaded and left on server;
+ // Advance to next message.
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ }
+ else if (c == FETCH_BODY)
+ {
+ m_pop3ConData->next_state = POP3_SEND_RETR;
+ PL_HashTableRemove (m_pop3ConData->uidlinfo->hash, (void*)info->uidl);
+ }
+ else if ((c != TOO_BIG) &&
+ (TestCapFlag(POP3_TOP_UNDEFINED | POP3_HAS_TOP)) &&
+ (m_pop3ConData->headers_only ||
+ ((m_pop3ConData->size_limit > 0) &&
+ (info->size > m_pop3ConData->size_limit) &&
+ !m_pop3ConData->only_uidl)) &&
+ info->uidl && *info->uidl)
+ {
+ // message is too big
+ m_pop3ConData->truncating_cur_msg = true;
+ m_pop3ConData->next_state = POP3_SEND_TOP;
+ put_hash(m_pop3ConData->newuidl, info->uidl, TOO_BIG, popstateTimestamp);
+ }
+ else if (c == TOO_BIG)
+ {
+ /* message previously left on server, see if the max download size
+ has changed, because we may want to download the message this time
+ around. Otherwise ignore the message, we have the header. */
+ if ((m_pop3ConData->size_limit > 0) && (info->size <=
+ m_pop3ConData->size_limit))
+ PL_HashTableRemove (m_pop3ConData->uidlinfo->hash, (void*)info->uidl);
+ // remove from our table, and download
+ else
+ {
+ m_pop3ConData->truncating_cur_msg = true;
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ // ignore this message and get next one
+ put_hash(m_pop3ConData->newuidl, info->uidl, TOO_BIG, popstateTimestamp);
+ }
+ }
+
+ if (m_pop3ConData->next_state != POP3_SEND_DELE &&
+ info->uidl)
+ {
+ /* This is a message we have decided to keep on the server. Notate
+ that now for the future. (Don't change the popstate file at all
+ if only_uidl is set; in that case, there might be brand new messages
+ on the server that we *don't* want to mark KEEP; we just want to
+ leave them around until the user next does a GetNewMail.) */
+
+ /* If this is a message we already know about (i.e., it was
+ in popstate.dat already), we need to maintain the original
+ date the message was downloaded. */
+ if (m_pop3ConData->truncating_cur_msg)
+ put_hash(m_pop3ConData->newuidl, info->uidl, TOO_BIG, popstateTimestamp);
+ else
+ put_hash(m_pop3ConData->newuidl, info->uidl, KEEP, popstateTimestamp);
+ }
+ }
+ if (m_pop3ConData->next_state == POP3_GET_MSG)
+ m_pop3ConData->last_accessed_msg++;
+ // Make sure we check the next message next time!
+ }
+ return 0;
+}
+
+
+/* start retreiving just the first 20 lines
+ */
+int32_t nsPop3Protocol::SendTop()
+{
+ char * cmd = PR_smprintf( "TOP %ld %d" CRLF,
+ m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum,
+ m_pop3ConData->headers_only ? 0 : 20);
+ int32_t status = -1;
+ if (cmd)
+ {
+ m_pop3ConData->next_state_after_response = POP3_TOP_RESPONSE;
+ m_pop3ConData->cur_msg_size = -1;
+
+ /* zero the bytes received in message in preparation for
+ * the next
+ */
+ m_bytesInMsgReceived = 0;
+ status = Pop3SendData(cmd);
+ }
+ PR_Free(cmd);
+ return status;
+}
+
+/* send the xsender command
+ */
+int32_t nsPop3Protocol::SendXsender()
+{
+ char * cmd = PR_smprintf("XSENDER %ld" CRLF, m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
+ int32_t status = -1;
+ if (cmd)
+ {
+ m_pop3ConData->next_state_after_response = POP3_XSENDER_RESPONSE;
+ status = Pop3SendData(cmd);
+ PR_Free(cmd);
+ }
+ return status;
+}
+
+int32_t nsPop3Protocol::XsenderResponse()
+{
+ m_pop3ConData->seenFromHeader = false;
+ m_senderInfo = "";
+
+ if (m_pop3ConData->command_succeeded) {
+ if (m_commandResponse.Length() > 4)
+ m_senderInfo = m_commandResponse;
+ }
+ else {
+ ClearCapFlag(POP3_HAS_XSENDER);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ if (m_pop3ConData->truncating_cur_msg)
+ m_pop3ConData->next_state = POP3_SEND_TOP;
+ else
+ m_pop3ConData->next_state = POP3_SEND_RETR;
+ return 0;
+}
+
+/* retreive the whole message
+ */
+int32_t
+nsPop3Protocol::SendRetr()
+{
+
+ char * cmd = PR_smprintf("RETR %ld" CRLF, m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
+ int32_t status = -1;
+ if (cmd)
+ {
+ m_pop3ConData->next_state_after_response = POP3_RETR_RESPONSE;
+ m_pop3ConData->cur_msg_size = -1;
+
+
+ /* zero the bytes received in message in preparation for
+ * the next
+ */
+ m_bytesInMsgReceived = 0;
+
+ if (m_pop3ConData->only_uidl)
+ {
+ /* Display bytes if we're only downloading one message. */
+ PR_ASSERT(!m_pop3ConData->graph_progress_bytes_p);
+ UpdateProgressPercent(0, m_totalDownloadSize);
+ m_pop3ConData->graph_progress_bytes_p = true;
+ }
+ else
+ {
+ nsString finalString;
+ mozilla::DebugOnly<nsresult> rv =
+ FormatCounterString(NS_LITERAL_STRING("receivingMessages"),
+ m_pop3ConData->real_new_counter,
+ m_pop3ConData->really_new_messages,
+ finalString);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "couldn't format string");
+ if (mProgressEventSink) {
+ rv = mProgressEventSink->OnStatus(this, m_channelContext, NS_OK,
+ finalString.get());
+ NS_ASSERTION(NS_SUCCEEDED(rv), "dropping error result");
+ }
+ }
+
+ status = Pop3SendData(cmd);
+ } // if cmd
+ PR_Free(cmd);
+ return status;
+}
+
+/* digest the message
+ */
+int32_t
+nsPop3Protocol::RetrResponse(nsIInputStream* inputStream,
+ uint32_t length)
+{
+ uint32_t buffer_size;
+ int32_t flags = 0;
+ char *uidl = NULL;
+ nsresult rv;
+ uint32_t status = 0;
+
+ if(m_pop3ConData->cur_msg_size == -1)
+ {
+ /* this is the beginning of a message
+ * get the response code and byte size
+ */
+ if(!m_pop3ConData->command_succeeded)
+ return Error("pop3RetrFailure");
+
+ /* a successful RETR response looks like: #num_bytes Junk
+ from TOP we only get the +OK and data
+ */
+ if (m_pop3ConData->truncating_cur_msg)
+ { /* TOP, truncated message */
+ flags |= nsMsgMessageFlags::Partial;
+ }
+ else
+ {
+ nsCString cmdResp(m_commandResponse);
+ char *newStr = cmdResp.BeginWriting();
+ char *num = NS_strtok( " ", &newStr);
+ if (num)
+ m_pop3ConData->cur_msg_size = atol(num);
+ m_commandResponse = newStr;
+ }
+
+ /* RETR complete message */
+ if (!m_senderInfo.IsEmpty())
+ flags |= nsMsgMessageFlags::SenderAuthed;
+
+ if(m_pop3ConData->cur_msg_size <= 0)
+ {
+ if (m_pop3ConData->msg_info)
+ m_pop3ConData->cur_msg_size = m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].size;
+ else
+ m_pop3ConData->cur_msg_size = 0;
+ }
+
+ if (m_pop3ConData->msg_info &&
+ m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].uidl)
+ uidl = m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].uidl;
+
+ m_pop3ConData->parsed_bytes = 0;
+ m_pop3ConData->pop3_size = m_pop3ConData->cur_msg_size;
+ m_pop3ConData->assumed_end = false;
+
+ m_pop3Server->GetDotFix(&m_pop3ConData->dot_fix);
+
+ MOZ_LOG(POP3LOGMODULE,LogLevel::Info,
+ (POP3LOG("Opening message stream: MSG_IncorporateBegin")));
+
+ /* open the message stream so we have someplace
+ * to put the data
+ */
+ m_pop3ConData->real_new_counter++;
+ /* (rb) count only real messages being downloaded */
+ rv = m_nsIPop3Sink->IncorporateBegin(uidl, m_url, flags,
+ &m_pop3ConData->msg_closure);
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("Done opening message stream!")));
+
+ if(!m_pop3ConData->msg_closure || NS_FAILED(rv))
+ return Error("pop3MessageWriteError");
+ }
+
+ m_pop3ConData->pause_for_read = true;
+
+ bool pauseForMoreData = false;
+ char *line = m_lineStreamBuffer->ReadNextLine(inputStream, status, pauseForMoreData, &rv, true);
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+ if (NS_FAILED(rv))
+ return -1;
+
+ buffer_size = status;
+
+ if (status == 0 && !line) // no bytes read in...
+ return (0);
+
+ if (m_pop3ConData->msg_closure) /* not done yet */
+ {
+ // buffer the line we just read in, and buffer all remaining lines in the stream
+ status = buffer_size;
+ do
+ {
+ if (m_pop3ConData->msg_closure)
+ {
+ rv = HandleLine(line, buffer_size);
+ if (NS_FAILED(rv))
+ return Error("pop3MessageWriteError");
+
+ // buffer_size already includes MSG_LINEBREAK_LEN so
+ // subtract and add CRLF
+ // but not really sure we always had CRLF in input since
+ // we also treat a single LF as line ending!
+ m_pop3ConData->parsed_bytes += buffer_size - MSG_LINEBREAK_LEN + 2;
+ }
+
+ // now read in the next line
+ PR_Free(line);
+ line = m_lineStreamBuffer->ReadNextLine(inputStream, buffer_size,
+ pauseForMoreData, &rv, true);
+ if (NS_FAILED(rv))
+ return -1;
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("RECV: %s"), line));
+ // buffer_size already includes MSG_LINEBREAK_LEN so
+ // subtract and add CRLF
+ // but not really sure we always had CRLF in input since
+ // we also treat a single LF as line ending!
+ status += buffer_size - MSG_LINEBREAK_LEN + 2;
+ } while (line);
+ }
+
+ buffer_size = status; // status holds # bytes we've actually buffered so far...
+
+ /* normal read. Yay! */
+ if ((int32_t) (m_bytesInMsgReceived + buffer_size) > m_pop3ConData->cur_msg_size)
+ buffer_size = m_pop3ConData->cur_msg_size - m_bytesInMsgReceived;
+
+ m_bytesInMsgReceived += buffer_size;
+ m_totalBytesReceived += buffer_size;
+
+ // *** jefft in case of the message size that server tells us is different
+ // from the actual message size
+ if (pauseForMoreData && m_pop3ConData->dot_fix &&
+ m_pop3ConData->assumed_end && m_pop3ConData->msg_closure)
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ if (NS_SUCCEEDED(rv))
+ rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
+ rv = m_nsIPop3Sink->IncorporateComplete(msgWindow,
+ m_pop3ConData->truncating_cur_msg ? m_pop3ConData->cur_msg_size : 0);
+
+ // The following was added to prevent the loss of Data when we try
+ // and write to somewhere we don't have write access error to (See
+ // bug 62480)
+ // (Note: This is only a temp hack until the underlying XPCOM is
+ // fixed to return errors)
+
+ if (NS_FAILED(rv))
+ return Error((rv == NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD) ?
+ "pop3TmpDownloadError" :
+ "pop3MessageWriteError");
+
+ m_pop3ConData->msg_closure = nullptr;
+ }
+
+ if (!m_pop3ConData->msg_closure)
+ /* meaning _handle_line read ".\r\n" at end-of-msg */
+ {
+ m_pop3ConData->pause_for_read = false;
+
+ if (m_pop3ConData->truncating_cur_msg ||
+ m_pop3ConData->leave_on_server )
+ {
+ Pop3UidlEntry *uidlEntry = NULL;
+ Pop3MsgInfo* info = m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg;
+
+ /* Check for filter actions - FETCH or DELETE */
+ if ((m_pop3ConData->newuidl) && (info->uidl))
+ uidlEntry = (Pop3UidlEntry *)PL_HashTableLookup(m_pop3ConData->newuidl, info->uidl);
+
+ if (uidlEntry && uidlEntry->status == FETCH_BODY &&
+ m_pop3ConData->truncating_cur_msg)
+ {
+ /* A filter decided to retrieve this full msg.
+ Use GetMsg() so the popstate will update correctly,
+ but don't let this msg get counted twice. */
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ m_pop3ConData->real_new_counter--;
+ /* Make sure we don't try to come through here again. */
+ PL_HashTableRemove (m_pop3ConData->newuidl, (void*)info->uidl);
+ put_hash(m_pop3ConData->uidlinfo->hash, info->uidl, FETCH_BODY, uidlEntry->dateReceived);
+
+ } else if (uidlEntry && uidlEntry->status == DELETE_CHAR)
+ {
+ // A filter decided to delete this msg from the server
+ m_pop3ConData->next_state = POP3_SEND_DELE;
+ } else
+ {
+ /* We've retrieved all or part of this message, but we want to
+ keep it on the server. Go on to the next message. */
+ m_pop3ConData->last_accessed_msg++;
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ }
+ if (m_pop3ConData->only_uidl)
+ {
+ /* GetMsg didn't update this field. Do it now */
+ uidlEntry = (Pop3UidlEntry *)PL_HashTableLookup(m_pop3ConData->uidlinfo->hash, m_pop3ConData->only_uidl);
+ NS_ASSERTION(uidlEntry, "uidl not found in uidlinfo");
+ if (uidlEntry)
+ put_hash(m_pop3ConData->uidlinfo->hash, m_pop3ConData->only_uidl, KEEP, uidlEntry->dateReceived);
+ }
+ }
+ else
+ {
+ m_pop3ConData->next_state = POP3_SEND_DELE;
+ }
+
+ /* if we didn't get the whole message add the bytes that we didn't get
+ to the bytes received part so that the progress percent stays sane.
+ */
+ if (m_bytesInMsgReceived < m_pop3ConData->cur_msg_size)
+ m_totalBytesReceived += (m_pop3ConData->cur_msg_size -
+ m_bytesInMsgReceived);
+ }
+
+ /* set percent done to portion of total bytes of all messages
+ that we're going to download. */
+ if (m_totalDownloadSize)
+ UpdateProgressPercent(m_totalBytesReceived, m_totalDownloadSize);
+
+ PR_Free(line);
+ return(0);
+}
+
+
+int32_t
+nsPop3Protocol::TopResponse(nsIInputStream* inputStream, uint32_t length)
+{
+ if (TestCapFlag(POP3_TOP_UNDEFINED))
+ {
+ ClearCapFlag(POP3_TOP_UNDEFINED);
+ if (m_pop3ConData->command_succeeded)
+ SetCapFlag(POP3_HAS_TOP);
+ else
+ ClearCapFlag(POP3_HAS_TOP);
+ m_pop3Server->SetPop3CapabilityFlags(m_pop3ConData->capability_flags);
+ }
+
+ if(m_pop3ConData->cur_msg_size == -1 && /* first line after TOP command sent */
+ !m_pop3ConData->command_succeeded) /* and TOP command failed */
+ {
+ /* TOP doesn't work so we can't retrieve the first part of this msg.
+ So just go download the whole thing, and warn the user.
+
+ Note that the progress bar will not be accurate in this case.
+ Oops. #### */
+ m_pop3ConData->truncating_cur_msg = false;
+
+ nsString statusTemplate;
+ mLocalBundle->GetStringFromName(
+ u"pop3ServerDoesNotSupportTopCommand",
+ getter_Copies(statusTemplate));
+ if (!statusTemplate.IsEmpty())
+ {
+ nsAutoCString hostName;
+ char16_t * statusString = nullptr;
+ m_url->GetHost(hostName);
+
+ statusString = nsTextFormatter::smprintf(statusTemplate.get(), hostName.get());
+ UpdateStatusWithString(statusString);
+ nsTextFormatter::smprintf_free(statusString);
+ }
+
+ if (m_prefAuthMethods != POP3_HAS_AUTH_USER &&
+ TestCapFlag(POP3_HAS_XSENDER))
+ m_pop3ConData->next_state = POP3_SEND_XSENDER;
+ else
+ m_pop3ConData->next_state = POP3_SEND_RETR;
+ return(0);
+ }
+
+ /* If TOP works, we handle it in the same way as RETR. */
+ return RetrResponse(inputStream, length);
+}
+
+/* line is handed over as null-terminated string with MSG_LINEBREAK */
+nsresult
+nsPop3Protocol::HandleLine(char *line, uint32_t line_length)
+{
+ nsresult rv = NS_OK;
+
+ NS_ASSERTION(m_pop3ConData->msg_closure, "m_pop3ConData->msg_closure is null in nsPop3Protocol::HandleLine()");
+ if (!m_pop3ConData->msg_closure)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!m_senderInfo.IsEmpty() && !m_pop3ConData->seenFromHeader)
+ {
+ if (line_length > 6 && !PL_strncasecmp("From: ", line, 6))
+ {
+ m_pop3ConData->seenFromHeader = true;
+ if (PL_strstr(line, m_senderInfo.get()) == NULL)
+ m_nsIPop3Sink->SetSenderAuthedFlag(m_pop3ConData->msg_closure,
+ false);
+ }
+ }
+
+ // line contains only a single dot and linebreak -> message end
+ if (line_length == 1 + MSG_LINEBREAK_LEN && line[0] == '.')
+ {
+ m_pop3ConData->assumed_end = true; /* in case byte count from server is */
+ /* wrong, mark we may have had the end */
+ if (!m_pop3ConData->dot_fix || m_pop3ConData->truncating_cur_msg ||
+ (m_pop3ConData->parsed_bytes >= (m_pop3ConData->pop3_size -3)))
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(m_url, &rv);
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ if (NS_SUCCEEDED(rv))
+ rv = mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
+ rv = m_nsIPop3Sink->IncorporateComplete(msgWindow,
+ m_pop3ConData->truncating_cur_msg ? m_pop3ConData->cur_msg_size : 0);
+
+ // The following was added to prevent the loss of Data when we try
+ // and write to somewhere we don't have write access error to (See
+ // bug 62480)
+ // (Note: This is only a temp hack until the underlying XPCOM is
+ // fixed to return errors)
+
+ if (NS_FAILED(rv)) {
+ Error((rv == NS_MSG_ERROR_COPYING_FROM_TMP_DOWNLOAD) ?
+ "pop3TmpDownloadError" :
+ "pop3MessageWriteError");
+ return rv;
+ }
+
+ m_pop3ConData->msg_closure = nullptr;
+ return rv;
+ }
+ }
+ /* Check if the line begins with the termination octet. If so
+ and if another termination octet follows, we step over the
+ first occurence of it. */
+ else if (line_length > 1 && line[0] == '.' && line[1] == '.') {
+ line++;
+ line_length--;
+
+ }
+
+ return m_nsIPop3Sink->IncorporateWrite(line, line_length);
+}
+
+int32_t nsPop3Protocol::SendDele()
+{
+ /* increment the last accessed message since we have now read it
+ */
+ char * cmd = PR_smprintf("DELE %ld" CRLF, m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg].msgnum);
+ m_pop3ConData->last_accessed_msg++;
+ int32_t status = -1;
+ if (cmd)
+ {
+ m_pop3ConData->next_state_after_response = POP3_DELE_RESPONSE;
+ status = Pop3SendData(cmd);
+ }
+ PR_Free(cmd);
+ return status;
+}
+
+int32_t nsPop3Protocol::DeleResponse()
+{
+ Pop3UidlHost *host = NULL;
+
+ host = m_pop3ConData->uidlinfo;
+
+ /* the return from the delete will come here
+ */
+ if(!m_pop3ConData->command_succeeded)
+ return Error("pop3DeleFailure");
+
+
+ /* ###chrisf
+ the delete succeeded. Write out state so that we
+ keep track of all the deletes which have not yet been
+ committed on the server. Flush this state upon successful
+ QUIT.
+
+ We will do this by adding each successfully deleted message id
+ to a list which we will write out to popstate.dat in
+ net_pop3_write_state().
+ */
+ if (host)
+ {
+ if (m_pop3ConData->msg_info &&
+ m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl)
+ {
+ if (m_pop3ConData->newuidl)
+ if (m_pop3ConData->leave_on_server)
+ {
+ PL_HashTableRemove(m_pop3ConData->newuidl, (void*)
+ m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl);
+ }
+ else
+ {
+ put_hash(m_pop3ConData->newuidl,
+ m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl, DELETE_CHAR, 0);
+ /* kill message in new hash table */
+ }
+ else
+ PL_HashTableRemove(host->hash,
+ (void*) m_pop3ConData->msg_info[m_pop3ConData->last_accessed_msg-1].uidl);
+ }
+ }
+
+ m_pop3ConData->next_state = POP3_GET_MSG;
+ m_pop3ConData->pause_for_read = false;
+
+ return(0);
+}
+
+
+int32_t
+nsPop3Protocol::CommitState(bool remove_last_entry)
+{
+ // only use newuidl if we successfully finished looping through all the
+ // messages in the inbox.
+ if (m_pop3ConData->newuidl)
+ {
+ if (m_pop3ConData->last_accessed_msg >= m_pop3ConData->number_of_messages)
+ {
+ PL_HashTableDestroy(m_pop3ConData->uidlinfo->hash);
+ m_pop3ConData->uidlinfo->hash = m_pop3ConData->newuidl;
+ m_pop3ConData->newuidl = nullptr;
+ }
+ else
+ {
+ /* If we are leaving messages on the server, pull out the last
+ uidl from the hash, because it might have been put in there before
+ we got it into the database.
+ */
+ if (remove_last_entry && m_pop3ConData->msg_info &&
+ !m_pop3ConData->only_uidl && m_pop3ConData->newuidl->nentries > 0)
+ {
+ Pop3MsgInfo* info = m_pop3ConData->msg_info + m_pop3ConData->last_accessed_msg;
+ if (info && info->uidl)
+ {
+ mozilla::DebugOnly<bool> val = PL_HashTableRemove(m_pop3ConData->newuidl,
+ info->uidl);
+ NS_ASSERTION(val, "uidl not in hash table");
+ }
+ }
+
+ // Add the entries in newuidl to m_pop3ConData->uidlinfo->hash to keep
+ // track of the messages we *did* download in this session.
+ PL_HashTableEnumerateEntries(m_pop3ConData->newuidl, net_pop3_copy_hash_entries, (void *)m_pop3ConData->uidlinfo->hash);
+ }
+ }
+
+ if (!m_pop3ConData->only_check_for_new_mail)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIFile> mailDirectory;
+
+ // get the mail directory
+ nsCOMPtr<nsIMsgIncomingServer> server =
+ do_QueryInterface(m_pop3Server, &rv);
+ if (NS_FAILED(rv)) return -1;
+
+ rv = server->GetLocalPath(getter_AddRefs(mailDirectory));
+ if (NS_FAILED(rv)) return -1;
+
+ // write the state in the mail directory
+ net_pop3_write_state(m_pop3ConData->uidlinfo, mailDirectory.get());
+ }
+ return 0;
+}
+
+
+/* NET_process_Pop3 will control the state machine that
+ * loads messages from a pop3 server
+ *
+ * returns negative if the transfer is finished or error'd out
+ *
+ * returns zero or more if the transfer needs to be continued.
+ */
+nsresult nsPop3Protocol::ProcessProtocolState(nsIURI * url, nsIInputStream * aInputStream,
+ uint64_t sourceOffset, uint32_t aLength)
+{
+ int32_t status = 0;
+ bool urlStatusSet = false;
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(m_url);
+
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info, (POP3LOG("Entering NET_ProcessPop3 %d"),
+ aLength));
+
+ m_pop3ConData->pause_for_read = false; /* already paused; reset */
+
+ if(m_username.IsEmpty())
+ {
+ // net_pop3_block = false;
+ Error("pop3UsernameUndefined");
+ return NS_MSG_SERVER_USERNAME_MISSING;
+ }
+
+ while(!m_pop3ConData->pause_for_read)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Info,
+ (POP3LOG("Entering state: %d"), m_pop3ConData->next_state));
+
+ switch(m_pop3ConData->next_state)
+ {
+ case POP3_READ_PASSWORD:
+ // This is a separate state so that we're waiting for the user to type
+ // in a password while we don't actually have a connection to the pop
+ // server open; this saves us from having to worry about the server
+ // timing out on us while we wait for user input.
+ if (NS_FAILED(StartGetAsyncPassword(POP3_OBTAIN_PASSWORD_EARLY)))
+ status = -1;
+ break;
+ case POP3_FINISH_OBTAIN_PASSWORD_EARLY:
+ {
+ if (m_passwordResult.IsEmpty() || m_username.IsEmpty())
+ {
+ status = MK_POP3_PASSWORD_UNDEFINED;
+ m_pop3ConData->biffstate = nsIMsgFolder::nsMsgBiffState_Unknown;
+ m_nsIPop3Sink->SetBiffStateAndUpdateFE(m_pop3ConData->biffstate, 0, false);
+
+ /* update old style biff */
+ m_pop3ConData->next_state = POP3_FREE;
+ m_pop3ConData->pause_for_read = false;
+ break;
+ }
+
+ m_pop3ConData->pause_for_read = false;
+ // we are already connected so just go on and send the username
+ if (m_prefAuthMethods == POP3_HAS_AUTH_USER)
+ {
+ m_currentAuthMethod = POP3_HAS_AUTH_USER;
+ m_pop3ConData->next_state = POP3_SEND_USERNAME;
+ }
+ else
+ {
+ if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
+ m_pop3ConData->next_state = POP3_SEND_AUTH;
+ else
+ m_pop3ConData->next_state = POP3_SEND_CAPA;
+ }
+ break;
+ }
+
+
+ case POP3_START_CONNECT:
+ {
+ m_pop3ConData->next_state = POP3_FINISH_CONNECT;
+ m_pop3ConData->pause_for_read = false;
+ break;
+ }
+
+ case POP3_FINISH_CONNECT:
+ {
+ m_pop3ConData->pause_for_read = false;
+ m_pop3ConData->next_state = POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE;
+ break;
+ }
+
+ case POP3_WAIT_FOR_RESPONSE:
+ status = WaitForResponse(aInputStream, aLength);
+ break;
+
+ case POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE:
+ {
+ status = WaitForStartOfConnectionResponse(aInputStream, aLength);
+
+ if(status)
+ {
+ if (m_prefAuthMethods == POP3_HAS_AUTH_USER)
+ {
+ m_currentAuthMethod = POP3_HAS_AUTH_USER;
+ m_pop3ConData->next_state = POP3_SEND_USERNAME;
+ }
+ else
+ {
+ if (TestCapFlag(POP3_AUTH_MECH_UNDEFINED))
+ m_pop3ConData->next_state = POP3_SEND_AUTH;
+ else
+ m_pop3ConData->next_state = POP3_SEND_CAPA;
+ }
+ }
+
+ break;
+ }
+
+ case POP3_SEND_AUTH:
+ status = SendAuth();
+ break;
+
+ case POP3_AUTH_RESPONSE:
+ status = AuthResponse(aInputStream, aLength);
+ break;
+
+ case POP3_SEND_CAPA:
+ status = SendCapa();
+ break;
+
+ case POP3_CAPA_RESPONSE:
+ status = CapaResponse(aInputStream, aLength);
+ break;
+
+ case POP3_TLS_RESPONSE:
+ status = SendTLSResponse();
+ break;
+
+ case POP3_PROCESS_AUTH:
+ status = ProcessAuth();
+ break;
+
+ case POP3_NEXT_AUTH_STEP:
+ status = NextAuthStep();
+ break;
+
+ case POP3_AUTH_LOGIN:
+ status = AuthLogin();
+ break;
+
+ case POP3_AUTH_LOGIN_RESPONSE:
+ status = AuthLoginResponse();
+ break;
+
+ case POP3_AUTH_NTLM:
+ status = AuthNtlm();
+ break;
+
+ case POP3_AUTH_NTLM_RESPONSE:
+ status = AuthNtlmResponse();
+ break;
+
+ case POP3_AUTH_GSSAPI:
+ status = AuthGSSAPI();
+ break;
+
+ case POP3_AUTH_GSSAPI_FIRST:
+ UpdateStatus(u"hostContact");
+ status = AuthGSSAPIResponse(true);
+ break;
+
+ case POP3_AUTH_GSSAPI_STEP:
+ status = AuthGSSAPIResponse(false);
+ break;
+
+ case POP3_SEND_USERNAME:
+ if (NS_FAILED(StartGetAsyncPassword(POP3_OBTAIN_PASSWORD_BEFORE_USERNAME)))
+ status = -1;
+ break;
+
+ case POP3_OBTAIN_PASSWORD_BEFORE_USERNAME:
+ status = -1;
+ break;
+
+ case POP3_FINISH_OBTAIN_PASSWORD_BEFORE_USERNAME:
+ UpdateStatus(u"hostContact");
+ status = SendUsername();
+ break;
+
+ case POP3_SEND_PASSWORD:
+ if (NS_FAILED(StartGetAsyncPassword(POP3_OBTAIN_PASSWORD_BEFORE_PASSWORD)))
+ status = -1;
+ break;
+
+ case POP3_FINISH_OBTAIN_PASSWORD_BEFORE_PASSWORD:
+ status = SendPassword();
+ break;
+
+ case POP3_SEND_GURL:
+ status = SendGurl();
+ break;
+
+ case POP3_GURL_RESPONSE:
+ status = GurlResponse();
+ break;
+
+ case POP3_SEND_STAT:
+ status = SendStat();
+ break;
+
+ case POP3_GET_STAT:
+ status = GetStat();
+ break;
+
+ case POP3_SEND_LIST:
+ status = SendList();
+ break;
+
+ case POP3_GET_LIST:
+ status = GetList(aInputStream, aLength);
+ break;
+
+ case POP3_SEND_UIDL_LIST:
+ status = SendUidlList();
+ break;
+
+ case POP3_GET_UIDL_LIST:
+ status = GetUidlList(aInputStream, aLength);
+ break;
+
+ case POP3_SEND_XTND_XLST_MSGID:
+ status = SendXtndXlstMsgid();
+ break;
+
+ case POP3_GET_XTND_XLST_MSGID:
+ status = GetXtndXlstMsgid(aInputStream, aLength);
+ break;
+
+ case POP3_GET_MSG:
+ status = GetMsg();
+ break;
+
+ case POP3_SEND_TOP:
+ status = SendTop();
+ break;
+
+ case POP3_TOP_RESPONSE:
+ status = TopResponse(aInputStream, aLength);
+ break;
+
+ case POP3_SEND_XSENDER:
+ status = SendXsender();
+ break;
+
+ case POP3_XSENDER_RESPONSE:
+ status = XsenderResponse();
+ break;
+
+ case POP3_SEND_RETR:
+ status = SendRetr();
+ break;
+
+ case POP3_RETR_RESPONSE:
+ status = RetrResponse(aInputStream, aLength);
+ break;
+
+ case POP3_SEND_DELE:
+ status = SendDele();
+ break;
+
+ case POP3_DELE_RESPONSE:
+ status = DeleResponse();
+ break;
+
+ case POP3_SEND_QUIT:
+ /* attempt to send a server quit command. Since this means
+ everything went well, this is a good time to update the
+ status file and the FE's biff state.
+ */
+ if (!m_pop3ConData->only_uidl)
+ {
+ /* update old style biff */
+ if (!m_pop3ConData->only_check_for_new_mail)
+ {
+ /* We don't want to pop up a warning message any more (see
+ bug 54116), so instead we put the "no new messages" or
+ "retrieved x new messages"
+ in the status line. Unfortunately, this tends to be running
+ in a progress pane, so we try to get the real pane and
+ show the message there. */
+
+ if (m_totalDownloadSize <= 0)
+ {
+ UpdateStatus(u"noNewMessages");
+ /* There are no new messages. */
+ }
+ else
+ {
+ nsString statusString;
+ nsresult rv = FormatCounterString(NS_LITERAL_STRING("receivedMsgs"),
+ m_pop3ConData->real_new_counter - 1,
+ m_pop3ConData->really_new_messages,
+ statusString);
+ if (NS_SUCCEEDED(rv))
+ UpdateStatusWithString(statusString.get());
+ }
+ }
+ }
+
+ status = Pop3SendData("QUIT" CRLF);
+ m_pop3ConData->next_state = POP3_WAIT_FOR_RESPONSE;
+ m_pop3ConData->next_state_after_response = POP3_QUIT_RESPONSE;
+ break;
+
+ case POP3_QUIT_RESPONSE:
+ if(m_pop3ConData->command_succeeded)
+ {
+ /* the QUIT succeeded. We can now flush the state in popstate.dat which
+ keeps track of any uncommitted DELE's */
+
+ /* clear the hash of all our uncommitted deletes */
+ if (!m_pop3ConData->leave_on_server && m_pop3ConData->newuidl)
+ {
+ PL_HashTableEnumerateEntries(m_pop3ConData->newuidl,
+ net_pop3_remove_messages_marked_delete,
+ (void *)m_pop3ConData);
+ }
+ m_pop3ConData->next_state = POP3_DONE;
+ }
+ else
+ {
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ }
+ break;
+
+ case POP3_DONE:
+ CommitState(false);
+ m_pop3ConData->urlStatus = NS_OK;
+ urlStatusSet = true;
+ m_pop3ConData->next_state = POP3_FREE;
+ break;
+
+ case POP3_ERROR_DONE:
+ /* write out the state */
+ if(m_pop3ConData->list_done)
+ CommitState(true);
+
+ if(m_pop3ConData->msg_closure)
+ {
+ m_nsIPop3Sink->IncorporateAbort(m_pop3ConData->only_uidl != nullptr);
+ m_pop3ConData->msg_closure = NULL;
+ m_nsIPop3Sink->AbortMailDelivery(this);
+ }
+
+ if(m_pop3ConData->msg_del_started)
+ {
+ nsString statusString;
+ nsresult rv = FormatCounterString(NS_LITERAL_STRING("receivedMsgs"),
+ m_pop3ConData->real_new_counter - 1,
+ m_pop3ConData->really_new_messages,
+ statusString);
+ if (NS_SUCCEEDED(rv))
+ UpdateStatusWithString(statusString.get());
+
+ NS_ASSERTION (!TestFlag(POP3_PASSWORD_FAILED), "POP3_PASSWORD_FAILED set when del_started");
+ m_nsIPop3Sink->AbortMailDelivery(this);
+ }
+ { // this brace is to avoid compiler error about vars in switch case.
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+
+ if (mailnewsurl)
+ mailnewsurl->GetMsgWindow(getter_AddRefs(msgWindow));
+ // no msgWindow means no re-prompt, so treat as error.
+ if (TestFlag(POP3_PASSWORD_FAILED) && msgWindow)
+ {
+ // We get here because the password was wrong.
+ if (!m_socketIsOpen && mailnewsurl)
+ {
+ // The server dropped the connection, so we're going
+ // to re-run the url.
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug,
+ (POP3LOG("need to rerun url because connection dropped during auth")));
+ m_needToRerunUrl = true;
+ return NS_OK;
+ }
+ m_pop3ConData->next_state = POP3_READ_PASSWORD;
+ m_pop3ConData->command_succeeded = true;
+ status = 0;
+ break;
+ }
+ else
+ /* Else we got a "real" error, so finish up. */
+ m_pop3ConData->next_state = POP3_FREE;
+ }
+ m_pop3ConData->urlStatus = NS_ERROR_FAILURE;
+ urlStatusSet = true;
+ m_pop3ConData->pause_for_read = false;
+ break;
+
+ case POP3_FREE:
+ {
+ UpdateProgressPercent(0,0); // clear out the progress meter
+ nsCOMPtr<nsIMsgIncomingServer> server = do_QueryInterface(m_pop3Server);
+ if (server)
+ {
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Clearing server busy in POP3_FREE")));
+ server->SetServerBusy(false); // the server is now not busy
+ }
+ MOZ_LOG(POP3LOGMODULE, LogLevel::Debug, (POP3LOG("Clearing running protocol in POP3_FREE")));
+ CloseSocket();
+ m_pop3Server->SetRunningProtocol(nullptr);
+ if (mailnewsurl && urlStatusSet)
+ mailnewsurl->SetUrlState(false, m_pop3ConData->urlStatus);
+
+ m_url = nullptr;
+ return NS_OK;
+ }
+ default:
+ NS_ERROR("Got to unexpected state in nsPop3Protocol::ProcessProtocolState");
+ status = -1;
+ } /* end switch */
+
+ if((status < 0) && m_pop3ConData->next_state != POP3_FREE)
+ {
+ m_pop3ConData->pause_for_read = false;
+ m_pop3ConData->next_state = POP3_ERROR_DONE;
+ }
+
+ } /* end while */
+
+ return NS_OK;
+
+}
+
+NS_IMETHODIMP nsPop3Protocol::MarkMessages(nsTArray<Pop3UidlEntry*> *aUIDLArray)
+{
+ NS_ENSURE_ARG_POINTER(aUIDLArray);
+ uint32_t count = aUIDLArray->Length();
+
+ for (uint32_t i = 0; i < count; i++)
+ {
+ bool changed;
+ if (m_pop3ConData->newuidl)
+ MarkMsgInHashTable(m_pop3ConData->newuidl, aUIDLArray->ElementAt(i), &changed);
+ if (m_pop3ConData->uidlinfo)
+ MarkMsgInHashTable(m_pop3ConData->uidlinfo->hash, aUIDLArray->ElementAt(i), &changed);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Protocol::CheckMessage(const char *aUidl, bool *aBool)
+{
+ Pop3UidlEntry *uidlEntry = nullptr;
+
+ if (aUidl)
+ {
+ if (m_pop3ConData->newuidl)
+ uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->newuidl, aUidl);
+ else if (m_pop3ConData->uidlinfo)
+ uidlEntry = (Pop3UidlEntry *) PL_HashTableLookup(m_pop3ConData->uidlinfo->hash, aUidl);
+ }
+
+ *aBool = uidlEntry ? true : false;
+ return NS_OK;
+}
+
+
+/* Function for finding an APOP Timestamp and simple check
+ it for its validity. If returning NS_OK m_ApopTimestamp
+ contains the validated substring of m_commandResponse. */
+nsresult nsPop3Protocol::GetApopTimestamp()
+{
+ int32_t startMark = m_commandResponse.Length(), endMark = -1;
+
+ while (true)
+ {
+ // search for previous <
+ if ((startMark = MsgRFindChar(m_commandResponse, '<', startMark - 1)) < 0)
+ return NS_ERROR_FAILURE;
+
+ // search for next >
+ if ((endMark = m_commandResponse.FindChar('>', startMark)) < 0)
+ continue;
+
+ // look for an @ between start and end as a raw test
+ int32_t at = m_commandResponse.FindChar('@', startMark);
+ if (at < 0 || at >= endMark)
+ continue;
+
+ // now test if sub only consists of chars in ASCII range
+ nsCString sub(Substring(m_commandResponse, startMark, endMark - startMark + 1));
+ if (NS_IsAscii(sub.get()))
+ {
+ // set m_ApopTimestamp to the validated substring
+ m_ApopTimestamp.Assign(sub);
+ break;
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsPop3Protocol.h b/mailnews/local/src/nsPop3Protocol.h
new file mode 100644
index 000000000..a937427d1
--- /dev/null
+++ b/mailnews/local/src/nsPop3Protocol.h
@@ -0,0 +1,402 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsPop3Protocol_h__
+#define nsPop3Protocol_h__
+
+#include "mozilla/Attributes.h"
+#include "nsIStreamListener.h"
+#include "nsIOutputStream.h"
+#include "nsIInputStream.h"
+#include "nsIPop3URL.h"
+#include "nsIPop3Sink.h"
+#include "nsMsgLineBuffer.h"
+#include "nsMsgProtocol.h"
+#include "nsIPop3Protocol.h"
+#include "MailNewsTypes.h"
+#include "nsIStringBundle.h"
+#include "nsIMsgStatusFeedback.h"
+#include "nsIMsgFolder.h" // TO include biffState enum. Change to bool later...
+#include "nsIAuthModule.h"
+#include "nsITimer.h"
+#include "nsIMsgAsyncPrompter.h"
+
+#include "prerror.h"
+#include "plhash.h"
+#include "nsCOMPtr.h"
+
+/* A more guaranteed way of making sure that we never get duplicate messages
+is to always get each message's UIDL (if the server supports it)
+and use these for storing up deletes which were not committed on the
+server. Based on our experience, it looks like we do NOT need to
+do this (it has performance tradeoffs, etc.). To turn it on, three
+things need to happen: #define POP_ALWAYS_USE_UIDL_FOR_DUPLICATES, verify that
+the uidl's are correctly getting added when the delete response is received,
+and change the POP3_QUIT_RESPONSE state to flush the newly committed deletes. */
+
+/*
+ * Cannot have the following line uncommented. It is defined.
+ * #ifdef POP_ALWAYS_USE_UIDL_FOR_DUPLICATES will always be evaluated
+ * as true.
+ *
+#define POP_ALWAYS_USE_UIDL_FOR_DUPLICATES 0
+ *
+ */
+
+#define MK_OUT_OF_MEMORY -207
+#define MK_POP3_PASSWORD_UNDEFINED -313
+#define XP_NO_ANSWER 14401
+#define XP_THE_PREVIOUSLY_ENTERED_PASSWORD_IS_INVALID_ETC 14405
+#define XP_PASSWORD_FOR_POP3_USER 14590
+
+#define OUTPUT_BUFFER_SIZE 8192 // maximum size of command string
+
+/* structure to hold data pertaining to the active state of
+ * a transfer in progress.
+ *
+ */
+
+enum Pop3CapabilityEnum {
+ POP3_CAPABILITY_UNDEFINED = 0x00000000,
+ POP3_HAS_XSENDER = 0x00000001,
+ POP3_GURL_UNDEFINED = 0x00000002,
+ POP3_HAS_GURL = 0x00000004,
+ POP3_UIDL_UNDEFINED = 0x00000008,
+ POP3_HAS_UIDL = 0x00000010,
+ POP3_XTND_XLST_UNDEFINED = 0x00000020,
+ POP3_HAS_XTND_XLST = 0x00000040,
+ POP3_TOP_UNDEFINED = 0x00000080,
+ POP3_HAS_TOP = 0x00000100,
+ POP3_AUTH_MECH_UNDEFINED = 0x00000200,
+ POP3_HAS_AUTH_USER = 0x00000400,
+ POP3_HAS_AUTH_LOGIN = 0x00000800,
+ POP3_HAS_AUTH_PLAIN = 0x00001000,
+ POP3_HAS_AUTH_CRAM_MD5 = 0x00002000,
+ POP3_HAS_AUTH_APOP = 0x00004000,
+ POP3_HAS_AUTH_NTLM = 0x00008000,
+ POP3_HAS_AUTH_MSN = 0x00010000,
+ POP3_HAS_RESP_CODES = 0x00020000,
+ POP3_HAS_AUTH_RESP_CODE = 0x00040000,
+ POP3_HAS_STLS = 0x00080000,
+ POP3_HAS_AUTH_GSSAPI = 0x00100000
+};
+
+// TODO use value > 0?
+#define POP3_HAS_AUTH_NONE 0
+#define POP3_HAS_AUTH_ANY 0x00001C00
+#define POP3_HAS_AUTH_ANY_SEC 0x0011E000
+
+enum Pop3StatesEnum {
+ POP3_READ_PASSWORD, // 0
+ //
+ POP3_START_CONNECT, // 1
+ POP3_FINISH_CONNECT, // 2
+ POP3_WAIT_FOR_RESPONSE, // 3
+ POP3_WAIT_FOR_START_OF_CONNECTION_RESPONSE, // 4
+ POP3_SEND_USERNAME, // 5
+
+ POP3_SEND_PASSWORD, // 6
+ POP3_SEND_STAT, // 7
+ POP3_GET_STAT, // 8
+ POP3_SEND_LIST, // 9
+ POP3_GET_LIST, // 10
+
+ POP3_SEND_UIDL_LIST, // 11
+ POP3_GET_UIDL_LIST, // 12
+ POP3_SEND_XTND_XLST_MSGID, // 13
+ POP3_GET_XTND_XLST_MSGID, // 14
+ POP3_GET_MSG, // 15
+
+ POP3_SEND_TOP, // 16
+ POP3_TOP_RESPONSE, // 17
+ POP3_SEND_RETR, // 18
+ POP3_RETR_RESPONSE, // 19
+ POP3_SEND_DELE, // 20
+
+ POP3_DELE_RESPONSE, // 21
+ POP3_SEND_QUIT, // 22
+ POP3_DONE, // 23
+ POP3_ERROR_DONE, // 24
+ POP3_FREE, // 25
+ POP3_SEND_AUTH, // 26
+ POP3_AUTH_RESPONSE, // 27
+ POP3_SEND_CAPA, // 28
+ POP3_CAPA_RESPONSE, // 29
+ POP3_PROCESS_AUTH, // 30
+ POP3_NEXT_AUTH_STEP, // 31
+
+ POP3_AUTH_LOGIN, // 32
+ POP3_AUTH_LOGIN_RESPONSE, // 33
+ POP3_AUTH_NTLM, // 34
+ POP3_AUTH_NTLM_RESPONSE, // 35
+ POP3_SEND_XSENDER, // 36
+ POP3_XSENDER_RESPONSE, // 37
+ POP3_SEND_GURL, // 38
+
+ POP3_GURL_RESPONSE, // 39
+ POP3_QUIT_RESPONSE, // 40
+ POP3_TLS_RESPONSE, // 41
+
+ POP3_AUTH_GSSAPI, // 42
+ POP3_AUTH_GSSAPI_FIRST, // 43
+ POP3_AUTH_GSSAPI_STEP, // 44
+
+ /**
+ * Async wait to obtain the password and deal with the result.
+ * The *PREOBTAIN* states are used for where we try and get the password
+ * before we've initiated a connection to the server.
+ */
+ POP3_OBTAIN_PASSWORD_EARLY, // 45
+ POP3_FINISH_OBTAIN_PASSWORD_EARLY, // 46
+ POP3_OBTAIN_PASSWORD_BEFORE_USERNAME, // 47
+ POP3_FINISH_OBTAIN_PASSWORD_BEFORE_USERNAME,// 48
+ POP3_OBTAIN_PASSWORD_BEFORE_PASSWORD, // 49
+ POP3_FINISH_OBTAIN_PASSWORD_BEFORE_PASSWORD // 50
+};
+
+
+#define KEEP 'k' /* If we want to keep this item on server. */
+#define DELETE_CHAR 'd' /* If we want to delete this item. */
+#define TOO_BIG 'b' /* item left on server because it was too big */
+#define FETCH_BODY 'f' /* Fetch full body of a partial msg */
+
+typedef struct Pop3UidlEntry { /* information about this message */
+ char* uidl;
+ char status; // KEEP=='k', DELETE='d' TOO_BIG='b' FETCH_BODY='f'
+ uint32_t dateReceived; // time message received, used for aging
+} Pop3UidlEntry;
+
+typedef struct Pop3UidlHost {
+ char* host;
+ char* user;
+ PLHashTable * hash;
+ Pop3UidlEntry* uidlEntries;
+ struct Pop3UidlHost* next;
+} Pop3UidlHost;
+
+typedef struct Pop3MsgInfo {
+ int32_t msgnum;
+ int32_t size;
+ char* uidl;
+} Pop3MsgInfo;
+
+typedef struct _Pop3ConData {
+ bool leave_on_server; /* Whether we're supposed to leave messages
+ on server. */
+ bool headers_only; /* Whether to just fetch headers on initial
+ downloads. */
+ int32_t size_limit; /* Leave messages bigger than this on the
+ server and only download a partial
+ message. */
+ uint32_t capability_flags; /* What capability this server has? */
+
+ Pop3StatesEnum next_state; /* the next state or action to be taken */
+ Pop3StatesEnum next_state_after_response;
+ bool pause_for_read; /* Pause now for next read? */
+
+ bool command_succeeded; /* did the last command succeed? */
+ bool list_done; /* did we get the complete list of msgIDs? */
+ int32_t first_msg;
+
+ uint32_t obuffer_size;
+ uint32_t obuffer_fp;
+
+ int32_t really_new_messages;
+ int32_t real_new_counter;
+ int32_t number_of_messages;
+ Pop3MsgInfo *msg_info; /* Message sizes and uidls (used only if we
+ are playing games that involve leaving
+ messages on the server). */
+ int32_t last_accessed_msg;
+ int32_t cur_msg_size;
+ bool truncating_cur_msg; /* are we using top and uidl? */
+ bool msg_del_started; /* True if MSG_BeginMailDel...
+ * called
+ */
+ bool only_check_for_new_mail;
+ nsMsgBiffState biffstate; /* If just checking for, what the answer is. */
+
+ bool verify_logon; /* true if we're just seeing if we can logon */
+
+ void *msg_closure;
+
+ bool graph_progress_bytes_p; /* whether we should display info about
+ the bytes transferred (usually we
+ display info about the number of
+ messages instead.) */
+
+ Pop3UidlHost *uidlinfo;
+ PLHashTable *newuidl;
+ char *only_uidl; /* If non-NULL, then load only this UIDL. */
+
+ bool get_url;
+ bool seenFromHeader;
+ int32_t parsed_bytes;
+ int32_t pop3_size;
+ bool dot_fix;
+ bool assumed_end;
+ nsresult urlStatus;
+} Pop3ConData;
+
+// State Flags (Note, I use the word state in terms of storing
+// state information about the connection (authentication, have we sent
+// commands, etc. I do not intend it to refer to protocol state)
+#define POP3_PAUSE_FOR_READ 0x00000001 /* should we pause for the next read */
+#define POP3_PASSWORD_FAILED 0x00000002
+#define POP3_STOPLOGIN 0x00000004 /* error loging in, so stop here */
+#define POP3_AUTH_FAILURE 0x00000008 /* extended code said authentication failed */
+
+
+class nsPop3Protocol : public nsMsgProtocol,
+ public nsIPop3Protocol,
+ public nsIMsgAsyncPromptListener
+{
+public:
+ nsPop3Protocol(nsIURI* aURL);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIPOP3PROTOCOL
+ NS_DECL_NSIMSGASYNCPROMPTLISTENER
+
+ nsresult Initialize(nsIURI * aURL);
+ virtual nsresult LoadUrl(nsIURI *aURL, nsISupports * aConsumer = nullptr) override;
+ void Cleanup();
+
+ const char* GetUsername() { return m_username.get(); }
+ void SetUsername(const char* name);
+
+ nsresult StartGetAsyncPassword(Pop3StatesEnum aNextState);
+
+ NS_IMETHOD OnTransportStatus(nsITransport *transport, nsresult status, int64_t progress, int64_t progressMax) override;
+ NS_IMETHOD OnStopRequest(nsIRequest *request, nsISupports * aContext, nsresult aStatus) override;
+ NS_IMETHOD Cancel(nsresult status) override;
+
+ static void MarkMsgInHashTable(PLHashTable *hashTable, const Pop3UidlEntry *uidl,
+ bool *changed);
+
+ static nsresult MarkMsgForHost(const char *hostName, const char *userName,
+ nsIFile *mailDirectory,
+ nsTArray<Pop3UidlEntry*> &UIDLArray);
+private:
+ virtual ~nsPop3Protocol();
+ nsCString m_ApopTimestamp;
+ nsCOMPtr<nsIStringBundle> mLocalBundle;
+
+ nsCString m_username;
+ nsCString m_senderInfo;
+ nsCString m_commandResponse;
+ nsCString m_GSSAPICache;
+
+ // Used for asynchronous password prompts to store the password temporarily.
+ nsCString m_passwordResult;
+
+ // progress state information
+ void UpdateProgressPercent(int64_t totalDone, int64_t total);
+ void UpdateStatus(const char16_t *aStatusName);
+ void UpdateStatusWithString(const char16_t *aString);
+ nsresult FormatCounterString(const nsString &stringName,
+ uint32_t count1,
+ uint32_t count2,
+ nsString &resultString);
+
+ int32_t m_bytesInMsgReceived;
+ int64_t m_totalFolderSize;
+ int64_t m_totalDownloadSize; /* Number of bytes we're going to
+ download. Might be much less
+ than the total_folder_size. */
+ int64_t m_totalBytesReceived; // total # bytes received for the connection
+
+ virtual nsresult ProcessProtocolState(nsIURI * url, nsIInputStream * inputStream,
+ uint64_t sourceOffset, uint32_t length) override;
+ virtual int32_t Pop3SendData(const char * dataBuffer, bool aSuppressLogging = false);
+
+ virtual const char* GetType() override {return "pop3";}
+
+ nsCOMPtr<nsIURI> m_url;
+ nsCOMPtr<nsIPop3Sink> m_nsIPop3Sink;
+ nsCOMPtr<nsIPop3IncomingServer> m_pop3Server;
+
+ nsMsgLineStreamBuffer * m_lineStreamBuffer; // used to efficiently extract lines from the incoming data stream
+ Pop3ConData* m_pop3ConData;
+ void FreeMsgInfo();
+ void Abort();
+
+ bool m_tlsEnabled;
+ int32_t m_socketType;
+ bool m_password_already_sent;
+ bool m_needToRerunUrl;
+
+ void SetCapFlag(uint32_t flag);
+ void ClearCapFlag(uint32_t flag);
+ bool TestCapFlag(uint32_t flag);
+ uint32_t GetCapFlags();
+
+ void InitPrefAuthMethods(int32_t authMethodPrefValue);
+ nsresult ChooseAuthMethod();
+ void MarkAuthMethodAsFailed(int32_t failedAuthMethod);
+ void ResetAuthMethods();
+ int32_t m_prefAuthMethods; // set of capability flags for auth methods
+ int32_t m_failedAuthMethods; // ditto
+ int32_t m_currentAuthMethod; // exactly one capability flag, or 0
+
+ int32_t m_listpos;
+
+ nsresult HandleLine(char *line, uint32_t line_length);
+
+ nsresult GetApopTimestamp();
+
+ //////////////////////////////////////////////////////////////////////////////////////////
+ // Begin Pop3 protocol state handlers
+ //////////////////////////////////////////////////////////////////////////////////////////
+ int32_t WaitForStartOfConnectionResponse(nsIInputStream* inputStream,
+ uint32_t length);
+ int32_t WaitForResponse(nsIInputStream* inputStream,
+ uint32_t length);
+ int32_t Error(const char* err_code, const char16_t **params = nullptr,
+ uint32_t length = 0);
+ int32_t SendAuth();
+ int32_t AuthResponse(nsIInputStream* inputStream, uint32_t length);
+ int32_t SendCapa();
+ int32_t CapaResponse(nsIInputStream* inputStream, uint32_t length);
+ int32_t SendTLSResponse();
+ int32_t ProcessAuth();
+ int32_t NextAuthStep();
+ int32_t AuthLogin();
+ int32_t AuthLoginResponse();
+ int32_t AuthNtlm();
+ int32_t AuthNtlmResponse();
+ int32_t AuthGSSAPI();
+ int32_t AuthGSSAPIResponse(bool first);
+ int32_t SendUsername();
+ int32_t SendPassword();
+ int32_t SendStatOrGurl(bool sendStat);
+ int32_t SendStat();
+ int32_t GetStat();
+ int32_t SendGurl();
+ int32_t GurlResponse();
+ int32_t SendList();
+ int32_t GetList(nsIInputStream* inputStream, uint32_t length);
+ int32_t HandleNoUidListAvailable();
+ int32_t SendXtndXlstMsgid();
+ int32_t GetXtndXlstMsgid(nsIInputStream* inputStream, uint32_t length);
+ int32_t SendUidlList();
+ int32_t GetUidlList(nsIInputStream* inputStream, uint32_t length);
+ int32_t GetMsg();
+ int32_t SendTop();
+ int32_t SendXsender();
+ int32_t XsenderResponse();
+ int32_t SendRetr();
+
+ int32_t RetrResponse(nsIInputStream* inputStream, uint32_t length);
+ int32_t TopResponse(nsIInputStream* inputStream, uint32_t length);
+ int32_t SendDele();
+ int32_t DeleResponse();
+ int32_t CommitState(bool remove_last_entry);
+
+ Pop3StatesEnum GetNextPasswordObtainState();
+ nsresult RerunUrl();
+};
+
+#endif /* nsPop3Protocol_h__ */
diff --git a/mailnews/local/src/nsPop3Service.cpp b/mailnews/local/src/nsPop3Service.cpp
new file mode 100644
index 000000000..d0177b559
--- /dev/null
+++ b/mailnews/local/src/nsPop3Service.cpp
@@ -0,0 +1,711 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsPop3Service.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsIPop3IncomingServer.h"
+
+#include "nsPop3URL.h"
+#include "nsPop3Sink.h"
+#include "nsPop3Protocol.h"
+#include "nsMsgLocalCID.h"
+#include "nsMsgBaseCID.h"
+#include "nsCOMPtr.h"
+#include "nsIMsgWindow.h"
+#include "nsINetUtil.h"
+
+#include "nsIRDFService.h"
+#include "nsRDFCID.h"
+#include "nsIDirectoryService.h"
+#include "nsMailDirServiceDefs.h"
+#include "prprf.h"
+#include "nsMsgUtils.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMsgAccount.h"
+#include "nsLocalMailFolder.h"
+#include "nsIMailboxUrl.h"
+#include "nsIPrompt.h"
+#include "nsINetUtil.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "mozilla/Services.h"
+
+#define PREF_MAIL_ROOT_POP3 "mail.root.pop3" // old - for backward compatibility only
+#define PREF_MAIL_ROOT_POP3_REL "mail.root.pop3-rel"
+
+static NS_DEFINE_CID(kPop3UrlCID, NS_POP3URL_CID);
+static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+
+nsPop3Service::nsPop3Service()
+{
+}
+
+nsPop3Service::~nsPop3Service()
+{}
+
+NS_IMPL_ISUPPORTS(nsPop3Service,
+ nsIPop3Service,
+ nsIProtocolHandler,
+ nsIMsgProtocolInfo)
+
+NS_IMETHODIMP nsPop3Service::CheckForNewMail(nsIMsgWindow* aMsgWindow,
+ nsIUrlListener *aUrlListener,
+ nsIMsgFolder *aInbox,
+ nsIPop3IncomingServer *aPopServer,
+ nsIURI **aURL)
+{
+ return GetMail(false /* don't download, just check */,
+ aMsgWindow, aUrlListener, aInbox, aPopServer, aURL);
+}
+
+
+nsresult nsPop3Service::GetNewMail(nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aUrlListener,
+ nsIMsgFolder *aInbox,
+ nsIPop3IncomingServer *aPopServer,
+ nsIURI **aURL)
+{
+ return GetMail(true /* download */,
+ aMsgWindow, aUrlListener, aInbox, aPopServer, aURL);
+}
+
+nsresult nsPop3Service::GetMail(bool downloadNewMail,
+ nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aUrlListener,
+ nsIMsgFolder *aInbox,
+ nsIPop3IncomingServer *aPopServer,
+ nsIURI **aURL)
+{
+
+ NS_ENSURE_ARG_POINTER(aInbox);
+ int32_t popPort = -1;
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ nsCOMPtr<nsIURI> url;
+
+ server = do_QueryInterface(aPopServer);
+ NS_ENSURE_TRUE(server, NS_MSG_INVALID_OR_MISSING_SERVER);
+
+ nsCOMPtr<nsIMsgLocalMailFolder> destLocalFolder = do_QueryInterface(aInbox);
+ if (destLocalFolder)
+ {
+ // We don't know the needed size yet, so at least check
+ // if there is some free space (1MB) in the message store.
+ bool destFolderTooBig;
+ destLocalFolder->WarnIfLocalFileTooBig(aMsgWindow, 0xFFFF, &destFolderTooBig);
+ if (destFolderTooBig)
+ return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
+ }
+
+ nsCString popHost;
+ nsCString popUser;
+ nsresult rv = server->GetHostName(popHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (popHost.IsEmpty())
+ return NS_MSG_INVALID_OR_MISSING_SERVER;
+
+ rv = server->GetPort(&popPort);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = server->GetUsername(popUser);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (popUser.IsEmpty())
+ return NS_MSG_SERVER_USERNAME_MISSING;
+
+ nsCString escapedUsername;
+ MsgEscapeString(popUser, nsINetUtil::ESCAPE_XALPHAS, escapedUsername);
+
+ if (NS_SUCCEEDED(rv) && aPopServer)
+ {
+ // now construct a pop3 url...
+ // we need to escape the username because it may contain
+ // characters like / % or @
+ char * urlSpec = (downloadNewMail)
+ ? PR_smprintf("pop3://%s@%s:%d", escapedUsername.get(), popHost.get(), popPort)
+ : PR_smprintf("pop3://%s@%s:%d/?check", escapedUsername.get(), popHost.get(), popPort);
+ rv = BuildPop3Url(urlSpec, aInbox, aPopServer, aUrlListener, getter_AddRefs(url), aMsgWindow);
+ PR_smprintf_free(urlSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ NS_ENSURE_TRUE(url, rv);
+
+ if (NS_SUCCEEDED(rv))
+ rv = RunPopUrl(server, url);
+
+ if (aURL) // we already have a ref count on pop3url...
+ NS_IF_ADDREF(*aURL = url);
+
+ return rv;
+}
+
+NS_IMETHODIMP nsPop3Service::VerifyLogon(nsIMsgIncomingServer *aServer,
+ nsIUrlListener *aUrlListener,
+ nsIMsgWindow *aMsgWindow,
+ nsIURI **aURL)
+{
+ NS_ENSURE_ARG_POINTER(aServer);
+ nsCString popHost;
+ nsCString popUser;
+ int32_t popPort = -1;
+
+ nsresult rv = aServer->GetHostName(popHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (popHost.IsEmpty())
+ return NS_MSG_INVALID_OR_MISSING_SERVER;
+
+ rv = aServer->GetPort(&popPort);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = aServer->GetUsername(popUser);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (popUser.IsEmpty())
+ return NS_MSG_SERVER_USERNAME_MISSING;
+
+ nsCString escapedUsername;
+ MsgEscapeString(popUser, nsINetUtil::ESCAPE_XALPHAS, escapedUsername);
+
+ nsCOMPtr<nsIPop3IncomingServer> popServer = do_QueryInterface(aServer, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // now construct a pop3 url...
+ // we need to escape the username because it may contain
+ // characters like / % or @
+ char *urlSpec = PR_smprintf("pop3://%s@%s:%d/?verifyLogon",
+ escapedUsername.get(), popHost.get(), popPort);
+ NS_ENSURE_TRUE(urlSpec, NS_ERROR_OUT_OF_MEMORY);
+
+ nsCOMPtr<nsIURI> url;
+ rv = BuildPop3Url(urlSpec, nullptr, popServer, aUrlListener,
+ getter_AddRefs(url), aMsgWindow);
+ PR_smprintf_free(urlSpec);
+
+ if (NS_SUCCEEDED(rv) && url)
+ {
+ rv = RunPopUrl(aServer, url);
+ if (NS_SUCCEEDED(rv) && aURL)
+ url.forget(aURL);
+ }
+
+ return rv;
+}
+
+nsresult nsPop3Service::BuildPop3Url(const char *urlSpec,
+ nsIMsgFolder *inbox,
+ nsIPop3IncomingServer *server,
+ nsIUrlListener *aUrlListener,
+ nsIURI **aUrl,
+ nsIMsgWindow *aMsgWindow)
+{
+ nsresult rv;
+
+ nsPop3Sink *pop3Sink = new nsPop3Sink();
+
+ NS_ENSURE_TRUE(pop3Sink, NS_ERROR_OUT_OF_MEMORY);
+
+ pop3Sink->SetPopServer(server);
+ pop3Sink->SetFolder(inbox);
+
+ // now create a pop3 url and a protocol instance to run the url....
+ nsCOMPtr<nsIPop3URL> pop3Url = do_CreateInstance(kPop3UrlCID, &rv);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ pop3Url->SetPop3Sink(pop3Sink);
+
+ rv = CallQueryInterface(pop3Url, aUrl);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ rv = (*aUrl)->SetSpec(nsDependentCString(urlSpec));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(pop3Url);
+ if (mailnewsurl)
+ {
+ if (aUrlListener)
+ mailnewsurl->RegisterListener(aUrlListener);
+ if (aMsgWindow)
+ mailnewsurl->SetMsgWindow(aMsgWindow);
+ }
+
+ return rv;
+}
+
+nsresult nsPop3Service::RunPopUrl(nsIMsgIncomingServer *aServer, nsIURI *aUrlToRun)
+{
+
+ NS_ENSURE_ARG_POINTER(aServer);
+ NS_ENSURE_ARG_POINTER(aUrlToRun);
+
+ nsCString userName;
+
+ // load up required server information
+ // we store the username unescaped in the server
+ // so there is no need to unescape it
+ nsresult rv = aServer->GetRealUsername(userName);
+
+ // find out if the server is busy or not...if the server is busy, we are
+ // *NOT* going to run the url
+ bool serverBusy = false;
+ rv = aServer->GetServerBusy(&serverBusy);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!serverBusy)
+ {
+ RefPtr<nsPop3Protocol> protocol = new nsPop3Protocol(aUrlToRun);
+ if (protocol)
+ {
+ // the protocol stores the unescaped username, so there is no need to escape it.
+ protocol->SetUsername(userName.get());
+ rv = protocol->LoadUrl(aUrlToRun);
+ if (NS_FAILED(rv))
+ aServer->SetServerBusy(false);
+ }
+ }
+ else
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> url = do_QueryInterface(aUrlToRun);
+ if (url)
+ AlertServerBusy(url);
+ rv = NS_ERROR_FAILURE;
+ }
+ return rv;
+}
+
+
+NS_IMETHODIMP nsPop3Service::GetScheme(nsACString &aScheme)
+{
+ aScheme.AssignLiteral("pop3");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Service::GetDefaultPort(int32_t *aDefaultPort)
+{
+ NS_ENSURE_ARG_POINTER(aDefaultPort);
+ *aDefaultPort = nsIPop3URL::DEFAULT_POP3_PORT;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Service::AllowPort(int32_t port, const char *scheme, bool *_retval)
+{
+ *_retval = true; // allow pop on any port
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Service::GetDefaultDoBiff(bool *aDoBiff)
+{
+ NS_ENSURE_ARG_POINTER(aDoBiff);
+ // by default, do biff for POP3 servers
+ *aDoBiff = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Service::GetProtocolFlags(uint32_t *result)
+{
+ NS_ENSURE_ARG_POINTER(result);
+ *result = URI_NORELATIVE | URI_DANGEROUS_TO_LOAD | ALLOWS_PROXY |
+ URI_FORBIDS_COOKIE_ACCESS;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Service::NewURI(const nsACString &aSpec,
+ const char *aOriginCharset, // ignored
+ nsIURI *aBaseURI,
+ nsIURI **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ nsAutoCString folderUri(aSpec);
+ nsCOMPtr<nsIRDFResource> resource;
+ int32_t offset = folderUri.FindChar('?');
+ if (offset != kNotFound)
+ folderUri.SetLength(offset);
+
+ const char *uidl = PL_strstr(nsCString(aSpec).get(), "uidl=");
+ NS_ENSURE_TRUE(uidl, NS_ERROR_FAILURE);
+
+ nsresult rv;
+
+ nsCOMPtr<nsIRDFService> rdfService(do_GetService(kRDFServiceCID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = rdfService->GetResource(folderUri, getter_AddRefs(resource));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(resource, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgIncomingServer> server;
+
+ nsLocalFolderScanState folderScanState;
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(folder);
+ nsCOMPtr<nsIMailboxUrl> mailboxUrl = do_QueryInterface(aBaseURI);
+
+ if (mailboxUrl && localFolder)
+ {
+ rv = localFolder->GetFolderScanState(&folderScanState);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ nsMsgKey msgKey;
+ mailboxUrl->GetMessageKey(&msgKey);
+ folder->GetMessageHeader(msgKey, getter_AddRefs(msgHdr));
+ // we do this to get the account key
+ if (msgHdr)
+ localFolder->GetUidlFromFolder(&folderScanState, msgHdr);
+ if (!folderScanState.m_accountKey.IsEmpty())
+ {
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (accountManager)
+ {
+ nsCOMPtr<nsIMsgAccount> account;
+ accountManager->GetAccount(folderScanState.m_accountKey, getter_AddRefs(account));
+ if (account)
+ account->GetIncomingServer(getter_AddRefs(server));
+ }
+ }
+ }
+
+ if (!server)
+ rv = folder->GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIPop3IncomingServer> popServer = do_QueryInterface(server,&rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCString hostname;
+ nsCString username;
+ server->GetHostName(hostname);
+ server->GetUsername(username);
+
+ int32_t port;
+ server->GetPort(&port);
+ if (port == -1) port = nsIPop3URL::DEFAULT_POP3_PORT;
+
+ // we need to escape the username because it may contain
+ // characters like / % or @
+ nsCString escapedUsername;
+ MsgEscapeString(username, nsINetUtil::ESCAPE_XALPHAS, escapedUsername);
+
+ nsAutoCString popSpec("pop://");
+ popSpec += escapedUsername;
+ popSpec += "@";
+ popSpec += hostname;
+ popSpec += ":";
+ popSpec.AppendInt(port);
+ popSpec += "?";
+ popSpec += uidl;
+ nsCOMPtr<nsIUrlListener> urlListener = do_QueryInterface(folder, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = BuildPop3Url(popSpec.get(), folder, popServer,
+ urlListener, _retval, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsurl = do_QueryInterface(*_retval, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ // escape the username before we call SetUsername(). we do this because GetUsername()
+ // will unescape the username
+ mailnewsurl->SetUsername(escapedUsername);
+ }
+
+ nsCOMPtr<nsIPop3URL> popurl = do_QueryInterface(mailnewsurl, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString messageUri (aSpec);
+ if (!strncmp(messageUri.get(), "mailbox:", 8))
+ messageUri.Replace(0, 8, "mailbox-message:");
+ offset = messageUri.Find("?number=");
+ if (offset != kNotFound)
+ messageUri.Replace(offset, 8, "#");
+ offset = messageUri.FindChar('&');
+ if (offset != kNotFound)
+ messageUri.SetLength(offset);
+ popurl->SetMessageUri(messageUri.get());
+ nsCOMPtr<nsIPop3Sink> pop3Sink;
+ rv = popurl->GetPop3Sink(getter_AddRefs(pop3Sink));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ pop3Sink->SetBuildMessageUri(true);
+
+ return NS_OK;
+}
+
+void nsPop3Service::AlertServerBusy(nsIMsgMailNewsUrl *url)
+{
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ mozilla::services::GetStringBundleService();
+ if (!bundleService)
+ return;
+ nsCOMPtr<nsIStringBundle> bundle;
+ rv = bundleService->CreateBundle("chrome://messenger/locale/localMsgs.properties", getter_AddRefs(bundle));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ nsCOMPtr<nsIMsgWindow> msgWindow;
+ nsCOMPtr<nsIPrompt> dialog;
+ rv = url->GetMsgWindow(getter_AddRefs(msgWindow)); //it is ok to have null msgWindow, for example when biffing
+ if (NS_FAILED(rv) || !msgWindow)
+ return;
+
+ rv = msgWindow->GetPromptDialog(getter_AddRefs(dialog));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ nsString accountName;
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ rv = url->GetServer(getter_AddRefs(server));
+ NS_ENSURE_SUCCESS_VOID(rv);
+ rv = server->GetPrettyName(accountName);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ const char16_t *params[] = { accountName.get() };
+ nsString alertString;
+ nsString dialogTitle;
+ bundle->FormatStringFromName(
+ u"pop3ServerBusy",
+ params, 1, getter_Copies(alertString));
+ bundle->FormatStringFromName(
+ u"pop3ErrorDialogTitle",
+ params, 1, getter_Copies(dialogTitle));
+ if (!alertString.IsEmpty())
+ dialog->Alert(dialogTitle.get(), alertString.get());
+}
+
+NS_IMETHODIMP nsPop3Service::NewChannel(nsIURI *aURI, nsIChannel **_retval)
+{
+ return NewChannel2(aURI, nullptr, _retval);
+}
+
+NS_IMETHODIMP nsPop3Service::NewChannel2(nsIURI *aURI,
+ nsILoadInfo *aLoadInfo,
+ nsIChannel **_retval)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+ nsresult rv;
+
+ nsCOMPtr<nsIMsgMailNewsUrl> url = do_QueryInterface(aURI, &rv);
+ nsCString realUserName;
+ if (NS_SUCCEEDED(rv) && url)
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ url->GetServer(getter_AddRefs(server));
+ if (server)
+ {
+ // find out if the server is busy or not...if the server is busy, we are
+ // *NOT* going to run the url. The error code isn't quite right...
+ // We might want to put up an error right here.
+ bool serverBusy = false;
+ rv = server->GetServerBusy(&serverBusy);
+ if (serverBusy)
+ {
+ AlertServerBusy(url);
+ return NS_MSG_FOLDER_BUSY;
+ }
+ server->GetRealUsername(realUserName);
+ }
+ }
+
+ RefPtr<nsPop3Protocol> protocol = new nsPop3Protocol(aURI);
+ NS_ENSURE_TRUE(protocol, NS_ERROR_OUT_OF_MEMORY);
+
+ rv = protocol->Initialize(aURI);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = protocol->SetLoadInfo(aLoadInfo);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ protocol->SetUsername(realUserName.get());
+
+ return CallQueryInterface(protocol, _retval);
+}
+
+
+NS_IMETHODIMP
+nsPop3Service::SetDefaultLocalPath(nsIFile *aPath)
+{
+ NS_ENSURE_ARG(aPath);
+ return NS_SetPersistentFile(PREF_MAIL_ROOT_POP3_REL, PREF_MAIL_ROOT_POP3, aPath);
+}
+
+NS_IMETHODIMP
+nsPop3Service::GetDefaultLocalPath(nsIFile **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = nullptr;
+
+ bool havePref;
+ nsCOMPtr<nsIFile> localFile;
+ nsresult rv = NS_GetPersistentFile(PREF_MAIL_ROOT_POP3_REL,
+ PREF_MAIL_ROOT_POP3,
+ NS_APP_MAIL_50_DIR,
+ havePref,
+ getter_AddRefs(localFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool exists;
+ rv = localFile->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && !exists)
+ rv = localFile->Create(nsIFile::DIRECTORY_TYPE, 0775);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!havePref || !exists) {
+ rv = NS_SetPersistentFile(PREF_MAIL_ROOT_POP3_REL, PREF_MAIL_ROOT_POP3, localFile);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to set root dir pref.");
+ }
+
+ NS_IF_ADDREF(*aResult = localFile);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsPop3Service::GetServerIID(nsIID **aServerIID)
+{
+ *aServerIID = new nsIID(NS_GET_IID(nsIPop3IncomingServer));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::GetRequiresUsername(bool *aRequiresUsername)
+{
+ NS_ENSURE_ARG_POINTER(aRequiresUsername);
+ *aRequiresUsername = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::GetPreflightPrettyNameWithEmailAddress(bool *aPreflightPrettyNameWithEmailAddress)
+{
+ NS_ENSURE_ARG_POINTER(aPreflightPrettyNameWithEmailAddress);
+ *aPreflightPrettyNameWithEmailAddress = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::GetCanLoginAtStartUp(bool *aCanLoginAtStartUp)
+{
+ NS_ENSURE_ARG_POINTER(aCanLoginAtStartUp);
+ *aCanLoginAtStartUp = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::GetCanDelete(bool *aCanDelete)
+{
+ NS_ENSURE_ARG_POINTER(aCanDelete);
+ *aCanDelete = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::GetCanDuplicate(bool *aCanDuplicate)
+{
+ NS_ENSURE_ARG_POINTER(aCanDuplicate);
+ *aCanDuplicate = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::GetCanGetMessages(bool *aCanGetMessages)
+{
+ NS_ENSURE_ARG_POINTER(aCanGetMessages);
+ *aCanGetMessages = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::GetCanGetIncomingMessages(bool *aCanGetIncomingMessages)
+{
+ NS_ENSURE_ARG_POINTER(aCanGetIncomingMessages);
+ *aCanGetIncomingMessages = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::GetShowComposeMsgLink(bool *showComposeMsgLink)
+{
+ NS_ENSURE_ARG_POINTER(showComposeMsgLink);
+ *showComposeMsgLink = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::GetFoldersCreatedAsync(bool *aAsyncCreation)
+{
+ NS_ENSURE_ARG_POINTER(aAsyncCreation);
+ *aAsyncCreation = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::GetDefaultServerPort(bool isSecure, int32_t *aPort)
+{
+ NS_ENSURE_ARG_POINTER(aPort);
+
+ if (!isSecure)
+ return GetDefaultPort(aPort);
+
+ *aPort = nsIPop3URL::DEFAULT_POP3S_PORT;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::NotifyDownloadStarted(nsIMsgFolder *aFolder)
+{
+ nsTObserverArray<nsCOMPtr<nsIPop3ServiceListener> >::ForwardIterator
+ iter(mListeners);
+ nsCOMPtr<nsIPop3ServiceListener> listener;
+ while (iter.HasMore()) {
+ listener = iter.GetNext();
+ listener->OnDownloadStarted(aFolder);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::NotifyDownloadProgress(nsIMsgFolder *aFolder,
+ uint32_t aNumMessages,
+ uint32_t aNumTotalMessages)
+{
+ nsTObserverArray<nsCOMPtr<nsIPop3ServiceListener> >::ForwardIterator
+ iter(mListeners);
+ nsCOMPtr<nsIPop3ServiceListener> listener;
+ while (iter.HasMore()) {
+ listener = iter.GetNext();
+ listener->OnDownloadProgress(aFolder, aNumMessages, aNumTotalMessages);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3Service::NotifyDownloadCompleted(nsIMsgFolder *aFolder,
+ uint32_t aNumMessages)
+{
+ nsTObserverArray<nsCOMPtr<nsIPop3ServiceListener> >::ForwardIterator
+ iter(mListeners);
+ nsCOMPtr<nsIPop3ServiceListener> listener;
+ while (iter.HasMore()) {
+ listener = iter.GetNext();
+ listener->OnDownloadCompleted(aFolder, aNumMessages);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Service::AddListener(nsIPop3ServiceListener *aListener)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+ mListeners.AppendElementUnlessExists(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPop3Service::RemoveListener(nsIPop3ServiceListener *aListener)
+{
+ NS_ENSURE_ARG_POINTER(aListener);
+ mListeners.RemoveElement(aListener);
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsPop3Service.h b/mailnews/local/src/nsPop3Service.h
new file mode 100644
index 000000000..61dd9cf5c
--- /dev/null
+++ b/mailnews/local/src/nsPop3Service.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsPop3Service_h___
+#define nsPop3Service_h___
+
+#include "nscore.h"
+
+#include "nsIPop3Service.h"
+#include "nsIPop3URL.h"
+#include "nsIUrlListener.h"
+#include "nsIStreamListener.h"
+#include "nsIProtocolHandler.h"
+#include "nsIMsgProtocolInfo.h"
+#include "nsTObserverArray.h"
+
+class nsIMsgMailNewsUrl;
+
+class nsPop3Service : public nsIPop3Service,
+ public nsIProtocolHandler,
+ public nsIMsgProtocolInfo
+{
+public:
+
+ nsPop3Service();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPOP3SERVICE
+ NS_DECL_NSIPROTOCOLHANDLER
+ NS_DECL_NSIMSGPROTOCOLINFO
+
+protected:
+ virtual ~nsPop3Service();
+ nsresult GetMail(bool downloadNewMail,
+ nsIMsgWindow* aMsgWindow,
+ nsIUrlListener * aUrlListener,
+ nsIMsgFolder *inbox,
+ nsIPop3IncomingServer *popServer,
+ nsIURI ** aURL);
+ // convience function to make constructing of the pop3 url easier...
+ nsresult BuildPop3Url(const char * urlSpec, nsIMsgFolder *inbox,
+ nsIPop3IncomingServer *, nsIUrlListener * aUrlListener,
+ nsIURI ** aUrl, nsIMsgWindow *aMsgWindow);
+
+ nsresult RunPopUrl(nsIMsgIncomingServer * aServer, nsIURI * aUrlToRun);
+ void AlertServerBusy(nsIMsgMailNewsUrl *url);
+ nsTObserverArray<nsCOMPtr<nsIPop3ServiceListener> > mListeners;
+};
+
+#endif /* nsPop3Service_h___ */
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;
+}
diff --git a/mailnews/local/src/nsPop3Sink.h b/mailnews/local/src/nsPop3Sink.h
new file mode 100644
index 000000000..bd11eba35
--- /dev/null
+++ b/mailnews/local/src/nsPop3Sink.h
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef nsPop3Sink_h__
+#define nsPop3Sink_h__
+
+#include "nscore.h"
+#include "nsIURL.h"
+#include "nsIPop3Sink.h"
+#include "nsIOutputStream.h"
+#include "prmem.h"
+#include "prio.h"
+#include "plstr.h"
+#include "prenv.h"
+#include "nsIMsgFolder.h"
+#include "nsAutoPtr.h"
+#include "nsTArray.h"
+#include "nsStringGlue.h"
+
+class nsParseNewMailState;
+class nsIMsgFolder;
+
+struct partialRecord
+{
+ partialRecord();
+ ~partialRecord();
+
+ nsCOMPtr<nsIMsgDBHdr> m_msgDBHdr;
+ nsCString m_uidl;
+};
+
+class nsPop3Sink : public nsIPop3Sink
+{
+public:
+ nsPop3Sink();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIPOP3SINK
+ nsresult GetServerFolder(nsIMsgFolder **aFolder);
+ nsresult FindPartialMessages();
+ void CheckPartialMessages(nsIPop3Protocol *protocol);
+
+ static char* GetDummyEnvelope(void);
+
+protected:
+ virtual ~nsPop3Sink();
+ nsresult WriteLineToMailbox(const nsACString& buffer);
+ nsresult ReleaseFolderLock();
+ nsresult HandleTempDownloadFailed(nsIMsgWindow *msgWindow);
+
+ bool m_authed;
+ nsCString m_accountUrl;
+ uint32_t m_biffState;
+ int32_t m_numNewMessages;
+ int32_t m_numNewMessagesInFolder;
+ int32_t m_numMsgsDownloaded;
+ bool m_senderAuthed;
+ nsCString m_outputBuffer;
+ nsCOMPtr<nsIPop3IncomingServer> m_popServer;
+ //Currently the folder we want to update about biff info
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ RefPtr<nsParseNewMailState> m_newMailParser;
+ nsCOMPtr <nsIOutputStream> m_outFileStream; // the file we write to, which may be temporary
+ nsCOMPtr<nsIMsgPluggableStore> m_msgStore;
+ bool m_uidlDownload;
+ bool m_buildMessageUri;
+ bool m_downloadingToTempFile;
+ nsCOMPtr <nsIFile> m_tmpDownloadFile;
+ nsCOMPtr<nsIMsgWindow> m_window;
+ nsCString m_messageUri;
+ nsCString m_baseMessageUri;
+ nsCString m_origMessageUri;
+ nsCString m_accountKey;
+ nsTArray<partialRecord*> m_partialMsgsArray;
+};
+
+#endif
diff --git a/mailnews/local/src/nsPop3URL.cpp b/mailnews/local/src/nsPop3URL.cpp
new file mode 100644
index 000000000..f3d724793
--- /dev/null
+++ b/mailnews/local/src/nsPop3URL.cpp
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsIURL.h"
+#include "nsPop3URL.h"
+#include "nsPop3Protocol.h"
+#include "nsStringGlue.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "prprf.h"
+
+nsPop3URL::nsPop3URL(): nsMsgMailNewsUrl()
+{
+}
+
+nsPop3URL::~nsPop3URL()
+{
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(nsPop3URL, nsMsgMailNewsUrl, nsIPop3URL)
+
+
+////////////////////////////////////////////////////////////////////////////////////
+// Begin nsIPop3URL specific support
+////////////////////////////////////////////////////////////////////////////////////
+
+nsresult nsPop3URL::SetPop3Sink(nsIPop3Sink* aPop3Sink)
+{
+ if (aPop3Sink)
+ m_pop3Sink = aPop3Sink;
+ return NS_OK;
+}
+
+nsresult nsPop3URL::GetPop3Sink(nsIPop3Sink** aPop3Sink)
+{
+ if (aPop3Sink)
+ {
+ *aPop3Sink = m_pop3Sink;
+ NS_IF_ADDREF(*aPop3Sink);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3URL::GetMessageUri(char ** aMessageUri)
+{
+ if(!aMessageUri || m_messageUri.IsEmpty())
+ return NS_ERROR_NULL_POINTER;
+ *aMessageUri = ToNewCString(m_messageUri);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPop3URL::SetMessageUri(const char *aMessageUri)
+{
+ if (aMessageUri)
+ m_messageUri = aMessageUri;
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsPop3URL.h b/mailnews/local/src/nsPop3URL.h
new file mode 100644
index 000000000..4c6ec6b0a
--- /dev/null
+++ b/mailnews/local/src/nsPop3URL.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+#ifndef nsPop3URL_h__
+#define nsPop3URL_h__
+
+#include "nsIPop3URL.h"
+#include "nsMsgMailNewsUrl.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsCOMPtr.h"
+
+class nsPop3URL : public nsIPop3URL, public nsMsgMailNewsUrl
+{
+public:
+ NS_DECL_NSIPOP3URL
+ nsPop3URL();
+ NS_DECL_ISUPPORTS_INHERITED
+
+protected:
+ virtual ~nsPop3URL();
+
+ nsCString m_messageUri;
+
+ /* Pop3 specific event sinks */
+ nsCOMPtr<nsIPop3Sink> m_pop3Sink;
+};
+
+#endif // nsPop3URL_h__
diff --git a/mailnews/local/src/nsRssIncomingServer.cpp b/mailnews/local/src/nsRssIncomingServer.cpp
new file mode 100644
index 000000000..fe1e8abe3
--- /dev/null
+++ b/mailnews/local/src/nsRssIncomingServer.cpp
@@ -0,0 +1,260 @@
+/* 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 "nsIRssService.h"
+#include "nsRssIncomingServer.h"
+#include "nsMsgFolderFlags.h"
+#include "nsINewsBlogFeedDownloader.h"
+#include "nsMsgBaseCID.h"
+#include "nsIFile.h"
+#include "nsIMsgFolderNotificationService.h"
+
+#include "nsIMsgLocalMailFolder.h"
+#include "nsIDBFolderInfo.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsArrayUtils.h"
+#include "nsMsgUtils.h"
+
+nsrefcnt nsRssIncomingServer::gInstanceCount = 0;
+
+NS_IMPL_ISUPPORTS_INHERITED(nsRssIncomingServer,
+ nsMsgIncomingServer,
+ nsIRssIncomingServer,
+ nsIMsgFolderListener,
+ nsILocalMailIncomingServer)
+
+nsRssIncomingServer::nsRssIncomingServer()
+{
+ m_canHaveFilters = true;
+
+ if (gInstanceCount == 0)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIMsgFolderNotificationService> notifyService =
+ do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ notifyService->AddListener(this,
+ nsIMsgFolderNotificationService::folderAdded |
+ nsIMsgFolderNotificationService::folderDeleted |
+ nsIMsgFolderNotificationService::folderMoveCopyCompleted |
+ nsIMsgFolderNotificationService::folderRenamed);
+ }
+
+ gInstanceCount++;
+}
+
+nsRssIncomingServer::~nsRssIncomingServer()
+{
+ gInstanceCount--;
+
+ if (gInstanceCount == 0)
+ {
+ nsresult rv;
+ nsCOMPtr<nsIMsgFolderNotificationService> notifyService =
+ do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ notifyService->RemoveListener(this);
+ }
+}
+
+nsresult nsRssIncomingServer::FillInDataSourcePath(const nsAString& aDataSourceName,
+ nsIFile ** aLocation)
+{
+ nsresult rv;
+ // Get the local path for this server.
+ nsCOMPtr<nsIFile> localFile;
+ rv = GetLocalPath(getter_AddRefs(localFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Append the name of the subscriptions data source.
+ rv = localFile->Append(aDataSourceName);
+ NS_IF_ADDREF(*aLocation = localFile);
+ return rv;
+}
+
+// nsIRSSIncomingServer methods
+NS_IMETHODIMP nsRssIncomingServer::GetSubscriptionsDataSourcePath(nsIFile ** aLocation)
+{
+ return FillInDataSourcePath(NS_LITERAL_STRING("feeds.rdf"), aLocation);
+}
+
+NS_IMETHODIMP nsRssIncomingServer::GetFeedItemsDataSourcePath(nsIFile ** aLocation)
+{
+ return FillInDataSourcePath(NS_LITERAL_STRING("feeditems.rdf"), aLocation);
+}
+
+NS_IMETHODIMP nsRssIncomingServer::CreateDefaultMailboxes()
+{
+ // For Feeds, all we have is Trash.
+ return CreateLocalFolder(NS_LITERAL_STRING("Trash"));
+}
+
+NS_IMETHODIMP nsRssIncomingServer::SetFlagsOnDefaultMailboxes()
+{
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(rootFolder, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ localFolder->SetFlagsOnDefaultMailboxes(nsMsgFolderFlags::Trash);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::PerformBiff(nsIMsgWindow *aMsgWindow)
+{
+ // Get the account root (server) folder and pass it on.
+ nsCOMPtr<nsIMsgFolder> rootRSSFolder;
+ GetRootMsgFolder(getter_AddRefs(rootRSSFolder));
+ nsCOMPtr<nsIUrlListener> urlListener = do_QueryInterface(rootRSSFolder);
+ nsresult rv;
+ bool isBiff = true;
+ nsCOMPtr<nsINewsBlogFeedDownloader> rssDownloader =
+ do_GetService("@mozilla.org/newsblog-feed-downloader;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rssDownloader->DownloadFeed(rootRSSFolder, urlListener, isBiff, aMsgWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::GetNewMail(nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aUrlListener,
+ nsIMsgFolder *aFolder,
+ nsIURI **_retval)
+{
+ // Pass the selected folder on to the downloader.
+ NS_ENSURE_ARG_POINTER(aFolder);
+ nsresult rv;
+ bool isBiff = false;
+ nsCOMPtr<nsINewsBlogFeedDownloader> rssDownloader =
+ do_GetService("@mozilla.org/newsblog-feed-downloader;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rssDownloader->DownloadFeed(aFolder, aUrlListener, isBiff, aMsgWindow);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::GetAccountManagerChrome(nsAString& aResult)
+{
+ aResult.AssignLiteral("am-newsblog.xul");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::GetOfflineSupportLevel(int32_t *aSupportLevel)
+{
+ NS_ENSURE_ARG_POINTER(aSupportLevel);
+ *aSupportLevel = OFFLINE_SUPPORT_LEVEL_NONE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::GetSupportsDiskSpace(bool *aSupportsDiskSpace)
+{
+ NS_ENSURE_ARG_POINTER(aSupportsDiskSpace);
+ *aSupportsDiskSpace = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::GetServerRequiresPasswordForBiff(bool *aServerRequiresPasswordForBiff)
+{
+ NS_ENSURE_ARG_POINTER(aServerRequiresPasswordForBiff);
+ // For Feed folders, we don't require a password.
+ *aServerRequiresPasswordForBiff = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::GetCanSearchMessages(bool *canSearchMessages)
+{
+ NS_ENSURE_ARG_POINTER(canSearchMessages);
+ *canSearchMessages = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::MsgAdded(nsIMsgDBHdr *aMsg)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::MsgsClassified(nsIArray *aMsgs,
+ bool aJunkProcessed,
+ bool aTraitProcessed)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::MsgsDeleted(nsIArray *aMsgs)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::MsgsMoveCopyCompleted(bool aMove,
+ nsIArray *aSrcMsgs,
+ nsIMsgFolder *aDestFolder,
+ nsIArray *aDestMsgs)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::MsgKeyChanged(nsMsgKey aOldKey,
+ nsIMsgDBHdr *aNewHdr)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::FolderAdded(nsIMsgFolder *aFolder)
+{
+ // Nothing to do. Not necessary for new folder adds, as a new folder never
+ // has a subscription.
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::FolderDeleted(nsIMsgFolder *aFolder)
+{
+ // Not necessary for folder deletes, which are move to Trash and handled by
+ // movecopy. Virtual folder or trash folder deletes send a folderdeleted,
+ // but these should have no subscriptions already.
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssIncomingServer::FolderMoveCopyCompleted(bool aMove,
+ nsIMsgFolder *aSrcFolder,
+ nsIMsgFolder *aDestFolder)
+{
+ return FolderChanged(aDestFolder, aSrcFolder, (aMove ? "move" : "copy"));
+}
+
+NS_IMETHODIMP nsRssIncomingServer::FolderRenamed(nsIMsgFolder *aOrigFolder,
+ nsIMsgFolder *aNewFolder)
+{
+ return FolderChanged(aNewFolder, aOrigFolder, "rename");
+}
+
+NS_IMETHODIMP nsRssIncomingServer::ItemEvent(nsISupports *aItem,
+ const nsACString &aEvent,
+ nsISupports *aData)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult nsRssIncomingServer::FolderChanged(nsIMsgFolder *aFolder,
+ nsIMsgFolder *aOrigFolder,
+ const char *aAction)
+{
+ if (!aFolder)
+ return NS_OK;
+
+ nsresult rv;
+ nsCOMPtr<nsINewsBlogFeedDownloader> rssDownloader =
+ do_GetService("@mozilla.org/newsblog-feed-downloader;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rssDownloader->UpdateSubscriptionsDS(aFolder, aOrigFolder, aAction);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsRssIncomingServer::GetSortOrder(int32_t* aSortOrder)
+{
+ NS_ENSURE_ARG_POINTER(aSortOrder);
+ *aSortOrder = 400000000;
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsRssIncomingServer.h b/mailnews/local/src/nsRssIncomingServer.h
new file mode 100644
index 000000000..67406f19a
--- /dev/null
+++ b/mailnews/local/src/nsRssIncomingServer.h
@@ -0,0 +1,43 @@
+/* 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/. */
+
+#ifndef __nsRssIncomingServer_h
+#define __nsRssIncomingServer_h
+
+#include "mozilla/Attributes.h"
+#include "nsIRssIncomingServer.h"
+#include "nsILocalMailIncomingServer.h"
+#include "nsMsgIncomingServer.h"
+#include "nsIMsgFolderListener.h"
+#include "nsMailboxServer.h"
+
+class nsRssIncomingServer : public nsMailboxServer,
+ public nsIRssIncomingServer,
+ public nsILocalMailIncomingServer,
+ public nsIMsgFolderListener
+
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIRSSINCOMINGSERVER
+ NS_DECL_NSILOCALMAILINCOMINGSERVER
+ NS_DECL_NSIMSGFOLDERLISTENER
+
+ NS_IMETHOD GetOfflineSupportLevel(int32_t *aSupportLevel) override;
+ NS_IMETHOD GetSupportsDiskSpace(bool *aSupportsDiskSpace) override;
+ NS_IMETHOD GetAccountManagerChrome(nsAString& aResult) override;
+ NS_IMETHOD PerformBiff(nsIMsgWindow *aMsgWindow) override;
+ NS_IMETHOD GetServerRequiresPasswordForBiff(bool *aServerRequiresPasswordForBiff) override;
+ NS_IMETHOD GetCanSearchMessages(bool *canSearchMessages) override;
+ NS_IMETHOD GetSortOrder(int32_t* aSortOrder) override;
+
+ nsRssIncomingServer();
+protected:
+ virtual ~nsRssIncomingServer();
+ nsresult FolderChanged(nsIMsgFolder *aFolder, nsIMsgFolder *aOrigFolder, const char *aAction);
+ nsresult FillInDataSourcePath(const nsAString& aDataSourceName, nsIFile ** aLocation);
+ static nsrefcnt gInstanceCount;
+};
+
+#endif /* __nsRssIncomingServer_h */
diff --git a/mailnews/local/src/nsRssService.cpp b/mailnews/local/src/nsRssService.cpp
new file mode 100644
index 000000000..f5caac257
--- /dev/null
+++ b/mailnews/local/src/nsRssService.cpp
@@ -0,0 +1,130 @@
+/* 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 "nsRssService.h"
+#include "nsIRssIncomingServer.h"
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "nsMailDirServiceDefs.h"
+#include "nsIProperties.h"
+#include "nsServiceManagerUtils.h"
+
+nsRssService::nsRssService()
+{
+}
+
+nsRssService::~nsRssService()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsRssService,
+ nsIRssService,
+ nsIMsgProtocolInfo)
+
+NS_IMETHODIMP nsRssService::GetDefaultLocalPath(nsIFile * *aDefaultLocalPath)
+{
+ NS_ENSURE_ARG_POINTER(aDefaultLocalPath);
+ *aDefaultLocalPath = nullptr;
+
+ nsCOMPtr<nsIFile> localFile;
+ nsCOMPtr<nsIProperties> dirService(do_GetService("@mozilla.org/file/directory_service;1"));
+ if (!dirService) return NS_ERROR_FAILURE;
+ dirService->Get(NS_APP_MAIL_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(localFile));
+ if (!localFile) return NS_ERROR_FAILURE;
+
+ bool exists;
+ nsresult rv = localFile->Exists(&exists);
+ if (NS_SUCCEEDED(rv) && !exists)
+ rv = localFile->Create(nsIFile::DIRECTORY_TYPE, 0775);
+ if (NS_FAILED(rv)) return rv;
+
+ NS_IF_ADDREF(*aDefaultLocalPath = localFile);
+ return NS_OK;
+
+}
+
+NS_IMETHODIMP nsRssService::SetDefaultLocalPath(nsIFile * aDefaultLocalPath)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsRssService::GetServerIID(nsIID * *aServerIID)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsRssService::GetRequiresUsername(bool *aRequiresUsername)
+{
+ NS_ENSURE_ARG_POINTER(aRequiresUsername);
+ *aRequiresUsername = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssService::GetPreflightPrettyNameWithEmailAddress(bool *aPreflightPrettyNameWithEmailAddress)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsRssService::GetCanDelete(bool *aCanDelete)
+{
+ NS_ENSURE_ARG_POINTER(aCanDelete);
+ *aCanDelete = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssService::GetCanLoginAtStartUp(bool *aCanLoginAtStartUp)
+{
+ NS_ENSURE_ARG_POINTER(aCanLoginAtStartUp);
+ *aCanLoginAtStartUp = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssService::GetCanDuplicate(bool *aCanDuplicate)
+{
+ NS_ENSURE_ARG_POINTER(aCanDuplicate);
+ *aCanDuplicate = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssService::GetDefaultServerPort(bool isSecure, int32_t *_retval)
+{
+ *_retval = -1;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssService::GetCanGetMessages(bool *aCanGetMessages)
+{
+ NS_ENSURE_ARG_POINTER(aCanGetMessages);
+ *aCanGetMessages = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssService::GetCanGetIncomingMessages(bool *aCanGetIncomingMessages)
+{
+ NS_ENSURE_ARG_POINTER(aCanGetIncomingMessages);
+ *aCanGetIncomingMessages = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssService::GetDefaultDoBiff(bool *aDefaultDoBiff)
+{
+ NS_ENSURE_ARG_POINTER(aDefaultDoBiff);
+ // by default, do biff for RSS feeds
+ *aDefaultDoBiff = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssService::GetShowComposeMsgLink(bool *aShowComposeMsgLink)
+{
+ NS_ENSURE_ARG_POINTER(aShowComposeMsgLink);
+ *aShowComposeMsgLink = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsRssService::GetFoldersCreatedAsync(bool *aAsyncCreation)
+{
+ NS_ENSURE_ARG_POINTER(aAsyncCreation);
+ *aAsyncCreation = false;
+ return NS_OK;
+}
diff --git a/mailnews/local/src/nsRssService.h b/mailnews/local/src/nsRssService.h
new file mode 100644
index 000000000..45893bba7
--- /dev/null
+++ b/mailnews/local/src/nsRssService.h
@@ -0,0 +1,25 @@
+/* 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/. */
+
+#ifndef nsRssService_h___
+#define nsRssService_h___
+
+#include "nsIRssService.h"
+#include "nsIMsgProtocolInfo.h"
+
+class nsRssService : public nsIMsgProtocolInfo, public nsIRssService
+{
+public:
+
+ nsRssService();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIRSSSERVICE
+ NS_DECL_NSIMSGPROTOCOLINFO
+
+private:
+ virtual ~nsRssService();
+};
+
+#endif /* nsRssService_h___ */