summaryrefslogtreecommitdiff
path: root/mailnews/import/src/nsImportMail.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/import/src/nsImportMail.cpp')
-rw-r--r--mailnews/import/src/nsImportMail.cpp1208
1 files changed, 1208 insertions, 0 deletions
diff --git a/mailnews/import/src/nsImportMail.cpp b/mailnews/import/src/nsImportMail.cpp
new file mode 100644
index 0000000000..ad584b8a61
--- /dev/null
+++ b/mailnews/import/src/nsImportMail.cpp
@@ -0,0 +1,1208 @@
+/* -*- 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 "prthread.h"
+#include "prprf.h"
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsIArray.h"
+#include "nsArrayUtils.h"
+
+#include "nsIImportMail.h"
+#include "nsIImportGeneric.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIImportMailboxDescriptor.h"
+
+#include "nsStringGlue.h"
+#include "nsUnicharUtils.h"
+
+#include "nsMsgUtils.h"
+#include "nsIMsgAccountManager.h"
+#include "nsMsgBaseCID.h"
+#include "nsIMsgFolder.h"
+#include "nsImportStringBundle.h"
+#include "nsIStringBundle.h"
+#include "nsTextFormatter.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIImportService.h"
+#include "ImportDebug.h"
+#include "plstr.h"
+#include "MailNewsTypes.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Services.h"
+
+#define IMPORT_MSGS_URL "chrome://messenger/locale/importMsgs.properties"
+
+////////////////////////////////////////////////////////////////////////
+
+static void ImportMailThread(void *stuff);
+
+class ImportThreadData;
+
+class nsImportGenericMail : public nsIImportGeneric
+{
+public:
+
+ nsImportGenericMail();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ /* nsISupports GetData (in string dataId); */
+ NS_IMETHOD GetData(const char *dataId, nsISupports **_retval) override;
+
+ NS_IMETHOD SetData(const char *dataId, nsISupports *pData) override;
+
+ NS_IMETHOD GetStatus(const char *statusKind, int32_t *_retval) override;
+
+ NS_IMETHOD WantsProgress(bool *_retval) override;
+
+ NS_IMETHODIMP BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval) override;
+
+ NS_IMETHOD ContinueImport(bool *_retval) override;
+
+ NS_IMETHOD GetProgress(int32_t *_retval) override;
+
+ NS_IMETHOD CancelImport(void) override;
+
+private:
+ virtual ~nsImportGenericMail();
+ bool CreateFolder(nsIMsgFolder **ppFolder);
+ void GetDefaultMailboxes(void);
+ void GetDefaultLocation(void);
+ void GetDefaultDestination(void);
+ void GetMailboxName(uint32_t index, nsISupportsString *pStr);
+
+public:
+ static void SetLogs(nsString& success, nsString& error, nsISupportsString *pSuccess, nsISupportsString *pError);
+ static void ReportError(int32_t id, const char16_t *pName, nsString *pStream, nsIStringBundle* aBundle);
+
+private:
+ nsString m_pName; // module name that created this interface
+ nsIMsgFolder * m_pDestFolder;
+ bool m_deleteDestFolder;
+ bool m_createdFolder;
+ nsCOMPtr <nsIFile> m_pSrcLocation;
+ bool m_gotLocation;
+ bool m_found;
+ bool m_userVerify;
+ nsIImportMail *m_pInterface;
+ nsIArray * m_pMailboxes;
+ nsISupportsString *m_pSuccessLog;
+ nsISupportsString *m_pErrorLog;
+ uint32_t m_totalSize;
+ bool m_doImport;
+ ImportThreadData * m_pThreadData;
+ bool m_performingMigration;
+ nsCOMPtr<nsIStringBundle> m_stringBundle;
+};
+
+class ImportThreadData {
+public:
+ bool driverAlive;
+ bool threadAlive;
+ bool abort;
+ bool fatalError;
+ uint32_t currentTotal;
+ uint32_t currentSize;
+ nsIMsgFolder * destRoot;
+ bool ownsDestRoot;
+ nsIArray *boxes;
+ nsIImportMail * mailImport;
+ nsISupportsString * successLog;
+ nsISupportsString * errorLog;
+ uint32_t currentMailbox;
+ bool performingMigration;
+ nsIStringBundle *stringBundle;
+
+ ImportThreadData();
+ ~ImportThreadData();
+ void DriverDelete();
+ void ThreadDelete();
+ void DriverAbort();
+};
+
+// 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_PRECONDITION(aImportGeneric != nullptr, "null ptr");
+ if (! aImportGeneric)
+ return NS_ERROR_NULL_POINTER;
+
+ nsImportGenericMail *pGen = new nsImportGenericMail();
+
+ if (pGen == nullptr)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(pGen);
+ nsresult rv = pGen->QueryInterface(NS_GET_IID(nsIImportGeneric), (void **)aImportGeneric);
+ NS_RELEASE(pGen);
+
+ return rv;
+}
+
+nsImportGenericMail::nsImportGenericMail()
+{
+ m_found = false;
+ m_userVerify = false;
+ m_gotLocation = false;
+ m_pInterface = nullptr;
+ m_pMailboxes = nullptr;
+ m_pSuccessLog = nullptr;
+ m_pErrorLog = nullptr;
+ m_totalSize = 0;
+ m_doImport = false;
+ m_pThreadData = nullptr;
+
+ m_pDestFolder = nullptr;
+ m_deleteDestFolder = false;
+ m_createdFolder = false;
+ m_performingMigration = false;
+
+ // Init logging module.
+ if (!IMPORTLOGMODULE)
+ IMPORTLOGMODULE = PR_NewLogModule("IMPORT");
+
+ 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_IF_RELEASE(m_pDestFolder);
+ NS_IF_RELEASE(m_pInterface);
+ NS_IF_RELEASE(m_pMailboxes);
+ NS_IF_RELEASE(m_pSuccessLog);
+ NS_IF_RELEASE(m_pErrorLog);
+}
+
+
+
+NS_IMPL_ISUPPORTS(nsImportGenericMail, nsIImportGeneric)
+
+
+NS_IMETHODIMP nsImportGenericMail::GetData(const char *dataId, nsISupports **_retval)
+{
+ nsresult rv = NS_OK;
+
+ NS_PRECONDITION(_retval != nullptr, "null ptr");
+ if (!_retval)
+ return NS_ERROR_NULL_POINTER;
+
+ *_retval = nullptr;
+ if (!PL_strcasecmp(dataId, "mailInterface")) {
+ *_retval = m_pInterface;
+ NS_IF_ADDREF(m_pInterface);
+ }
+
+ if (!PL_strcasecmp(dataId, "mailBoxes")) {
+ if (!m_pMailboxes)
+ GetDefaultMailboxes();
+ *_retval = m_pMailboxes;
+ NS_IF_ADDREF(m_pMailboxes);
+ }
+
+ 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);
+ NS_IF_ADDREF(*_retval = migrationString);
+ }
+
+ 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);
+ }
+ NS_ADDREF(*_retval = data);
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsImportGenericMail::SetData(const char *dataId, nsISupports *item)
+{
+ nsresult rv = NS_OK;
+ NS_PRECONDITION(dataId != nullptr, "null ptr");
+ if (!dataId)
+ return NS_ERROR_NULL_POINTER;
+
+ if (!PL_strcasecmp(dataId, "mailInterface")) {
+ NS_IF_RELEASE(m_pInterface);
+ if (item)
+ item->QueryInterface(NS_GET_IID(nsIImportMail), (void **) &m_pInterface);
+ }
+ if (!PL_strcasecmp(dataId, "mailBoxes")) {
+ NS_IF_RELEASE(m_pMailboxes);
+ if (item)
+ item->QueryInterface(NS_GET_IID(nsIArray), (void **) &m_pMailboxes);
+ }
+
+ if (!PL_strcasecmp(dataId, "mailLocation")) {
+ NS_IF_RELEASE(m_pMailboxes);
+ 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")) {
+ NS_IF_RELEASE(m_pDestFolder);
+ if (item)
+ item->QueryInterface(NS_GET_IID(nsIMsgFolder), (void **) &m_pDestFolder);
+ m_deleteDestFolder = false;
+ }
+
+ if (!PL_strcasecmp(dataId, "name")) {
+ nsCOMPtr<nsISupportsString> nameString;
+ if (item) {
+ item->QueryInterface(NS_GET_IID(nsISupportsString), getter_AddRefs(nameString));
+ rv = nameString->GetData(m_pName);
+ }
+ }
+
+ if (!PL_strcasecmp(dataId, "migration")) {
+ nsCOMPtr<nsISupportsPRBool> migrationString;
+ if (item) {
+ item->QueryInterface(NS_GET_IID(nsISupportsPRBool), getter_AddRefs(migrationString));
+ rv = migrationString->GetData(&m_performingMigration);
+ }
+ }
+ return rv;
+}
+
+NS_IMETHODIMP nsImportGenericMail::GetStatus(const char *statusKind, int32_t *_retval)
+{
+ NS_PRECONDITION(statusKind != nullptr, "null ptr");
+ NS_PRECONDITION(_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_pMailboxes || !m_pSrcLocation)
+ return;
+
+ m_pInterface->FindMailboxes(m_pSrcLocation, &m_pMailboxes);
+}
+
+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_PRECONDITION(_retval != nullptr, "null ptr");
+ NS_ENSURE_ARG_POINTER(_retval);
+
+ if (m_pThreadData) {
+ m_pThreadData->DriverAbort();
+ m_pThreadData = nullptr;
+ }
+
+ if (!m_pMailboxes) {
+ GetDefaultLocation();
+ GetDefaultMailboxes();
+ }
+
+ if (!m_pDestFolder) {
+ GetDefaultDestination();
+ }
+
+ bool result = false;
+
+ if (m_pMailboxes) {
+ uint32_t i;
+ bool import;
+ uint32_t count = 0;
+ uint32_t size;
+ uint32_t totalSize = 0;
+
+ (void) m_pMailboxes->GetLength(&count);
+ for (i = 0; i < count; i++) {
+ nsCOMPtr<nsIImportMailboxDescriptor> box =
+ do_QueryElementAt(m_pMailboxes, i);
+ if (box) {
+ import = false;
+ size = 0;
+ nsresult rv = box->GetImport(&import);
+ if (NS_SUCCEEDED(rv) && import) {
+ (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 (m_pMailboxes) {
+ nsCOMPtr<nsIImportMailboxDescriptor> box(do_QueryElementAt(m_pMailboxes, index));
+ if (box) {
+ nsAutoString name;
+ box->GetDisplayName(getter_Copies(name));
+ if (!name.IsEmpty()) {
+ pStr->SetData(name);
+ }
+ }
+ }
+}
+
+NS_IMETHODIMP nsImportGenericMail::BeginImport(nsISupportsString *successLog, nsISupportsString *errorLog, bool *_retval)
+{
+ NS_PRECONDITION(_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_pMailboxes) {
+ 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;
+ }
+
+ NS_IF_RELEASE(m_pSuccessLog);
+ NS_IF_RELEASE(m_pErrorLog);
+ m_pSuccessLog = successLog;
+ m_pErrorLog = errorLog;
+ NS_IF_ADDREF(m_pSuccessLog);
+ NS_IF_ADDREF(m_pErrorLog);
+
+
+ // kick off the thread to do the import!!!!
+ m_pThreadData = new ImportThreadData();
+ m_pThreadData->boxes = m_pMailboxes;
+ NS_ADDREF(m_pMailboxes);
+ m_pThreadData->mailImport = m_pInterface;
+ NS_ADDREF(m_pInterface);
+ m_pThreadData->errorLog = m_pErrorLog;
+ NS_IF_ADDREF(m_pErrorLog);
+ m_pThreadData->successLog = m_pSuccessLog;
+ NS_IF_ADDREF(m_pSuccessLog);
+
+ m_pThreadData->ownsDestRoot = m_deleteDestFolder;
+ m_pThreadData->destRoot = m_pDestFolder;
+ m_pThreadData->performingMigration = m_performingMigration;
+ NS_IF_ADDREF(m_pDestFolder);
+
+ NS_IF_ADDREF(m_pThreadData->stringBundle = m_stringBundle);
+
+ PRThread *pThread = PR_CreateThread(PR_USER_THREAD, &ImportMailThread, m_pThreadData,
+ PR_PRIORITY_NORMAL,
+ PR_LOCAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ 0);
+ if (!pThread) {
+ m_pThreadData->ThreadDelete();
+ m_pThreadData->abort = true;
+ m_pThreadData->DriverAbort();
+ m_pThreadData = nullptr;
+ *_retval = false;
+ nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOTHREAD,
+ m_stringBundle, error);
+ SetLogs(success, error, successLog, errorLog);
+ }
+ else
+ *_retval = true;
+
+ return NS_OK;
+
+}
+
+
+NS_IMETHODIMP nsImportGenericMail::ContinueImport(bool *_retval)
+{
+ NS_PRECONDITION(_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_PRECONDITION(_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);
+ char16_t *pText = nsTextFormatter::smprintf(pFmt, pName);
+ pStream->Append(pText);
+ nsTextFormatter::smprintf_free(pText);
+ NS_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;
+ boxes = nullptr;
+ mailImport = nullptr;
+ successLog = nullptr;
+ errorLog = nullptr;
+ stringBundle = nullptr;
+}
+
+ImportThreadData::~ImportThreadData()
+{
+ NS_IF_RELEASE(destRoot);
+ NS_IF_RELEASE(boxes);
+ NS_IF_RELEASE(mailImport);
+ NS_IF_RELEASE(errorLog);
+ NS_IF_RELEASE(successLog);
+ NS_IF_RELEASE(stringBundle);
+}
+
+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, nullptr);
+ }
+ 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 = 0;
+ rv = pData->boxes->GetLength(&count);
+
+ uint32_t i;
+ bool import;
+ uint32_t size;
+ uint32_t depth = 1;
+ uint32_t newDepth;
+ nsString lastName;
+ char16_t * pName;
+
+ 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 (i = 0; (i < count) && !(pData->abort); i++) {
+ nsCOMPtr<nsIImportMailboxDescriptor> box =
+ do_QueryElementAt(pData->boxes, i);
+ if (box) {
+ pData->currentMailbox = i;
+
+ import = false;
+ size = 0;
+ rv = box->GetImport(&import);
+ if (import)
+ 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'.", 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'.", lastName.get());
+ nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOPROXY,
+ pData->stringBundle, error);
+ pData->fatalError = true;
+ break;
+ }
+ }
+ depth = newDepth;
+ pName = nullptr;
+ box->GetDisplayName(&pName);
+ if (pName) {
+ lastName = pName;
+ NS_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.", lastName.get());
+ nsImportGenericMail::ReportError(IMPORT_ERROR_MB_CREATE, lastName.get(),
+ &error, pData->stringBundle);
+ }
+
+ if (size && import && 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);
+ NS_Free(pError);
+ }
+ if (pSuccess) {
+ success.Append(pSuccess);
+ NS_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(NS_MSGACCOUNTMANAGER_CONTRACTID, &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, nullptr);
+ }
+ 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::services::GetStringBundleService();
+ 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()) {
+ const char16_t *moduleName[] = { m_pName.get() };
+ rv = bundle->FormatStringFromName(u"ImportModuleFolderName",
+ moduleName, 1,
+ getter_Copies(folderName));
+ }
+ else {
+ rv = bundle->GetStringFromName(u"DefaultFolderName",
+ getter_Copies(folderName));
+ }
+ if (NS_FAILED(rv)) {
+ IMPORT_LOG0("*** Failed to get Folder Name!\n");
+ return false;
+ }
+ nsCOMPtr <nsIMsgAccountManager> accMgr = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &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.
+ nsCOMPtr<nsISimpleEnumerator> aEnumerator;
+ rv = localRootFolder->GetSubFolders(getter_AddRefs(aEnumerator));
+ 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:
+ GetSubFoldersRunnable(nsIMsgFolder *aFolder);
+ NS_DECL_NSIRUNNABLE
+private:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+};
+
+GetSubFoldersRunnable::GetSubFoldersRunnable(nsIMsgFolder *aFolder) :
+ m_folder(aFolder)
+{
+}
+
+NS_IMETHODIMP GetSubFoldersRunnable::Run()
+{
+ nsCOMPtr<nsISimpleEnumerator> dummy;
+ return m_folder->GetSubFolders(getter_AddRefs(dummy));
+}
+
+
+nsresult ProxyGetSubFolders(nsIMsgFolder *aFolder)
+{
+ RefPtr<GetSubFoldersRunnable> getSubFolders =
+ new GetSubFoldersRunnable(aFolder);
+ return NS_DispatchToMainThread(getSubFolders, NS_DISPATCH_SYNC);
+}
+
+class GetChildNamedRunnable : public mozilla::Runnable
+{
+public:
+ GetChildNamedRunnable(nsIMsgFolder *aFolder, const nsAString& aName, nsIMsgFolder **aChild);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsString m_name;
+ nsIMsgFolder **m_child;
+};
+
+GetChildNamedRunnable::GetChildNamedRunnable(nsIMsgFolder *aFolder,
+ const nsAString & aName,
+ nsIMsgFolder **aChild) :
+ m_folder(aFolder), m_name(aName), m_child(aChild)
+{
+}
+
+NS_IMETHODIMP GetChildNamedRunnable::Run()
+{
+ return m_folder->GetChildNamed(m_name, m_child);
+}
+
+
+nsresult ProxyGetChildNamed(nsIMsgFolder *aFolder, const nsAString & aName,
+ nsIMsgFolder **aChild)
+{
+ RefPtr<GetChildNamedRunnable> getChildNamed =
+ new GetChildNamedRunnable(aFolder, aName, aChild);
+ return NS_DispatchToMainThread(getChildNamed, NS_DISPATCH_SYNC);
+}
+
+class GetParentRunnable : public mozilla::Runnable
+{
+public:
+ GetParentRunnable(nsIMsgFolder *aFolder, nsIMsgFolder **aParent);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsIMsgFolder **m_parent;
+};
+
+GetParentRunnable::GetParentRunnable(nsIMsgFolder *aFolder, nsIMsgFolder **aParent) :
+ m_folder(aFolder), m_parent(aParent)
+{
+}
+
+NS_IMETHODIMP GetParentRunnable::Run()
+{
+ return m_folder->GetParent(m_parent);
+}
+
+
+nsresult ProxyGetParent(nsIMsgFolder *aFolder, nsIMsgFolder **aParent)
+{
+ RefPtr<GetParentRunnable> getParent =
+ new GetParentRunnable(aFolder, aParent);
+ return NS_DispatchToMainThread(getParent, NS_DISPATCH_SYNC);
+}
+
+class ContainsChildNamedRunnable : public mozilla::Runnable
+{
+public:
+ ContainsChildNamedRunnable(nsIMsgFolder *aFolder, const nsAString& aName, bool *aResult);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsString m_name;
+ bool *m_result;
+};
+
+ContainsChildNamedRunnable::ContainsChildNamedRunnable(nsIMsgFolder *aFolder,
+ const nsAString &aName,
+ bool *aResult) :
+ m_folder(aFolder), m_name(aName), m_result(aResult)
+{
+}
+
+NS_IMETHODIMP ContainsChildNamedRunnable::Run()
+{
+ return m_folder->ContainsChildNamed(m_name, m_result);
+}
+
+
+nsresult ProxyContainsChildNamed(nsIMsgFolder *aFolder, const nsAString &aName,
+ bool *aResult)
+{
+ RefPtr<ContainsChildNamedRunnable> containsChildNamed =
+ new ContainsChildNamedRunnable(aFolder, aName, aResult);
+ return NS_DispatchToMainThread(containsChildNamed, NS_DISPATCH_SYNC);
+}
+
+
+class GenerateUniqueSubfolderNameRunnable : public mozilla::Runnable
+{
+public:
+ GenerateUniqueSubfolderNameRunnable(nsIMsgFolder *aFolder,
+ const nsAString& prefix,
+ nsIMsgFolder *otherFolder,
+ nsAString& name);
+ NS_DECL_NSIRUNNABLE
+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)
+ : m_folder(aFolder), m_prefix(aPrefix), m_otherFolder(aOtherFolder), m_name(aName)
+{
+}
+
+NS_IMETHODIMP GenerateUniqueSubfolderNameRunnable::Run()
+{
+ return m_folder->GenerateUniqueSubfolderName(m_prefix, m_otherFolder, m_name);
+}
+
+
+nsresult ProxyGenerateUniqueSubfolderName(nsIMsgFolder *aFolder,
+ const nsAString& aPrefix,
+ nsIMsgFolder *aOtherFolder,
+ nsAString& aName)
+
+{
+ RefPtr<GenerateUniqueSubfolderNameRunnable> generateUniqueSubfolderName =
+ new GenerateUniqueSubfolderNameRunnable(aFolder, aPrefix, aOtherFolder, aName);
+ return NS_DispatchToMainThread(generateUniqueSubfolderName, NS_DISPATCH_SYNC);
+}
+
+class CreateSubfolderRunnable : public mozilla::Runnable
+{
+public:
+ CreateSubfolderRunnable(nsIMsgFolder *aFolder, const nsAString& aName);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+ nsString m_name;
+};
+
+CreateSubfolderRunnable::CreateSubfolderRunnable(nsIMsgFolder *aFolder,
+ const nsAString &aName) :
+ m_folder(aFolder), m_name(aName)
+{
+}
+
+NS_IMETHODIMP CreateSubfolderRunnable::Run()
+{
+ return m_folder->CreateSubfolder(m_name, nullptr);
+}
+
+
+nsresult ProxyCreateSubfolder(nsIMsgFolder *aFolder, const nsAString &aName)
+{
+ RefPtr<CreateSubfolderRunnable> createSubfolder =
+ new CreateSubfolderRunnable(aFolder, aName);
+ return NS_DispatchToMainThread(createSubfolder, NS_DISPATCH_SYNC);
+}
+
+class ForceDBClosedRunnable : public mozilla::Runnable
+{
+public:
+ ForceDBClosedRunnable(nsIMsgFolder *aFolder);
+ NS_DECL_NSIRUNNABLE
+protected:
+ nsCOMPtr<nsIMsgFolder> m_folder;
+};
+
+ForceDBClosedRunnable::ForceDBClosedRunnable(nsIMsgFolder *aFolder) :
+ m_folder(aFolder)
+{
+}
+
+NS_IMETHODIMP ForceDBClosedRunnable::Run()
+{
+ return m_folder->ForceDBClosed();
+}
+
+nsresult ProxyForceDBClosed(nsIMsgFolder *aFolder)
+{
+ RefPtr<ForceDBClosedRunnable> forceDBClosed =
+ new ForceDBClosedRunnable(aFolder);
+ return NS_DispatchToMainThread(forceDBClosed, NS_DISPATCH_SYNC);
+}
+
+