summaryrefslogtreecommitdiffstats
path: root/mailnews/local/src/nsLocalMailFolder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/local/src/nsLocalMailFolder.cpp')
-rw-r--r--mailnews/local/src/nsLocalMailFolder.cpp3852
1 files changed, 3852 insertions, 0 deletions
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;
+}