summaryrefslogtreecommitdiffstats
path: root/mailnews/local/src/nsMsgBrkMBoxStore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/local/src/nsMsgBrkMBoxStore.cpp')
-rw-r--r--mailnews/local/src/nsMsgBrkMBoxStore.cpp1124
1 files changed, 1124 insertions, 0 deletions
diff --git a/mailnews/local/src/nsMsgBrkMBoxStore.cpp b/mailnews/local/src/nsMsgBrkMBoxStore.cpp
new file mode 100644
index 000000000..6eb3063ad
--- /dev/null
+++ b/mailnews/local/src/nsMsgBrkMBoxStore.cpp
@@ -0,0 +1,1124 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ Class for handling Berkeley Mailbox stores.
+*/
+
+#include "prlog.h"
+#include "msgCore.h"
+#include "nsMsgBrkMBoxStore.h"
+#include "nsIMsgFolder.h"
+#include "nsMsgFolderFlags.h"
+#include "nsILocalMailIncomingServer.h"
+#include "nsIMsgLocalMailFolder.h"
+#include "nsCOMArray.h"
+#include "nsIFile.h"
+#include "nsIMsgHdr.h"
+#include "nsNetUtil.h"
+#include "nsIMsgDatabase.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsMsgUtils.h"
+#include "nsMsgDBCID.h"
+#include "nsIDBFolderInfo.h"
+#include "nsIArray.h"
+#include "nsArrayUtils.h"
+#include "nsMsgLocalFolderHdrs.h"
+#include "nsMailHeaders.h"
+#include "nsReadLine.h"
+#include "nsParseMailbox.h"
+#include "nsIMailboxService.h"
+#include "nsMsgLocalCID.h"
+#include "nsIMsgFolderCompactor.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "mozilla/Preferences.h"
+#include "prprf.h"
+#include <cstdlib> // for std::abs(int/long)
+#include <cmath> // for std::abs(float/double)
+
+nsMsgBrkMBoxStore::nsMsgBrkMBoxStore()
+{
+}
+
+nsMsgBrkMBoxStore::~nsMsgBrkMBoxStore()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsMsgBrkMBoxStore, nsIMsgPluggableStore)
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::DiscoverSubFolders(nsIMsgFolder *aParentFolder,
+ bool aDeep)
+{
+ NS_ENSURE_ARG_POINTER(aParentFolder);
+
+ nsCOMPtr<nsIFile> path;
+ nsresult rv = aParentFolder->GetFilePath(getter_AddRefs(path));
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool exists;
+ path->Exists(&exists);
+ if (!exists) {
+ rv = path->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return AddSubFolders(aParentFolder, path, aDeep);
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::CreateFolder(nsIMsgFolder *aParent,
+ const nsAString &aFolderName,
+ nsIMsgFolder **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aParent);
+ NS_ENSURE_ARG_POINTER(aResult);
+ if (aFolderName.IsEmpty())
+ return NS_MSG_ERROR_INVALID_FOLDER_NAME;
+
+ nsCOMPtr<nsIFile> path;
+ nsCOMPtr<nsIMsgFolder> child;
+ nsresult rv = aParent->GetFilePath(getter_AddRefs(path));
+ if (NS_FAILED(rv))
+ return rv;
+ //Get a directory based on our current path.
+ rv = CreateDirectoryForFolder(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Now we have a valid directory or we have returned.
+ // Make sure the new folder name is valid
+ nsAutoString safeFolderName(aFolderName);
+ NS_MsgHashIfNecessary(safeFolderName);
+
+ path->Append(safeFolderName);
+ bool exists;
+ path->Exists(&exists);
+ if (exists) //check this because localized names are different from disk names
+ return NS_MSG_FOLDER_EXISTS;
+
+ rv = path->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ //GetFlags and SetFlags in AddSubfolder will fail because we have no db at
+ // this point but mFlags is set.
+ rv = aParent->AddSubfolder(safeFolderName, getter_AddRefs(child));
+ if (!child || NS_FAILED(rv))
+ {
+ path->Remove(false);
+ return rv;
+ }
+ // Create an empty database for this mail folder, set its name from the user
+ nsCOMPtr<nsIMsgDBService> msgDBService =
+ do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+ if (msgDBService)
+ {
+ nsCOMPtr<nsIMsgDatabase> unusedDB;
+ rv = msgDBService->OpenFolderDB(child, true, getter_AddRefs(unusedDB));
+ if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
+ rv = msgDBService->CreateNewDB(child, getter_AddRefs(unusedDB));
+
+ if ((NS_SUCCEEDED(rv) || rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE) &&
+ unusedDB)
+ {
+ //need to set the folder name
+ nsCOMPtr<nsIDBFolderInfo> folderInfo;
+ rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
+ if (NS_SUCCEEDED(rv))
+ folderInfo->SetMailboxName(safeFolderName);
+
+ unusedDB->SetSummaryValid(true);
+ unusedDB->Close(true);
+ aParent->UpdateSummaryTotals(true);
+ }
+ else
+ {
+ path->Remove(false);
+ rv = NS_MSG_CANT_CREATE_FOLDER;
+ }
+ }
+ child.forget(aResult);
+ return rv;
+}
+
+// Get the current attributes of the mbox file, corrected for caching
+void nsMsgBrkMBoxStore::GetMailboxModProperties(nsIMsgFolder *aFolder,
+ int64_t *aSize, uint32_t *aDate)
+{
+ // We'll simply return 0 on errors.
+ *aDate = 0;
+ *aSize = 0;
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ rv = pathFile->GetFileSize(aSize);
+ if (NS_FAILED(rv))
+ return; // expected result for virtual folders
+
+ PRTime lastModTime;
+ rv = pathFile->GetLastModifiedTime(&lastModTime);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ *aDate = (uint32_t) (lastModTime / PR_MSEC_PER_SEC);
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::HasSpaceAvailable(nsIMsgFolder *aFolder,
+ int64_t aSpaceRequested,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_ARG_POINTER(aFolder);
+
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool allow4GBfolders = mozilla::Preferences::GetBool("mailnews.allowMboxOver4GB", true);
+
+ if (!allow4GBfolders) {
+ // Allow the mbox to only reach 0xFFC00000 = 4 GiB - 4 MiB.
+ int64_t fileSize;
+ rv = pathFile->GetFileSize(&fileSize);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ *aResult = ((fileSize + aSpaceRequested) < 0xFFC00000LL);
+ if (!*aResult)
+ return NS_ERROR_FILE_TOO_BIG;
+ }
+
+ *aResult = DiskSpaceAvailableInStore(pathFile, aSpaceRequested);
+ if (!*aResult)
+ return NS_ERROR_FILE_DISK_FULL;
+
+ return NS_OK;
+}
+
+static bool gGotGlobalPrefs = false;
+static int32_t gTimeStampLeeway = 60;
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::IsSummaryFileValid(nsIMsgFolder *aFolder,
+ nsIMsgDatabase *aDB,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aDB);
+ NS_ENSURE_ARG_POINTER(aResult);
+ // We only check local folders for db validity.
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder(do_QueryInterface(aFolder));
+ if (!localFolder)
+ {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIDBFolderInfo> folderInfo;
+ rv = aDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
+ NS_ENSURE_SUCCESS(rv, rv);
+ int64_t folderSize;
+ uint32_t folderDate;
+ int32_t numUnreadMessages;
+
+ *aResult = false;
+
+ folderInfo->GetNumUnreadMessages(&numUnreadMessages);
+ folderInfo->GetFolderSize(&folderSize);
+ folderInfo->GetFolderDate(&folderDate);
+
+ int64_t fileSize = 0;
+ uint32_t actualFolderTimeStamp = 0;
+ GetMailboxModProperties(aFolder, &fileSize, &actualFolderTimeStamp);
+
+ if (folderSize == fileSize && numUnreadMessages >= 0)
+ {
+ if (!folderSize)
+ {
+ *aResult = true;
+ return NS_OK;
+ }
+ if (!gGotGlobalPrefs)
+ {
+ nsCOMPtr<nsIPrefBranch> pPrefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ if (pPrefBranch)
+ {
+ rv = pPrefBranch->GetIntPref("mail.db_timestamp_leeway", &gTimeStampLeeway);
+ gGotGlobalPrefs = true;
+ }
+ }
+ // if those values are ok, check time stamp
+ if (gTimeStampLeeway == 0)
+ *aResult = folderDate == actualFolderTimeStamp;
+ else
+ *aResult = std::abs((int32_t) (actualFolderTimeStamp - folderDate)) <= gTimeStampLeeway;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::SetSummaryFileValid(nsIMsgFolder *aFolder,
+ nsIMsgDatabase *aDB,
+ bool aValid)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aDB);
+ // We only need to do this for local folders.
+ nsCOMPtr<nsIMsgLocalMailFolder> localFolder(do_QueryInterface(aFolder));
+ if (!localFolder)
+ return NS_OK;
+
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIDBFolderInfo> folderInfo;
+ rv = aDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
+ NS_ENSURE_SUCCESS(rv, rv);
+ bool exists;
+ pathFile->Exists(&exists);
+ if (!exists)
+ return NS_MSG_ERROR_FOLDER_MISSING;
+
+ if (aValid)
+ {
+ uint32_t actualFolderTimeStamp;
+ int64_t fileSize;
+ GetMailboxModProperties(aFolder, &fileSize, &actualFolderTimeStamp);
+ folderInfo->SetFolderSize(fileSize);
+ folderInfo->SetFolderDate(actualFolderTimeStamp);
+ }
+ else
+ {
+ folderInfo->SetVersion(0); // that ought to do the trick.
+ }
+ aDB->Commit(nsMsgDBCommitType::kLargeCommit);
+ return rv;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::DeleteFolder(nsIMsgFolder *aFolder)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ //Delete mailbox
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ pathFile->Remove(false);
+
+ bool isDirectory = false;
+ pathFile->IsDirectory(&isDirectory);
+ if (!isDirectory)
+ {
+ nsAutoString leafName;
+ pathFile->GetLeafName(leafName);
+ leafName.AppendLiteral(FOLDER_SUFFIX);
+ pathFile->SetLeafName(leafName);
+ }
+ isDirectory = false;
+ pathFile->IsDirectory(&isDirectory);
+ //If this is a directory, then remove it.
+ return isDirectory ? pathFile->Remove(true) : NS_OK;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::RenameFolder(nsIMsgFolder *aFolder,
+ const nsAString & aNewName,
+ nsIMsgFolder **aNewFolder)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aNewFolder);
+
+ uint32_t numChildren;
+ aFolder->GetNumSubFolders(&numChildren);
+ nsString existingName;
+ aFolder->GetName(existingName);
+
+ nsCOMPtr<nsIFile> oldPathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(oldPathFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIMsgFolder> parentFolder;
+ rv = aFolder->GetParent(getter_AddRefs(parentFolder));
+ if (!parentFolder)
+ return NS_ERROR_NULL_POINTER;
+
+ nsCOMPtr<nsISupports> parentSupport = do_QueryInterface(parentFolder);
+
+ nsCOMPtr<nsIFile> oldSummaryFile;
+ rv = aFolder->GetSummaryFile(getter_AddRefs(oldSummaryFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> dirFile;
+ oldPathFile->Clone(getter_AddRefs(dirFile));
+
+ if (numChildren > 0)
+ {
+ rv = CreateDirectoryForFolder(dirFile);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsAutoString safeName(aNewName);
+ NS_MsgHashIfNecessary(safeName);
+
+ nsAutoCString oldLeafName;
+ oldPathFile->GetNativeLeafName(oldLeafName);
+
+ nsCOMPtr<nsIFile> parentPathFile;
+ parentFolder->GetFilePath(getter_AddRefs(parentPathFile));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool isDirectory = false;
+ parentPathFile->IsDirectory(&isDirectory);
+ if (!isDirectory)
+ {
+ nsAutoString leafName;
+ parentPathFile->GetLeafName(leafName);
+ leafName.AppendLiteral(FOLDER_SUFFIX);
+ rv = parentPathFile->SetLeafName(leafName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ aFolder->ForceDBClosed();
+ //save off dir name before appending .msf
+ rv = oldPathFile->MoveTo(nullptr, safeName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsString dbName(safeName);
+ dbName += NS_LITERAL_STRING(SUMMARY_SUFFIX);
+ oldSummaryFile->MoveTo(nullptr, dbName);
+
+ if (numChildren > 0)
+ {
+ // rename "*.sbd" directory
+ nsAutoString newNameDirStr(safeName);
+ newNameDirStr += NS_LITERAL_STRING(".sbd");
+ dirFile->MoveTo(nullptr, newNameDirStr);
+ }
+
+ return parentFolder->AddSubfolder(safeName, aNewFolder);
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::CopyFolder(nsIMsgFolder *aSrcFolder,
+ nsIMsgFolder *aDstFolder,
+ bool aIsMoveFolder,
+ nsIMsgWindow *aMsgWindow,
+ nsIMsgCopyServiceListener *aListener,
+ const nsAString &aNewName)
+{
+ NS_ENSURE_ARG_POINTER(aSrcFolder);
+ NS_ENSURE_ARG_POINTER(aDstFolder);
+
+ nsAutoString folderName;
+ if (aNewName.IsEmpty())
+ aSrcFolder->GetName(folderName);
+ else
+ folderName.Assign(aNewName);
+
+ nsAutoString safeFolderName(folderName);
+ NS_MsgHashIfNecessary(safeFolderName);
+ nsCOMPtr<nsIMsgLocalMailFolder> localSrcFolder(do_QueryInterface(aSrcFolder));
+ nsCOMPtr<nsIMsgDatabase> srcDB;
+ if (localSrcFolder)
+ localSrcFolder->GetDatabaseWOReparse(getter_AddRefs(srcDB));
+ bool summaryValid = !!srcDB;
+ srcDB = nullptr;
+ aSrcFolder->ForceDBClosed();
+
+ nsCOMPtr<nsIFile> oldPath;
+ nsresult rv = aSrcFolder->GetFilePath(getter_AddRefs(oldPath));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ nsCOMPtr<nsIFile> summaryFile;
+ GetSummaryFileLocation(oldPath, getter_AddRefs(summaryFile));
+
+ nsCOMPtr<nsIFile> newPath;
+ rv = aDstFolder->GetFilePath(getter_AddRefs(newPath));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ bool newPathIsDirectory = false;
+ newPath->IsDirectory(&newPathIsDirectory);
+ if (!newPathIsDirectory)
+ {
+ AddDirectorySeparator(newPath);
+ rv = newPath->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ if (rv == NS_ERROR_FILE_ALREADY_EXISTS)
+ rv = NS_OK;
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCOMPtr<nsIFile> origPath;
+ oldPath->Clone(getter_AddRefs(origPath));
+
+ //copying necessary for aborting.... if failure return
+ rv = oldPath->CopyTo(newPath, safeFolderName);
+ NS_ENSURE_SUCCESS(rv, rv); // Will fail if a file by that name exists
+
+ // Copy to dir can fail if filespec does not exist. If copy fails, we test
+ // if the filespec exist or not, if it does not that's ok, we continue
+ // without copying it. If it fails and filespec exist and is not zero sized
+ // there is real problem
+ // Copy the file to the new dir
+ nsAutoString dbName(safeFolderName);
+ dbName += NS_LITERAL_STRING(SUMMARY_SUFFIX);
+ rv = summaryFile->CopyTo(newPath, dbName);
+ if (NS_FAILED(rv)) // Test if the copy is successful
+ {
+ // Test if the filespec has data
+ bool exists;
+ int64_t fileSize;
+ summaryFile->Exists(&exists);
+ summaryFile->GetFileSize(&fileSize);
+ if (exists && fileSize > 0)
+ NS_ENSURE_SUCCESS(rv, rv); // Yes, it should have worked !
+ // else case is filespec is zero sized, no need to copy it,
+ // not an error
+ }
+
+ nsCOMPtr<nsIMsgFolder> newMsgFolder;
+ rv = aDstFolder->AddSubfolder(safeFolderName, getter_AddRefs(newMsgFolder));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // linux and mac are not good about maintaining the file stamp when copying
+ // folders around. So if the source folder db is good, set the dest db as
+ // good too.
+ nsCOMPtr<nsIMsgDatabase> destDB;
+ if (summaryValid)
+ {
+ nsAutoString folderLeafName;
+ origPath->GetLeafName(folderLeafName);
+ newPath->Append(folderLeafName);
+ nsCOMPtr<nsIMsgDBService> msgDBService =
+ do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = msgDBService->OpenMailDBFromFile(newPath, newMsgFolder, false,
+ true, getter_AddRefs(destDB));
+ if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE && destDB)
+ destDB->SetSummaryValid(true);
+ }
+ newMsgFolder->SetPrettyName(folderName);
+ uint32_t flags;
+ aSrcFolder->GetFlags(&flags);
+ newMsgFolder->SetFlags(flags);
+ bool changed = false;
+ rv = aSrcFolder->MatchOrChangeFilterDestination(newMsgFolder, true, &changed);
+ if (changed)
+ aSrcFolder->AlertFilterChanged(aMsgWindow);
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = aSrcFolder->GetSubFolders(getter_AddRefs(enumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Copy subfolders to the new location
+ nsresult copyStatus = NS_OK;
+ nsCOMPtr<nsIMsgLocalMailFolder> localNewFolder(do_QueryInterface(newMsgFolder, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ bool hasMore;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore &&
+ NS_SUCCEEDED(copyStatus))
+ {
+ nsCOMPtr<nsISupports> item;
+ enumerator->GetNext(getter_AddRefs(item));
+
+ nsCOMPtr<nsIMsgFolder> folder(do_QueryInterface(item));
+ if (!folder)
+ continue;
+
+ copyStatus = localNewFolder->CopyFolderLocal(folder, false,
+ aMsgWindow, aListener);
+ // Test if the call succeeded, if not we have to stop recursive call
+ if (NS_FAILED(copyStatus))
+ {
+ // Copy failed we have to notify caller to handle the error and stop
+ // moving the folders. In case this happens to the topmost level of
+ // recursive call, then we just need to break from the while loop and
+ // go to error handling code.
+ if (!aIsMoveFolder)
+ return copyStatus;
+ break;
+ }
+ }
+ }
+
+ if (aIsMoveFolder && NS_SUCCEEDED(copyStatus))
+ {
+ if (localNewFolder)
+ {
+ nsCOMPtr<nsISupports> srcSupport(do_QueryInterface(aSrcFolder));
+ localNewFolder->OnCopyCompleted(srcSupport, true);
+ }
+
+ // Notify the "folder" that was dragged and dropped has been created. No
+ // need to do this for its subfolders. isMoveFolder will be true for folder.
+ aDstFolder->NotifyItemAdded(newMsgFolder);
+
+ nsCOMPtr<nsIMsgFolder> msgParent;
+ aSrcFolder->GetParent(getter_AddRefs(msgParent));
+ aSrcFolder->SetParent(nullptr);
+ if (msgParent)
+ {
+ // The files have already been moved, so delete storage false
+ msgParent->PropagateDelete(aSrcFolder, false, aMsgWindow);
+ oldPath->Remove(false); //berkeley mailbox
+ // We need to force closed the source db
+ nsCOMPtr<nsIMsgDatabase> srcDB;
+ aSrcFolder->Delete();
+
+ nsCOMPtr<nsIFile> parentPath;
+ rv = msgParent->GetFilePath(getter_AddRefs(parentPath));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ AddDirectorySeparator(parentPath);
+ nsCOMPtr<nsISimpleEnumerator> children;
+ parentPath->GetDirectoryEntries(getter_AddRefs(children));
+ bool more;
+ // checks if the directory is empty or not
+ if (children && NS_SUCCEEDED(children->HasMoreElements(&more)) && !more)
+ parentPath->Remove(true);
+ }
+ }
+ else
+ {
+ // This is the case where the copy of a subfolder failed.
+ // We have to delete the newDirectory tree to make a "rollback".
+ // Someone should add a popup to warn the user that the move was not
+ // possible.
+ if (aIsMoveFolder && NS_FAILED(copyStatus))
+ {
+ nsCOMPtr<nsIMsgFolder> msgParent;
+ newMsgFolder->ForceDBClosed();
+ newMsgFolder->GetParent(getter_AddRefs(msgParent));
+ newMsgFolder->SetParent(nullptr);
+ if (msgParent)
+ {
+ msgParent->PropagateDelete(newMsgFolder, false, aMsgWindow);
+ newMsgFolder->Delete();
+ newMsgFolder->ForceDBClosed();
+ AddDirectorySeparator(newPath);
+ newPath->Remove(true); //berkeley mailbox
+ }
+ return NS_ERROR_FAILURE;
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::GetNewMsgOutputStream(nsIMsgFolder *aFolder,
+ nsIMsgDBHdr **aNewMsgHdr,
+ bool *aReusable,
+ nsIOutputStream **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ NS_ENSURE_ARG_POINTER(aNewMsgHdr);
+ NS_ENSURE_ARG_POINTER(aReusable);
+ NS_ENSURE_ARG_POINTER(aResult);
+
+#ifdef _DEBUG
+ NS_ASSERTION(m_streamOutstandingFolder != aFolder, "didn't finish prev msg");
+ m_streamOutstandingFolder = aFolder;
+#endif
+ *aReusable = true;
+ nsCOMPtr<nsIFile> mboxFile;
+ aFolder->GetFilePath(getter_AddRefs(mboxFile));
+ nsCOMPtr<nsIMsgDatabase> db;
+ aFolder->GetMsgDatabase(getter_AddRefs(db));
+ if (!db && !*aNewMsgHdr)
+ NS_WARNING("no db, and no message header");
+ bool exists;
+ nsresult rv;
+ mboxFile->Exists(&exists);
+ if (!exists) {
+ rv = mboxFile->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ nsCString URI;
+ aFolder->GetURI(URI);
+ nsCOMPtr<nsISeekableStream> seekable;
+ if (m_outputStreams.Get(URI, aResult))
+ {
+ seekable = do_QueryInterface(*aResult, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
+ if (NS_FAILED(rv))
+ {
+ m_outputStreams.Remove(URI);
+ NS_RELEASE(*aResult);
+ }
+ }
+ if (!*aResult)
+ {
+ rv = MsgGetFileStream(mboxFile, aResult);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed opening offline store for output");
+ if (NS_FAILED(rv))
+ printf("failed opening offline store for %s\n", URI.get());
+ NS_ENSURE_SUCCESS(rv, rv);
+ seekable = do_QueryInterface(*aResult, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = seekable->Seek(nsISeekableStream::NS_SEEK_END, 0);
+ NS_ENSURE_SUCCESS(rv, rv);
+ m_outputStreams.Put(URI, *aResult);
+ }
+ int64_t filePos;
+ seekable->Tell(&filePos);
+
+ if (db && !*aNewMsgHdr)
+ {
+ db->CreateNewHdr(nsMsgKey_None, aNewMsgHdr);
+ }
+
+ if (*aNewMsgHdr)
+ {
+ char storeToken[100];
+ PR_snprintf(storeToken, sizeof(storeToken), "%lld", filePos);
+ (*aNewMsgHdr)->SetMessageOffset(filePos);
+ (*aNewMsgHdr)->SetStringProperty("storeToken", storeToken);
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::DiscardNewMessage(nsIOutputStream *aOutputStream,
+ nsIMsgDBHdr *aNewHdr)
+{
+ NS_ENSURE_ARG_POINTER(aOutputStream);
+ NS_ENSURE_ARG_POINTER(aNewHdr);
+#ifdef _DEBUG
+ m_streamOutstandingFolder = nullptr;
+#endif
+ uint64_t hdrOffset;
+ aNewHdr->GetMessageOffset(&hdrOffset);
+ aOutputStream->Close();
+ nsCOMPtr<nsIFile> mboxFile;
+ nsCOMPtr<nsIMsgFolder> folder;
+ nsresult rv = aNewHdr->GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ folder->GetFilePath(getter_AddRefs(mboxFile));
+ return mboxFile->SetFileSize(hdrOffset);
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::FinishNewMessage(nsIOutputStream *aOutputStream,
+ nsIMsgDBHdr *aNewHdr)
+{
+#ifdef _DEBUG
+ m_streamOutstandingFolder = nullptr;
+#endif
+ NS_ENSURE_ARG_POINTER(aOutputStream);
+// NS_ENSURE_ARG_POINTER(aNewHdr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::MoveNewlyDownloadedMessage(nsIMsgDBHdr *aNewHdr,
+ nsIMsgFolder *aDestFolder,
+ bool *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aNewHdr);
+ NS_ENSURE_ARG_POINTER(aDestFolder);
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::GetMsgInputStream(nsIMsgFolder *aMsgFolder,
+ const nsACString &aMsgToken,
+ int64_t *aOffset,
+ nsIMsgDBHdr *aMsgHdr,
+ bool *aReusable,
+ nsIInputStream **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aMsgFolder);
+ NS_ENSURE_ARG_POINTER(aResult);
+ NS_ENSURE_ARG_POINTER(aOffset);
+
+ // If there is no store token, then we set it to the existing message offset.
+ if (aMsgToken.IsEmpty())
+ {
+ uint64_t offset;
+ NS_ENSURE_ARG_POINTER(aMsgHdr);
+ aMsgHdr->GetMessageOffset(&offset);
+ *aOffset = int64_t(offset);
+ char storeToken[100];
+ PR_snprintf(storeToken, sizeof(storeToken), "%lld", *aOffset);
+ aMsgHdr->SetStringProperty("storeToken", storeToken);
+ }
+ else
+ *aOffset = ParseUint64Str(PromiseFlatCString(aMsgToken).get());
+ *aReusable = true;
+ nsCString URI;
+ nsCOMPtr<nsIFile> mboxFile;
+
+ aMsgFolder->GetURI(URI);
+ aMsgFolder->GetFilePath(getter_AddRefs(mboxFile));
+ return NS_NewLocalFileInputStream(aResult, mboxFile);
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::DeleteMessages(nsIArray *aHdrArray)
+{
+ return ChangeFlags(aHdrArray, nsMsgMessageFlags::Expunged, true);
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::CopyMessages(bool isMove, nsIArray *aHdrArray,
+ nsIMsgFolder *aDstFolder,
+ nsIMsgCopyServiceListener *aListener,
+ nsIArray **aDstHdrs,
+ nsITransaction **aUndoAction,
+ bool *aCopyDone)
+{
+ NS_ENSURE_ARG_POINTER(aHdrArray);
+ NS_ENSURE_ARG_POINTER(aDstFolder);
+ NS_ENSURE_ARG_POINTER(aDstHdrs);
+ NS_ENSURE_ARG_POINTER(aCopyDone);
+ *aDstHdrs = nullptr;
+ *aUndoAction = nullptr;
+ *aCopyDone = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgBrkMBoxStore::GetSupportsCompaction(bool *aSupportsCompaction)
+{
+ NS_ENSURE_ARG_POINTER(aSupportsCompaction);
+ *aSupportsCompaction = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::CompactFolder(nsIMsgFolder *aFolder,
+ nsIUrlListener *aListener,
+ nsIMsgWindow *aMsgWindow)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ nsresult rv;
+ nsCOMPtr<nsIMsgFolderCompactor> folderCompactor =
+ do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ int64_t expungedBytes = 0;
+ aFolder->GetExpungedBytes(&expungedBytes);
+ // check if we need to compact the folder
+ return (expungedBytes > 0) ?
+ folderCompactor->Compact(aFolder, false, aListener, aMsgWindow) :
+ aFolder->NotifyCompactCompleted();
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::RebuildIndex(nsIMsgFolder *aFolder,
+ nsIMsgDatabase *aMsgDB,
+ nsIMsgWindow *aMsgWindow,
+ nsIUrlListener *aListener)
+{
+ NS_ENSURE_ARG_POINTER(aFolder);
+ nsCOMPtr<nsIFile> pathFile;
+ nsresult rv = aFolder->GetFilePath(getter_AddRefs(pathFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ bool isLocked;
+ aFolder->GetLocked(&isLocked);
+ if (isLocked)
+ {
+ NS_ASSERTION(false, "Could not get folder lock");
+ return NS_MSG_FOLDER_BUSY;
+ }
+
+ nsCOMPtr<nsIMailboxService> mailboxService =
+ do_GetService(NS_MAILBOXSERVICE_CONTRACTID1, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<nsMsgMailboxParser> parser = new nsMsgMailboxParser(aFolder);
+ NS_ENSURE_TRUE(parser, NS_ERROR_OUT_OF_MEMORY);
+ rv = parser->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mailboxService->ParseMailbox(aMsgWindow, pathFile, parser, aListener,
+ nullptr);
+ if (NS_SUCCEEDED(rv))
+ ResetForceReparse(aMsgDB);
+ return rv;
+}
+
+nsresult
+nsMsgBrkMBoxStore::GetOutputStream(nsIArray *aHdrArray,
+ nsCOMPtr<nsIOutputStream> &outputStream,
+ nsCOMPtr<nsISeekableStream> &seekableStream,
+ int64_t &restorePos)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgDBHdr> msgHdr = do_QueryElementAt(aHdrArray, 0, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCOMPtr<nsIMsgFolder> folder;
+ msgHdr->GetFolder(getter_AddRefs(folder));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCString URI;
+ folder->GetURI(URI);
+ restorePos = -1;
+ if (m_outputStreams.Get(URI, getter_AddRefs(outputStream)))
+ {
+ seekableStream = do_QueryInterface(outputStream);
+ rv = seekableStream->Tell(&restorePos);
+ if (NS_FAILED(rv))
+ {
+ outputStream = nullptr;
+ m_outputStreams.Remove(URI);
+ }
+ }
+ nsCOMPtr<nsIFile> mboxFile;
+ folder->GetFilePath(getter_AddRefs(mboxFile));
+ if (!outputStream)
+ {
+ rv = MsgGetFileStream(mboxFile, getter_AddRefs(outputStream));
+ seekableStream = do_QueryInterface(outputStream);
+ if (NS_SUCCEEDED(rv))
+ m_outputStreams.Put(URI, outputStream);
+ }
+ return rv;
+}
+
+void nsMsgBrkMBoxStore::SetDBValid(nsIMsgDBHdr *aHdr)
+{
+ nsCOMPtr<nsIMsgFolder> folder;
+ aHdr->GetFolder(getter_AddRefs(folder));
+ if (folder)
+ {
+ nsCOMPtr<nsIMsgDatabase> db;
+ folder->GetMsgDatabase(getter_AddRefs(db));
+ if (db)
+ SetSummaryFileValid(folder, db, true);
+ }
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeFlags(nsIArray *aHdrArray,
+ uint32_t aFlags,
+ bool aSet)
+{
+ NS_ENSURE_ARG_POINTER(aHdrArray);
+ nsCOMPtr<nsIOutputStream> outputStream;
+ nsCOMPtr<nsISeekableStream> seekableStream;
+ int64_t restoreStreamPos;
+
+ uint32_t messageCount;
+ nsresult rv = aHdrArray->GetLength(&messageCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!messageCount)
+ return NS_ERROR_INVALID_ARG;
+
+ rv = GetOutputStream(aHdrArray, outputStream, seekableStream,
+ restoreStreamPos);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ for (uint32_t i = 0; i < messageCount; i++)
+ {
+ msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
+ // Seek to x-mozilla-status offset and rewrite value.
+ rv = UpdateFolderFlag(msgHdr, aSet, aFlags, outputStream);
+ if (NS_FAILED(rv))
+ {
+ NS_WARNING("updateFolderFlag failed");
+ break;
+ }
+ }
+ if (restoreStreamPos != -1)
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, restoreStreamPos);
+ else if (outputStream)
+ outputStream->Close();
+ if (messageCount > 0)
+ {
+ msgHdr = do_QueryElementAt(aHdrArray, 0);
+ SetDBValid(msgHdr);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::ChangeKeywords(nsIArray *aHdrArray,
+ const nsACString &aKeywords,
+ bool aAdd)
+{
+ NS_ENSURE_ARG_POINTER(aHdrArray);
+ nsCOMPtr<nsIOutputStream> outputStream;
+ nsCOMPtr<nsISeekableStream> seekableStream;
+ int64_t restoreStreamPos;
+
+ uint32_t messageCount;
+ nsresult rv = aHdrArray->GetLength(&messageCount);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!messageCount)
+ return NS_ERROR_INVALID_ARG;
+
+ rv = GetOutputStream(aHdrArray, outputStream, seekableStream,
+ restoreStreamPos);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(outputStream, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoPtr<nsLineBuffer<char> > lineBuffer(new nsLineBuffer<char>);
+ NS_ENSURE_TRUE(lineBuffer, NS_ERROR_OUT_OF_MEMORY);
+
+ // For each message, we seek to the beginning of the x-mozilla-status header,
+ // and start reading lines, looking for x-mozilla-keys: headers; If we're
+ // adding the keyword and we find
+ // a header with the desired keyword already in it, we don't need to
+ // do anything. Likewise, if removing keyword and we don't find it,
+ // we don't need to do anything. Otherwise, if adding, we need to
+ // see if there's an x-mozilla-keys
+ // header with room for the new keyword. If so, we replace the
+ // corresponding number of spaces with the keyword. If no room,
+ // we can't do anything until the folder is compacted and another
+ // x-mozilla-keys header is added. In that case, we set a property
+ // on the header, which the compaction code will check.
+
+ nsTArray<nsCString> keywordArray;
+ ParseString(aKeywords, ' ', keywordArray);
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ for (uint32_t i = 0; i < messageCount; ++i) // for each message
+ {
+ msgHdr = do_QueryElementAt(aHdrArray, i, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ uint64_t messageOffset;
+ msgHdr->GetMessageOffset(&messageOffset);
+ uint32_t statusOffset = 0;
+ (void)msgHdr->GetStatusOffset(&statusOffset);
+ uint64_t desiredOffset = messageOffset + statusOffset;
+
+ ChangeKeywordsHelper(msgHdr, desiredOffset, lineBuffer, keywordArray,
+ aAdd, outputStream, seekableStream, inputStream);
+ }
+ lineBuffer = nullptr;
+ if (restoreStreamPos != -1)
+ seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, restoreStreamPos);
+ else if (outputStream)
+ outputStream->Close();
+ if (messageCount > 0)
+ {
+ msgHdr = do_QueryElementAt(aHdrArray, 0);
+ SetDBValid(msgHdr);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgBrkMBoxStore::GetStoreType(nsACString& aType)
+{
+ aType.AssignLiteral("mbox");
+ return NS_OK;
+}
+
+// Iterates over the files in the "path" directory, and adds subfolders to
+// parent for each mailbox file found.
+nsresult
+nsMsgBrkMBoxStore::AddSubFolders(nsIMsgFolder *parent, nsCOMPtr<nsIFile> &path,
+ bool deep)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> tmp; // at top level so we can safely assign to path
+ bool isDirectory;
+ path->IsDirectory(&isDirectory);
+ if (!isDirectory)
+ {
+ rv = path->Clone(getter_AddRefs(tmp));
+ path = tmp;
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoString leafName;
+ path->GetLeafName(leafName);
+ leafName.AppendLiteral(".sbd");
+ path->SetLeafName(leafName);
+ path->IsDirectory(&isDirectory);
+ }
+ if (!isDirectory)
+ return NS_OK;
+ // first find out all the current subfolders and files, before using them
+ // while creating new subfolders; we don't want to modify and iterate the same
+ // directory at once.
+ nsCOMArray<nsIFile> currentDirEntries;
+ nsCOMPtr<nsISimpleEnumerator> directoryEnumerator;
+ rv = path->GetDirectoryEntries(getter_AddRefs(directoryEnumerator));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool hasMore;
+ while (NS_SUCCEEDED(directoryEnumerator->HasMoreElements(&hasMore)) &&
+ hasMore)
+ {
+ nsCOMPtr<nsISupports> aSupport;
+ directoryEnumerator->GetNext(getter_AddRefs(aSupport));
+ nsCOMPtr<nsIFile> currentFile(do_QueryInterface(aSupport, &rv));
+ if (currentFile)
+ currentDirEntries.AppendObject(currentFile);
+ }
+
+ // add the folders
+ int32_t count = currentDirEntries.Count();
+ for (int32_t i = 0; i < count; ++i)
+ {
+ nsCOMPtr<nsIFile> currentFile(currentDirEntries[i]);
+
+ nsAutoString leafName;
+ currentFile->GetLeafName(leafName);
+ directoryEnumerator->HasMoreElements(&hasMore);
+ // here we should handle the case where the current file is a .sbd directory
+ // w/o a matching folder file, or a directory w/o the name .sbd
+ if (nsShouldIgnoreFile(leafName))
+ continue;
+
+ nsCOMPtr<nsIMsgFolder> child;
+ rv = parent->AddSubfolder(leafName, getter_AddRefs(child));
+ if (child)
+ {
+ nsString folderName;
+ child->GetName(folderName); // try to get it from cache/db
+ if (folderName.IsEmpty())
+ child->SetPrettyName(leafName);
+ if (deep)
+ {
+ nsCOMPtr<nsIFile> path;
+ rv = child->GetFilePath(getter_AddRefs(path));
+ AddSubFolders(child, path, true);
+ }
+ }
+ }
+ return rv == NS_MSG_FOLDER_EXISTS ? NS_OK : rv;
+}
+
+/* Finds the directory associated with this folder. That is if the path is
+ c:\Inbox, it will return c:\Inbox.sbd if it succeeds. If that path doesn't
+ currently exist then it will create it. Path is strictly an out parameter.
+ */
+nsresult nsMsgBrkMBoxStore::CreateDirectoryForFolder(nsIFile *path)
+{
+ nsresult rv = NS_OK;
+
+ bool pathIsDirectory = false;
+ path->IsDirectory(&pathIsDirectory);
+ if (!pathIsDirectory)
+ {
+ // If the current path isn't a directory, add directory separator
+ // and test it out.
+ nsAutoString leafName;
+ path->GetLeafName(leafName);
+ leafName.AppendLiteral(FOLDER_SUFFIX);
+ rv = path->SetLeafName(leafName);
+ if (NS_FAILED(rv))
+ return rv;
+
+ //If that doesn't exist, then we have to create this directory
+ pathIsDirectory = false;
+ path->IsDirectory(&pathIsDirectory);
+ if (!pathIsDirectory)
+ {
+ bool pathExists;
+ path->Exists(&pathExists);
+ //If for some reason there's a file with the directory separator
+ //then we are going to fail.
+ rv = pathExists ? NS_MSG_COULD_NOT_CREATE_DIRECTORY :
+ path->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ }
+ }
+ return rv;
+}
+