diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mailnews/import/src/nsImportMail.cpp | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/mailnews/import/src/nsImportMail.cpp')
-rw-r--r-- | comm/mailnews/import/src/nsImportMail.cpp | 1007 |
1 files changed, 1007 insertions, 0 deletions
diff --git a/comm/mailnews/import/src/nsImportMail.cpp b/comm/mailnews/import/src/nsImportMail.cpp new file mode 100644 index 0000000000..e845f773f8 --- /dev/null +++ b/comm/mailnews/import/src/nsImportMail.cpp @@ -0,0 +1,1007 @@ +/* -*- 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 "nsImportMail.h" + +#include "nsXPCOM.h" +#include "nsISupportsPrimitives.h" +#include "nsIImportMailboxDescriptor.h" +#include "nsIMsgAccountManager.h" +#include "nsImportStringBundle.h" +#include "nsTextFormatter.h" +#include "ImportDebug.h" +#include "plstr.h" +#include "nsThreadUtils.h" +#include "mozilla/Components.h" +#include "msgCore.h" + +// forward decl for proxy methods +nsresult ProxyGetSubFolders(nsIMsgFolder* aFolder); +nsresult ProxyGetChildNamed(nsIMsgFolder* aFolder, const nsAString& aName, + nsIMsgFolder** aChild); +nsresult ProxyGetParent(nsIMsgFolder* aFolder, nsIMsgFolder** aParent); +nsresult ProxyContainsChildNamed(nsIMsgFolder* aFolder, const nsAString& aName, + bool* aResult); +nsresult ProxyGenerateUniqueSubfolderName(nsIMsgFolder* aFolder, + const nsAString& aPrefix, + nsIMsgFolder* aOtherFolder, + nsAString& aName); +nsresult ProxyCreateSubfolder(nsIMsgFolder* aFolder, const nsAString& aName); +nsresult ProxyForceDBClosed(nsIMsgFolder* aFolder); + +nsresult NS_NewGenericMail(nsIImportGeneric** aImportGeneric) { + NS_ASSERTION(aImportGeneric != nullptr, "null ptr"); + if (!aImportGeneric) return NS_ERROR_NULL_POINTER; + + RefPtr<nsImportGenericMail> pGen = new nsImportGenericMail(); + return pGen->QueryInterface(NS_GET_IID(nsIImportGeneric), + (void**)aImportGeneric); +} + +nsImportGenericMail::nsImportGenericMail() { + m_found = false; + m_userVerify = false; + m_gotLocation = false; + m_gotDefaultMailboxes = false; + m_totalSize = 0; + m_doImport = false; + m_pThreadData = nullptr; + + m_pDestFolder = nullptr; + m_deleteDestFolder = false; + m_createdFolder = false; + m_performingMigration = false; + + nsresult rv = nsImportStringBundle::GetStringBundle( + IMPORT_MSGS_URL, getter_AddRefs(m_stringBundle)); + if (NS_FAILED(rv)) + IMPORT_LOG0("Failed to get string bundle for Importing Mail"); +} + +nsImportGenericMail::~nsImportGenericMail() { + if (m_pThreadData) { + m_pThreadData->DriverAbort(); + m_pThreadData = nullptr; + } +} + +NS_IMPL_ISUPPORTS(nsImportGenericMail, nsIImportGeneric) + +NS_IMETHODIMP nsImportGenericMail::GetData(const char* dataId, + nsISupports** _retval) { + nsresult rv = NS_OK; + NS_ENSURE_ARG_POINTER(_retval); + + *_retval = nullptr; + if (!PL_strcasecmp(dataId, "mailInterface")) { + NS_IF_ADDREF(*_retval = m_pInterface); + } + + if (!PL_strcasecmp(dataId, "mailLocation")) { + if (!m_pSrcLocation) GetDefaultLocation(); + NS_IF_ADDREF(*_retval = m_pSrcLocation); + } + + if (!PL_strcasecmp(dataId, "mailDestination")) { + if (!m_pDestFolder) GetDefaultDestination(); + NS_IF_ADDREF(*_retval = m_pDestFolder); + } + + if (!PL_strcasecmp(dataId, "migration")) { + nsCOMPtr<nsISupportsPRBool> migrationString = + do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + migrationString->SetData(m_performingMigration); + migrationString.forget(_retval); + } + + if (!PL_strcasecmp(dataId, "currentMailbox")) { + // create an nsISupportsString, get the current mailbox + // name being imported and put it in the string + nsCOMPtr<nsISupportsString> data = + do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); + if (NS_FAILED(rv)) return rv; + if (m_pThreadData) { + GetMailboxName(m_pThreadData->currentMailbox, data); + } + data.forget(_retval); + } + + return rv; +} + +NS_IMETHODIMP nsImportGenericMail::SetData(const char* dataId, + nsISupports* item) { + nsresult rv = NS_OK; + NS_ASSERTION(dataId != nullptr, "null ptr"); + if (!dataId) return NS_ERROR_NULL_POINTER; + + if (!PL_strcasecmp(dataId, "mailInterface")) { + m_pInterface = nullptr; + if (item) m_pInterface = do_QueryInterface(item); + } + + if (!PL_strcasecmp(dataId, "mailLocation")) { + m_mailboxes.Clear(); + m_gotDefaultMailboxes = false; + m_pSrcLocation = nullptr; + if (item) { + nsresult rv; + nsCOMPtr<nsIFile> location = do_QueryInterface(item, &rv); + NS_ENSURE_SUCCESS(rv, rv); + m_pSrcLocation = location; + } + } + + if (!PL_strcasecmp(dataId, "mailDestination")) { + m_pDestFolder = nullptr; + if (item) m_pDestFolder = do_QueryInterface(item); + m_deleteDestFolder = false; + } + + if (!PL_strcasecmp(dataId, "name")) { + if (item) { + nsCOMPtr<nsISupportsString> nameString = do_QueryInterface(item, &rv); + if (NS_SUCCEEDED(rv)) rv = nameString->GetData(m_pName); + } + } + + if (!PL_strcasecmp(dataId, "migration")) { + if (item) { + nsCOMPtr<nsISupportsPRBool> migrationString = + do_QueryInterface(item, &rv); + if (NS_SUCCEEDED(rv)) + rv = migrationString->GetData(&m_performingMigration); + } + } + return rv; +} + +NS_IMETHODIMP nsImportGenericMail::GetStatus(const char* statusKind, + int32_t* _retval) { + NS_ASSERTION(statusKind != nullptr, "null ptr"); + NS_ASSERTION(_retval != nullptr, "null ptr"); + if (!statusKind || !_retval) return NS_ERROR_NULL_POINTER; + + *_retval = 0; + + if (!PL_strcasecmp(statusKind, "isInstalled")) { + GetDefaultLocation(); + *_retval = (int32_t)m_found; + } + + if (!PL_strcasecmp(statusKind, "canUserSetLocation")) { + GetDefaultLocation(); + *_retval = (int32_t)m_userVerify; + } + + return NS_OK; +} + +void nsImportGenericMail::GetDefaultLocation(void) { + if (!m_pInterface) return; + + if (m_pSrcLocation && m_gotLocation) return; + + m_gotLocation = true; + + nsCOMPtr<nsIFile> pLoc; + m_pInterface->GetDefaultLocation(getter_AddRefs(pLoc), &m_found, + &m_userVerify); + if (!m_pSrcLocation) m_pSrcLocation = pLoc; +} + +void nsImportGenericMail::GetDefaultMailboxes(void) { + if (!m_pInterface || !m_pSrcLocation) return; + if (m_gotDefaultMailboxes) return; + m_pInterface->FindMailboxes(m_pSrcLocation, m_mailboxes); + m_gotDefaultMailboxes = true; +} + +void nsImportGenericMail::GetDefaultDestination(void) { + if (m_pDestFolder) return; + if (!m_pInterface) return; + + nsIMsgFolder* rootFolder; + m_deleteDestFolder = false; + m_createdFolder = false; + if (CreateFolder(&rootFolder)) { + m_pDestFolder = rootFolder; + m_deleteDestFolder = true; + m_createdFolder = true; + return; + } + IMPORT_LOG0( + "*** GetDefaultDestination: Failed to create a default import " + "destination folder."); +} + +NS_IMETHODIMP nsImportGenericMail::WantsProgress(bool* _retval) { + NS_ASSERTION(_retval != nullptr, "null ptr"); + NS_ENSURE_ARG_POINTER(_retval); + + if (m_pThreadData) { + m_pThreadData->DriverAbort(); + m_pThreadData = nullptr; + } + + GetDefaultLocation(); + GetDefaultMailboxes(); + + if (!m_pDestFolder) { + GetDefaultDestination(); + } + + bool result = false; + uint32_t totalSize = 0; + for (nsIImportMailboxDescriptor* box : m_mailboxes) { + bool doImport = false; + uint32_t size = 0; + nsresult rv = box->GetImport(&doImport); + if (NS_SUCCEEDED(rv) && doImport) { + (void)box->GetSize(&size); + result = true; + } + totalSize += size; + } + m_totalSize = totalSize; + m_doImport = result; + *_retval = result; + return NS_OK; +} + +void nsImportGenericMail::GetMailboxName(uint32_t index, + nsISupportsString* pStr) { + if (index >= m_mailboxes.Length()) { + return; + } + nsAutoString name; + m_mailboxes[index]->GetDisplayName(getter_Copies(name)); + if (!name.IsEmpty()) { + pStr->SetData(name); + } +} + +NS_IMETHODIMP nsImportGenericMail::BeginImport(nsISupportsString* successLog, + nsISupportsString* errorLog, + bool* _retval) { + NS_ASSERTION(_retval != nullptr, "null ptr"); + if (!_retval) return NS_ERROR_NULL_POINTER; + + nsString success; + nsString error; + + if (!m_doImport) { + nsImportStringBundle::GetStringByID(IMPORT_NO_MAILBOXES, m_stringBundle, + success); + SetLogs(success, error, successLog, errorLog); + *_retval = true; + return NS_OK; + } + + if (!m_pInterface || !m_gotDefaultMailboxes) { + IMPORT_LOG0( + "*** BeginImport: Either the interface or source mailbox is not set " + "properly."); + nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOTINITIALIZED, + m_stringBundle, error); + SetLogs(success, error, successLog, errorLog); + *_retval = false; + return NS_OK; + } + + if (!m_pDestFolder) { + IMPORT_LOG0( + "*** BeginImport: The destination mailbox is not set properly."); + nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NODESTFOLDER, + m_stringBundle, error); + SetLogs(success, error, successLog, errorLog); + *_retval = false; + return NS_OK; + } + + if (m_pThreadData) { + m_pThreadData->DriverAbort(); + m_pThreadData = nullptr; + } + + m_pSuccessLog = successLog; + m_pErrorLog = errorLog; + + // kick off the thread to do the import!!!! + m_pThreadData = new ImportThreadData(); + m_pThreadData->boxes = m_mailboxes.Clone(); + m_pThreadData->mailImport = m_pInterface; + m_pThreadData->errorLog = m_pErrorLog; + m_pThreadData->successLog = m_pSuccessLog; + + m_pThreadData->ownsDestRoot = m_deleteDestFolder; + m_pThreadData->destRoot = m_pDestFolder; + m_pThreadData->performingMigration = m_performingMigration; + + m_pThreadData->stringBundle = m_stringBundle; + + // Previously this was run in a sub-thread, after introducing + // SeamonkeyImport.jsm and because JS XPCOM can only run in the main thread, + // this has been changed to run in the main thread. + ImportMailThread(m_pThreadData); + *_retval = true; + return NS_OK; +} + +NS_IMETHODIMP nsImportGenericMail::ContinueImport(bool* _retval) { + NS_ASSERTION(_retval != nullptr, "null ptr"); + if (!_retval) return NS_ERROR_NULL_POINTER; + + *_retval = true; + if (m_pThreadData) { + if (m_pThreadData->fatalError) *_retval = false; + } + + return NS_OK; +} + +NS_IMETHODIMP nsImportGenericMail::GetProgress(int32_t* _retval) { + // This returns the progress from the the currently + // running import mail or import address book thread. + NS_ASSERTION(_retval != nullptr, "null ptr"); + if (!_retval) return NS_ERROR_NULL_POINTER; + + if (!m_pThreadData || !(m_pThreadData->threadAlive)) { + *_retval = 100; + return NS_OK; + } + + uint32_t sz = 0; + if (m_pThreadData->currentSize && m_pInterface) { + if (NS_FAILED(m_pInterface->GetImportProgress(&sz))) sz = 0; + } + + // *_retval = (int32_t) (((uint32_t)(m_pThreadData->currentTotal + sz) * + // (uint32_t)100) / m_totalSize); + + if (m_totalSize) { + double perc; + perc = (double)m_pThreadData->currentTotal; + perc += sz; + perc *= 100; + perc /= m_totalSize; + *_retval = (int32_t)perc; + if (*_retval > 100) *_retval = 100; + } else + *_retval = 0; + + // never return 100% while the thread is still alive + if (*_retval > 99) *_retval = 99; + + return NS_OK; +} + +void nsImportGenericMail::ReportError(int32_t id, const char16_t* pName, + nsString* pStream, + nsIStringBundle* aBundle) { + if (!pStream) return; + + // load the error string + char16_t* pFmt = nsImportStringBundle::GetStringByID(id, aBundle); + nsString pText; + nsTextFormatter::ssprintf(pText, pFmt, pName); + pStream->Append(pText); + free(pFmt); + pStream->Append(NS_ConvertASCIItoUTF16(MSG_LINEBREAK)); +} + +void nsImportGenericMail::SetLogs(nsString& success, nsString& error, + nsISupportsString* pSuccess, + nsISupportsString* pError) { + nsAutoString str; + if (pSuccess) { + pSuccess->GetData(str); + str.Append(success); + pSuccess->SetData(str); + } + if (pError) { + pError->GetData(str); + str.Append(error); + pError->SetData(str); + } +} + +NS_IMETHODIMP nsImportGenericMail::CancelImport(void) { + if (m_pThreadData) { + m_pThreadData->abort = true; + m_pThreadData->DriverAbort(); + m_pThreadData = nullptr; + } + + return NS_OK; +} + +ImportThreadData::ImportThreadData() { + fatalError = false; + driverAlive = true; + threadAlive = true; + abort = false; + currentTotal = 0; + currentSize = 0; + destRoot = nullptr; + ownsDestRoot = false; +} + +ImportThreadData::~ImportThreadData() {} + +void ImportThreadData::DriverDelete(void) { + driverAlive = false; + if (!driverAlive && !threadAlive) delete this; +} + +void ImportThreadData::ThreadDelete() { + threadAlive = false; + if (!driverAlive && !threadAlive) delete this; +} + +void ImportThreadData::DriverAbort() { + if (abort && !threadAlive && destRoot) { + if (ownsDestRoot) { + destRoot->RecursiveDelete(true); + } else { + // FIXME: just delete the stuff we created? + } + } else + abort = true; + DriverDelete(); +} + +static void ImportMailThread(void* stuff) { + ImportThreadData* pData = (ImportThreadData*)stuff; + + IMPORT_LOG0("ImportMailThread: Starting..."); + + nsresult rv = NS_OK; + + nsCOMPtr<nsIMsgFolder> destRoot(pData->destRoot); + + uint32_t count = pData->boxes.Length(); + + uint32_t size; + uint32_t depth = 1; + uint32_t newDepth; + nsString lastName; + + nsCOMPtr<nsIMsgFolder> curFolder(destRoot); + + nsCOMPtr<nsIMsgFolder> newFolder; + nsCOMPtr<nsIMsgFolder> subFolder; + + bool exists; + + nsString success; + nsString error; + + // GetSubFolders() will initialize folders if they are not already + // initialized. + ProxyGetSubFolders(curFolder); + + IMPORT_LOG1("ImportMailThread: Total number of folders to import = %d.", + count); + + // Note that the front-end js script only displays one import result string so + // we combine both good and bad import status into one string (in var + // 'success'). + + for (uint32_t i = 0; (i < count) && !(pData->abort); i++) { + nsIImportMailboxDescriptor* box = pData->boxes[i]; + pData->currentMailbox = i; + + bool doImport = false; + size = 0; + rv = box->GetImport(&doImport); + if (doImport) rv = box->GetSize(&size); + rv = box->GetDepth(&newDepth); + if (newDepth > depth) { + // OK, we are going to add a subfolder under the last/previous folder we + // processed, so find this folder (stored in 'lastName') who is going to + // be the new parent folder. + IMPORT_LOG1("ImportMailThread: Processing child folder '%s'.", + NS_ConvertUTF16toUTF8(lastName).get()); + rv = ProxyGetChildNamed(curFolder, lastName, getter_AddRefs(subFolder)); + if (NS_FAILED(rv)) { + IMPORT_LOG1( + "*** ImportMailThread: Failed to get the interface for child " + "folder '%s'.", + NS_ConvertUTF16toUTF8(lastName).get()); + nsImportGenericMail::ReportError(IMPORT_ERROR_MB_FINDCHILD, + lastName.get(), &error, + pData->stringBundle); + pData->fatalError = true; + break; + } + curFolder = subFolder; + // Make sure this new parent folder obj has the correct subfolder list + // so far. + rv = ProxyGetSubFolders(curFolder); + } else if (newDepth < depth) { + rv = NS_OK; + while ((newDepth < depth) && NS_SUCCEEDED(rv)) { + rv = curFolder->GetParent(getter_AddRefs(curFolder)); + if (NS_FAILED(rv)) { + IMPORT_LOG1( + "*** ImportMailThread: Failed to get the interface for parent " + "folder '%s'.", + NS_ConvertUTF16toUTF8(lastName).get()); + nsImportGenericMail::ReportError(IMPORT_ERROR_MB_FINDCHILD, + lastName.get(), &error, + pData->stringBundle); + pData->fatalError = true; + break; + } + depth--; + } + if (NS_FAILED(rv)) { + IMPORT_LOG1( + "*** ImportMailThread: Failed to get the proxy interface for " + "parent folder '%s'.", + NS_ConvertUTF16toUTF8(lastName).get()); + nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOPROXY, + pData->stringBundle, error); + pData->fatalError = true; + break; + } + } + depth = newDepth; + char16_t* pName = nullptr; + box->GetDisplayName(&pName); + if (pName) { + lastName = pName; + free(pName); + } else + lastName.AssignLiteral("Unknown!"); + + // translate the folder name if we are doing migration, but + // only for special folders which are at the root level + if (pData->performingMigration && depth == 1) + pData->mailImport->TranslateFolderName(lastName, lastName); + + exists = false; + rv = ProxyContainsChildNamed(curFolder, lastName, &exists); + + // If we are performing profile migration (as opposed to importing) then + // we are starting with empty local folders. In that case, always choose + // to over-write the existing local folder with this name. Don't create a + // unique subfolder name. Otherwise you end up with "Inbox, Inbox0" or + // "Unsent Folders, UnsentFolders0" + if (exists && !pData->performingMigration) { + nsString subName; + ProxyGenerateUniqueSubfolderName(curFolder, lastName, nullptr, subName); + if (!subName.IsEmpty()) lastName.Assign(subName); + } + + IMPORT_LOG1("ImportMailThread: Creating new import folder '%s'.", + NS_ConvertUTF16toUTF8(lastName).get()); + ProxyCreateSubfolder( + curFolder, + lastName); // this may fail if the folder already exists..that's ok + + rv = ProxyGetChildNamed(curFolder, lastName, getter_AddRefs(newFolder)); + if (NS_FAILED(rv)) { + IMPORT_LOG1( + "*** ImportMailThread: Failed to locate subfolder '%s' after it's " + "been created.", + NS_ConvertUTF16toUTF8(lastName).get()); + nsImportGenericMail::ReportError(IMPORT_ERROR_MB_CREATE, lastName.get(), + &error, pData->stringBundle); + } + + if (size && doImport && newFolder && NS_SUCCEEDED(rv)) { + bool fatalError = false; + pData->currentSize = size; + char16_t* pSuccess = nullptr; + char16_t* pError = nullptr; + rv = pData->mailImport->ImportMailbox(box, newFolder, &pError, &pSuccess, + &fatalError); + if (pError) { + error.Append(pError); + free(pError); + } + if (pSuccess) { + success.Append(pSuccess); + free(pSuccess); + } + + pData->currentSize = 0; + pData->currentTotal += size; + + // commit to the db synchronously, but using a proxy since it doesn't + // like being used elsewhere than from the main thread. OK, we've copied + // the actual folder/file over if the folder size is not 0 (ie, the msg + // summary is no longer valid) so close the msg database so that when + // the folder is reopened the folder db can be reconstructed (which + // validates msg summary and forces folder to be reparsed). + rv = ProxyForceDBClosed(newFolder); + fatalError = NS_FAILED(rv); + + if (fatalError) { + IMPORT_LOG1( + "*** ImportMailThread: ImportMailbox returned fatalError, " + "mailbox #%d\n", + (int)i); + pData->fatalError = true; + break; + } + } + } + + // Now save the new acct info to pref file. + nsCOMPtr<nsIMsgAccountManager> accMgr = + do_GetService("@mozilla.org/messenger/account-manager;1", &rv); + if (NS_SUCCEEDED(rv) && accMgr) { + rv = accMgr->SaveAccountInfo(); + NS_ASSERTION(NS_SUCCEEDED(rv), "Can't save account info to pref file"); + } + + nsImportGenericMail::SetLogs(success, error, pData->successLog, + pData->errorLog); + + if (pData->abort || pData->fatalError) { + IMPORT_LOG0("*** ImportMailThread: Abort or fatalError flag was set\n"); + if (pData->ownsDestRoot) { + IMPORT_LOG0("Calling destRoot->RecursiveDelete\n"); + destRoot->RecursiveDelete(true); + } else { + // FIXME: just delete the stuff we created? + } + } + + IMPORT_LOG1("Import mailbox thread done: %d\n", (int)pData->currentTotal); + + pData->ThreadDelete(); +} + +// Creates a folder in Local Folders with the module name + mail +// for e.g: Outlook Mail +bool nsImportGenericMail::CreateFolder(nsIMsgFolder** ppFolder) { + nsresult rv; + *ppFolder = nullptr; + + nsCOMPtr<nsIStringBundle> bundle; + nsCOMPtr<nsIStringBundleService> bundleService = + mozilla::components::StringBundle::Service(); + if (!bundleService) return false; + rv = bundleService->CreateBundle(IMPORT_MSGS_URL, getter_AddRefs(bundle)); + if (NS_FAILED(rv)) return false; + nsString folderName; + if (!m_pName.IsEmpty()) { + AutoTArray<nsString, 1> moduleName = {m_pName}; + rv = bundle->FormatStringFromName("ImportModuleFolderName", moduleName, + folderName); + } else { + rv = bundle->GetStringFromName("DefaultFolderName", folderName); + } + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Failed to get Folder Name!\n"); + return false; + } + nsCOMPtr<nsIMsgAccountManager> accMgr = + do_GetService("@mozilla.org/messenger/account-manager;1", &rv); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Failed to create account manager!\n"); + return false; + } + + nsCOMPtr<nsIMsgIncomingServer> server; + rv = accMgr->GetLocalFoldersServer(getter_AddRefs(server)); + // if Local Folders does not exist already, create it + if (NS_FAILED(rv) || !server) { + rv = accMgr->CreateLocalMailAccount(); + if (NS_FAILED(rv)) { + IMPORT_LOG0("*** Failed to create Local Folders!\n"); + return false; + } + + rv = accMgr->GetLocalFoldersServer(getter_AddRefs(server)); + } + + if (NS_SUCCEEDED(rv) && server) { + nsCOMPtr<nsIMsgFolder> localRootFolder; + rv = server->GetRootMsgFolder(getter_AddRefs(localRootFolder)); + if (localRootFolder) { + // we need to call GetSubFolders() so that the folders get initialized + // if they are not initialized yet. + nsTArray<RefPtr<nsIMsgFolder>> dummy; + rv = localRootFolder->GetSubFolders(dummy); + if (NS_SUCCEEDED(rv)) { + // check if the folder name we picked already exists. + bool exists = false; + rv = localRootFolder->ContainsChildNamed(folderName, &exists); + if (exists) { + nsString name; + localRootFolder->GenerateUniqueSubfolderName(folderName, nullptr, + name); + if (!name.IsEmpty()) + folderName.Assign(name); + else { + IMPORT_LOG0("*** Failed to find a unique folder name!\n"); + return false; + } + } + IMPORT_LOG1("Creating folder for importing mail: '%s'\n", + NS_ConvertUTF16toUTF8(folderName).get()); + + // Bug 564162 identifies a dataloss design flaw. + // A working Thunderbird client can have mail in Local Folders and a + // subsequent import 'Everything' will trigger a migration which + // overwrites existing mailboxes with the imported mailboxes. + rv = localRootFolder->CreateSubfolder(folderName, nullptr); + if (NS_SUCCEEDED(rv)) { + rv = localRootFolder->GetChildNamed(folderName, ppFolder); + if (*ppFolder) { + IMPORT_LOG1("Folder '%s' created successfully\n", + NS_ConvertUTF16toUTF8(folderName).get()); + return true; + } + } + } + } // if localRootFolder + } // if server + IMPORT_LOG0("****** FAILED TO CREATE FOLDER FOR IMPORT\n"); + return false; +} + +/** + * These are the proxy objects we use to proxy nsIMsgFolder methods back + * the the main thread. Since there are only five, we can hand roll them. + * A better design might be a co-routine-ish design where the ui thread + * hands off each folder to the import thread and when the thread finishes + * the folder, the main thread hands it the next folder. + */ + +class GetSubFoldersRunnable : public mozilla::Runnable { + public: + explicit GetSubFoldersRunnable(nsIMsgFolder* aFolder); + NS_DECL_NSIRUNNABLE + nsresult mResult; + + private: + nsCOMPtr<nsIMsgFolder> m_folder; +}; + +GetSubFoldersRunnable::GetSubFoldersRunnable(nsIMsgFolder* aFolder) + : mozilla::Runnable("GetSubFoldersRunnable"), m_folder(aFolder) {} + +NS_IMETHODIMP GetSubFoldersRunnable::Run() { + nsTArray<RefPtr<nsIMsgFolder>> dummy; + mResult = m_folder->GetSubFolders(dummy); + return NS_OK; // Sync runnable must return OK. +} + +nsresult ProxyGetSubFolders(nsIMsgFolder* aFolder) { + RefPtr<GetSubFoldersRunnable> getSubFolders = + new GetSubFoldersRunnable(aFolder); + nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete( + "ProxyGetSubFolders"_ns, mozilla::GetMainThreadSerialEventTarget(), + do_AddRef(getSubFolders)); + NS_ENSURE_SUCCESS(rv, rv); + return getSubFolders->mResult; +} + +class GetChildNamedRunnable : public mozilla::Runnable { + public: + GetChildNamedRunnable(nsIMsgFolder* aFolder, const nsAString& aName, + nsIMsgFolder** aChild); + NS_DECL_NSIRUNNABLE + nsresult mResult; + + protected: + nsCOMPtr<nsIMsgFolder> m_folder; + nsString m_name; + nsIMsgFolder** m_child; +}; + +GetChildNamedRunnable::GetChildNamedRunnable(nsIMsgFolder* aFolder, + const nsAString& aName, + nsIMsgFolder** aChild) + : mozilla::Runnable("GetChildNamedRunnable"), + mResult(NS_OK), + m_folder(aFolder), + m_name(aName), + m_child(aChild) {} + +NS_IMETHODIMP GetChildNamedRunnable::Run() { + mResult = m_folder->GetChildNamed(m_name, m_child); + return NS_OK; // Sync runnable must return OK. +} + +nsresult ProxyGetChildNamed(nsIMsgFolder* aFolder, const nsAString& aName, + nsIMsgFolder** aChild) { + RefPtr<GetChildNamedRunnable> getChildNamed = + new GetChildNamedRunnable(aFolder, aName, aChild); + nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete( + "ProxyGetChildNamed"_ns, mozilla::GetMainThreadSerialEventTarget(), + do_AddRef(getChildNamed)); + NS_ENSURE_SUCCESS(rv, rv); + return getChildNamed->mResult; +} + +class GetParentRunnable : public mozilla::Runnable { + public: + GetParentRunnable(nsIMsgFolder* aFolder, nsIMsgFolder** aParent); + NS_DECL_NSIRUNNABLE + nsresult mResult; + + protected: + nsCOMPtr<nsIMsgFolder> m_folder; + nsIMsgFolder** m_parent; +}; + +GetParentRunnable::GetParentRunnable(nsIMsgFolder* aFolder, + nsIMsgFolder** aParent) + : mozilla::Runnable("GetParentRunnable"), + mResult(NS_OK), + m_folder(aFolder), + m_parent(aParent) {} + +NS_IMETHODIMP GetParentRunnable::Run() { + mResult = m_folder->GetParent(m_parent); + return NS_OK; // Sync runnable must return OK. +} + +nsresult ProxyGetParent(nsIMsgFolder* aFolder, nsIMsgFolder** aParent) { + RefPtr<GetParentRunnable> getParent = new GetParentRunnable(aFolder, aParent); + nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete( + "ProxyGetParent"_ns, mozilla::GetMainThreadSerialEventTarget(), + do_AddRef(getParent)); + NS_ENSURE_SUCCESS(rv, rv); + return getParent->mResult; +} + +class ContainsChildNamedRunnable : public mozilla::Runnable { + public: + ContainsChildNamedRunnable(nsIMsgFolder* aFolder, const nsAString& aName, + bool* aResult); + NS_DECL_NSIRUNNABLE + nsresult mResult; + + protected: + nsCOMPtr<nsIMsgFolder> m_folder; + nsString m_name; + bool* m_result; +}; + +ContainsChildNamedRunnable::ContainsChildNamedRunnable(nsIMsgFolder* aFolder, + const nsAString& aName, + bool* aResult) + : mozilla::Runnable("ContainsChildNamedRunnable"), + mResult(NS_OK), + m_folder(aFolder), + m_name(aName), + m_result(aResult) {} + +NS_IMETHODIMP ContainsChildNamedRunnable::Run() { + mResult = m_folder->ContainsChildNamed(m_name, m_result); + return NS_OK; // Sync runnable must return OK. +} + +nsresult ProxyContainsChildNamed(nsIMsgFolder* aFolder, const nsAString& aName, + bool* aResult) { + NS_ENSURE_ARG(aFolder); + RefPtr<ContainsChildNamedRunnable> containsChildNamed = + new ContainsChildNamedRunnable(aFolder, aName, aResult); + nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete( + "ProxyContainsChildNamed"_ns, mozilla::GetMainThreadSerialEventTarget(), + do_AddRef(containsChildNamed)); + NS_ENSURE_SUCCESS(rv, rv); + return containsChildNamed->mResult; +} + +class GenerateUniqueSubfolderNameRunnable : public mozilla::Runnable { + public: + GenerateUniqueSubfolderNameRunnable(nsIMsgFolder* aFolder, + const nsAString& prefix, + nsIMsgFolder* otherFolder, + nsAString& name); + NS_DECL_NSIRUNNABLE + nsresult mResult; + + protected: + nsCOMPtr<nsIMsgFolder> m_folder; + nsString m_prefix; + nsCOMPtr<nsIMsgFolder> m_otherFolder; + nsString m_name; +}; + +GenerateUniqueSubfolderNameRunnable::GenerateUniqueSubfolderNameRunnable( + nsIMsgFolder* aFolder, const nsAString& aPrefix, nsIMsgFolder* aOtherFolder, + nsAString& aName) + : mozilla::Runnable("GenerateUniqueSubfolderNameRunnable"), + mResult(NS_OK), + m_folder(aFolder), + m_prefix(aPrefix), + m_otherFolder(aOtherFolder), + m_name(aName) {} + +NS_IMETHODIMP GenerateUniqueSubfolderNameRunnable::Run() { + mResult = + m_folder->GenerateUniqueSubfolderName(m_prefix, m_otherFolder, m_name); + return NS_OK; // Sync runnable must return OK. +} + +nsresult ProxyGenerateUniqueSubfolderName(nsIMsgFolder* aFolder, + const nsAString& aPrefix, + nsIMsgFolder* aOtherFolder, + nsAString& aName) + +{ + RefPtr<GenerateUniqueSubfolderNameRunnable> generateUniqueSubfolderName = + new GenerateUniqueSubfolderNameRunnable(aFolder, aPrefix, aOtherFolder, + aName); + nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete( + "ProxyGenerateUniqueSubfolderName"_ns, + mozilla::GetMainThreadSerialEventTarget(), + do_AddRef(generateUniqueSubfolderName)); + NS_ENSURE_SUCCESS(rv, rv); + return generateUniqueSubfolderName->mResult; +} + +class CreateSubfolderRunnable : public mozilla::Runnable { + public: + CreateSubfolderRunnable(nsIMsgFolder* aFolder, const nsAString& aName); + NS_DECL_NSIRUNNABLE + nsresult mResult; + + protected: + nsCOMPtr<nsIMsgFolder> m_folder; + nsString m_name; +}; + +CreateSubfolderRunnable::CreateSubfolderRunnable(nsIMsgFolder* aFolder, + const nsAString& aName) + : mozilla::Runnable("CreateSubfolderRunnable"), + mResult(NS_OK), + m_folder(aFolder), + m_name(aName) {} + +NS_IMETHODIMP CreateSubfolderRunnable::Run() { + mResult = m_folder->CreateSubfolder(m_name, nullptr); + return NS_OK; // Sync runnable must return OK. +} + +nsresult ProxyCreateSubfolder(nsIMsgFolder* aFolder, const nsAString& aName) { + NS_ENSURE_ARG_POINTER(aFolder); + RefPtr<CreateSubfolderRunnable> createSubfolder = + new CreateSubfolderRunnable(aFolder, aName); + nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete( + "ProxyCreateSubfolder"_ns, mozilla::GetMainThreadSerialEventTarget(), + do_AddRef(createSubfolder)); + NS_ENSURE_SUCCESS(rv, rv); + return createSubfolder->mResult; +} + +class ForceDBClosedRunnable : public mozilla::Runnable { + public: + explicit ForceDBClosedRunnable(nsIMsgFolder* aFolder); + NS_DECL_NSIRUNNABLE + nsresult mResult; + + protected: + nsCOMPtr<nsIMsgFolder> m_folder; +}; + +ForceDBClosedRunnable::ForceDBClosedRunnable(nsIMsgFolder* aFolder) + : mozilla::Runnable("ForceDBClosedRunnable"), m_folder(aFolder) {} + +NS_IMETHODIMP ForceDBClosedRunnable::Run() { + mResult = m_folder->ForceDBClosed(); + return NS_OK; // Sync runnable must return OK. +} + +nsresult ProxyForceDBClosed(nsIMsgFolder* aFolder) { + RefPtr<ForceDBClosedRunnable> forceDBClosed = + new ForceDBClosedRunnable(aFolder); + nsresult rv = NS_DispatchAndSpinEventLoopUntilComplete( + "ProxyForceDBClosed"_ns, mozilla::GetMainThreadSerialEventTarget(), + do_AddRef(forceDBClosed)); + NS_ENSURE_SUCCESS(rv, rv); + return forceDBClosed->mResult; +} |