1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, you can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "nsImportMail.h"
7 
8 #include "nsXPCOM.h"
9 #include "nsISupportsPrimitives.h"
10 #include "nsIImportMailboxDescriptor.h"
11 #include "nsIMsgAccountManager.h"
12 #include "nsMsgBaseCID.h"
13 #include "nsImportStringBundle.h"
14 #include "nsTextFormatter.h"
15 #include "ImportDebug.h"
16 #include "plstr.h"
17 #include "nsThreadUtils.h"
18 #include "mozilla/Services.h"
19 #include "msgCore.h"
20 
21 // forward decl for proxy methods
22 nsresult ProxyGetSubFolders(nsIMsgFolder* aFolder);
23 nsresult ProxyGetChildNamed(nsIMsgFolder* aFolder, const nsAString& aName,
24                             nsIMsgFolder** aChild);
25 nsresult ProxyGetParent(nsIMsgFolder* aFolder, nsIMsgFolder** aParent);
26 nsresult ProxyContainsChildNamed(nsIMsgFolder* aFolder, const nsAString& aName,
27                                  bool* aResult);
28 nsresult ProxyGenerateUniqueSubfolderName(nsIMsgFolder* aFolder,
29                                           const nsAString& aPrefix,
30                                           nsIMsgFolder* aOtherFolder,
31                                           nsAString& aName);
32 nsresult ProxyCreateSubfolder(nsIMsgFolder* aFolder, const nsAString& aName);
33 nsresult ProxyForceDBClosed(nsIMsgFolder* aFolder);
34 
NS_NewGenericMail(nsIImportGeneric ** aImportGeneric)35 nsresult NS_NewGenericMail(nsIImportGeneric** aImportGeneric) {
36   NS_ASSERTION(aImportGeneric != nullptr, "null ptr");
37   if (!aImportGeneric) return NS_ERROR_NULL_POINTER;
38 
39   RefPtr<nsImportGenericMail> pGen = new nsImportGenericMail();
40   return pGen->QueryInterface(NS_GET_IID(nsIImportGeneric),
41                               (void**)aImportGeneric);
42 }
43 
nsImportGenericMail()44 nsImportGenericMail::nsImportGenericMail() {
45   m_found = false;
46   m_userVerify = false;
47   m_gotLocation = false;
48   m_gotDefaultMailboxes = false;
49   m_totalSize = 0;
50   m_doImport = false;
51   m_pThreadData = nullptr;
52 
53   m_pDestFolder = nullptr;
54   m_deleteDestFolder = false;
55   m_createdFolder = false;
56   m_performingMigration = false;
57 
58   nsresult rv = nsImportStringBundle::GetStringBundle(
59       IMPORT_MSGS_URL, getter_AddRefs(m_stringBundle));
60   if (NS_FAILED(rv))
61     IMPORT_LOG0("Failed to get string bundle for Importing Mail");
62 }
63 
~nsImportGenericMail()64 nsImportGenericMail::~nsImportGenericMail() {
65   if (m_pThreadData) {
66     m_pThreadData->DriverAbort();
67     m_pThreadData = nullptr;
68   }
69 }
70 
NS_IMPL_ISUPPORTS(nsImportGenericMail,nsIImportGeneric)71 NS_IMPL_ISUPPORTS(nsImportGenericMail, nsIImportGeneric)
72 
73 NS_IMETHODIMP nsImportGenericMail::GetData(const char* dataId,
74                                            nsISupports** _retval) {
75   nsresult rv = NS_OK;
76   NS_ENSURE_ARG_POINTER(_retval);
77 
78   *_retval = nullptr;
79   if (!PL_strcasecmp(dataId, "mailInterface")) {
80     NS_IF_ADDREF(*_retval = m_pInterface);
81   }
82 
83   if (!PL_strcasecmp(dataId, "mailLocation")) {
84     if (!m_pSrcLocation) GetDefaultLocation();
85     NS_IF_ADDREF(*_retval = m_pSrcLocation);
86   }
87 
88   if (!PL_strcasecmp(dataId, "mailDestination")) {
89     if (!m_pDestFolder) GetDefaultDestination();
90     NS_IF_ADDREF(*_retval = m_pDestFolder);
91   }
92 
93   if (!PL_strcasecmp(dataId, "migration")) {
94     nsCOMPtr<nsISupportsPRBool> migrationString =
95         do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID, &rv);
96     NS_ENSURE_SUCCESS(rv, rv);
97     migrationString->SetData(m_performingMigration);
98     migrationString.forget(_retval);
99   }
100 
101   if (!PL_strcasecmp(dataId, "currentMailbox")) {
102     // create an nsISupportsString, get the current mailbox
103     // name being imported and put it in the string
104     nsCOMPtr<nsISupportsString> data =
105         do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
106     if (NS_FAILED(rv)) return rv;
107     if (m_pThreadData) {
108       GetMailboxName(m_pThreadData->currentMailbox, data);
109     }
110     data.forget(_retval);
111   }
112 
113   return rv;
114 }
115 
SetData(const char * dataId,nsISupports * item)116 NS_IMETHODIMP nsImportGenericMail::SetData(const char* dataId,
117                                            nsISupports* item) {
118   nsresult rv = NS_OK;
119   NS_ASSERTION(dataId != nullptr, "null ptr");
120   if (!dataId) return NS_ERROR_NULL_POINTER;
121 
122   if (!PL_strcasecmp(dataId, "mailInterface")) {
123     m_pInterface = nullptr;
124     if (item) m_pInterface = do_QueryInterface(item);
125   }
126 
127   if (!PL_strcasecmp(dataId, "mailLocation")) {
128     m_mailboxes.Clear();
129     m_gotDefaultMailboxes = false;
130     m_pSrcLocation = nullptr;
131     if (item) {
132       nsresult rv;
133       nsCOMPtr<nsIFile> location = do_QueryInterface(item, &rv);
134       NS_ENSURE_SUCCESS(rv, rv);
135       m_pSrcLocation = location;
136     }
137   }
138 
139   if (!PL_strcasecmp(dataId, "mailDestination")) {
140     m_pDestFolder = nullptr;
141     if (item) m_pDestFolder = do_QueryInterface(item);
142     m_deleteDestFolder = false;
143   }
144 
145   if (!PL_strcasecmp(dataId, "name")) {
146     if (item) {
147       nsCOMPtr<nsISupportsString> nameString = do_QueryInterface(item, &rv);
148       if (NS_SUCCEEDED(rv)) rv = nameString->GetData(m_pName);
149     }
150   }
151 
152   if (!PL_strcasecmp(dataId, "migration")) {
153     if (item) {
154       nsCOMPtr<nsISupportsPRBool> migrationString =
155           do_QueryInterface(item, &rv);
156       if (NS_SUCCEEDED(rv))
157         rv = migrationString->GetData(&m_performingMigration);
158     }
159   }
160   return rv;
161 }
162 
GetStatus(const char * statusKind,int32_t * _retval)163 NS_IMETHODIMP nsImportGenericMail::GetStatus(const char* statusKind,
164                                              int32_t* _retval) {
165   NS_ASSERTION(statusKind != nullptr, "null ptr");
166   NS_ASSERTION(_retval != nullptr, "null ptr");
167   if (!statusKind || !_retval) return NS_ERROR_NULL_POINTER;
168 
169   *_retval = 0;
170 
171   if (!PL_strcasecmp(statusKind, "isInstalled")) {
172     GetDefaultLocation();
173     *_retval = (int32_t)m_found;
174   }
175 
176   if (!PL_strcasecmp(statusKind, "canUserSetLocation")) {
177     GetDefaultLocation();
178     *_retval = (int32_t)m_userVerify;
179   }
180 
181   return NS_OK;
182 }
183 
GetDefaultLocation(void)184 void nsImportGenericMail::GetDefaultLocation(void) {
185   if (!m_pInterface) return;
186 
187   if (m_pSrcLocation && m_gotLocation) return;
188 
189   m_gotLocation = true;
190 
191   nsCOMPtr<nsIFile> pLoc;
192   m_pInterface->GetDefaultLocation(getter_AddRefs(pLoc), &m_found,
193                                    &m_userVerify);
194   if (!m_pSrcLocation) m_pSrcLocation = pLoc;
195 }
196 
GetDefaultMailboxes(void)197 void nsImportGenericMail::GetDefaultMailboxes(void) {
198   if (!m_pInterface || !m_pSrcLocation) return;
199   if (m_gotDefaultMailboxes) return;
200   m_pInterface->FindMailboxes(m_pSrcLocation, m_mailboxes);
201   m_gotDefaultMailboxes = true;
202 }
203 
GetDefaultDestination(void)204 void nsImportGenericMail::GetDefaultDestination(void) {
205   if (m_pDestFolder) return;
206   if (!m_pInterface) return;
207 
208   nsIMsgFolder* rootFolder;
209   m_deleteDestFolder = false;
210   m_createdFolder = false;
211   if (CreateFolder(&rootFolder)) {
212     m_pDestFolder = rootFolder;
213     m_deleteDestFolder = true;
214     m_createdFolder = true;
215     return;
216   }
217   IMPORT_LOG0(
218       "*** GetDefaultDestination: Failed to create a default import "
219       "destination folder.");
220 }
221 
WantsProgress(bool * _retval)222 NS_IMETHODIMP nsImportGenericMail::WantsProgress(bool* _retval) {
223   NS_ASSERTION(_retval != nullptr, "null ptr");
224   NS_ENSURE_ARG_POINTER(_retval);
225 
226   if (m_pThreadData) {
227     m_pThreadData->DriverAbort();
228     m_pThreadData = nullptr;
229   }
230 
231   GetDefaultLocation();
232   GetDefaultMailboxes();
233 
234   if (!m_pDestFolder) {
235     GetDefaultDestination();
236   }
237 
238   bool result = false;
239   uint32_t totalSize = 0;
240   for (nsIImportMailboxDescriptor* box : m_mailboxes) {
241     bool doImport = false;
242     uint32_t size = 0;
243     nsresult rv = box->GetImport(&doImport);
244     if (NS_SUCCEEDED(rv) && doImport) {
245       (void)box->GetSize(&size);
246       result = true;
247     }
248     totalSize += size;
249   }
250   m_totalSize = totalSize;
251   m_doImport = result;
252   *_retval = result;
253   return NS_OK;
254 }
255 
GetMailboxName(uint32_t index,nsISupportsString * pStr)256 void nsImportGenericMail::GetMailboxName(uint32_t index,
257                                          nsISupportsString* pStr) {
258   if (index >= m_mailboxes.Length()) {
259     return;
260   }
261   nsAutoString name;
262   m_mailboxes[index]->GetDisplayName(getter_Copies(name));
263   if (!name.IsEmpty()) {
264     pStr->SetData(name);
265   }
266 }
267 
BeginImport(nsISupportsString * successLog,nsISupportsString * errorLog,bool * _retval)268 NS_IMETHODIMP nsImportGenericMail::BeginImport(nsISupportsString* successLog,
269                                                nsISupportsString* errorLog,
270                                                bool* _retval) {
271   NS_ASSERTION(_retval != nullptr, "null ptr");
272   if (!_retval) return NS_ERROR_NULL_POINTER;
273 
274   nsString success;
275   nsString error;
276 
277   if (!m_doImport) {
278     nsImportStringBundle::GetStringByID(IMPORT_NO_MAILBOXES, m_stringBundle,
279                                         success);
280     SetLogs(success, error, successLog, errorLog);
281     *_retval = true;
282     return NS_OK;
283   }
284 
285   if (!m_pInterface || !m_gotDefaultMailboxes) {
286     IMPORT_LOG0(
287         "*** BeginImport: Either the interface or source mailbox is not set "
288         "properly.");
289     nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOTINITIALIZED,
290                                         m_stringBundle, error);
291     SetLogs(success, error, successLog, errorLog);
292     *_retval = false;
293     return NS_OK;
294   }
295 
296   if (!m_pDestFolder) {
297     IMPORT_LOG0(
298         "*** BeginImport: The destination mailbox is not set properly.");
299     nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NODESTFOLDER,
300                                         m_stringBundle, error);
301     SetLogs(success, error, successLog, errorLog);
302     *_retval = false;
303     return NS_OK;
304   }
305 
306   if (m_pThreadData) {
307     m_pThreadData->DriverAbort();
308     m_pThreadData = nullptr;
309   }
310 
311   m_pSuccessLog = successLog;
312   m_pErrorLog = errorLog;
313 
314   // kick off the thread to do the import!!!!
315   m_pThreadData = new ImportThreadData();
316   m_pThreadData->boxes = m_mailboxes.Clone();
317   m_pThreadData->mailImport = m_pInterface;
318   m_pThreadData->errorLog = m_pErrorLog;
319   m_pThreadData->successLog = m_pSuccessLog;
320 
321   m_pThreadData->ownsDestRoot = m_deleteDestFolder;
322   m_pThreadData->destRoot = m_pDestFolder;
323   m_pThreadData->performingMigration = m_performingMigration;
324 
325   m_pThreadData->stringBundle = m_stringBundle;
326 
327   // Previously this was run in a sub-thread, after introducing
328   // SeamonkeyImport.jsm and because JS XPCOM can only run in the main thread,
329   // this has been changed to run in the main thread.
330   ImportMailThread(m_pThreadData);
331   *_retval = true;
332   return NS_OK;
333 }
334 
ContinueImport(bool * _retval)335 NS_IMETHODIMP nsImportGenericMail::ContinueImport(bool* _retval) {
336   NS_ASSERTION(_retval != nullptr, "null ptr");
337   if (!_retval) return NS_ERROR_NULL_POINTER;
338 
339   *_retval = true;
340   if (m_pThreadData) {
341     if (m_pThreadData->fatalError) *_retval = false;
342   }
343 
344   return NS_OK;
345 }
346 
GetProgress(int32_t * _retval)347 NS_IMETHODIMP nsImportGenericMail::GetProgress(int32_t* _retval) {
348   // This returns the progress from the the currently
349   // running import mail or import address book thread.
350   NS_ASSERTION(_retval != nullptr, "null ptr");
351   if (!_retval) return NS_ERROR_NULL_POINTER;
352 
353   if (!m_pThreadData || !(m_pThreadData->threadAlive)) {
354     *_retval = 100;
355     return NS_OK;
356   }
357 
358   uint32_t sz = 0;
359   if (m_pThreadData->currentSize && m_pInterface) {
360     if (NS_FAILED(m_pInterface->GetImportProgress(&sz))) sz = 0;
361   }
362 
363   // *_retval = (int32_t) (((uint32_t)(m_pThreadData->currentTotal + sz) *
364   // (uint32_t)100) / m_totalSize);
365 
366   if (m_totalSize) {
367     double perc;
368     perc = (double)m_pThreadData->currentTotal;
369     perc += sz;
370     perc *= 100;
371     perc /= m_totalSize;
372     *_retval = (int32_t)perc;
373     if (*_retval > 100) *_retval = 100;
374   } else
375     *_retval = 0;
376 
377   // never return 100% while the thread is still alive
378   if (*_retval > 99) *_retval = 99;
379 
380   return NS_OK;
381 }
382 
ReportError(int32_t id,const char16_t * pName,nsString * pStream,nsIStringBundle * aBundle)383 void nsImportGenericMail::ReportError(int32_t id, const char16_t* pName,
384                                       nsString* pStream,
385                                       nsIStringBundle* aBundle) {
386   if (!pStream) return;
387 
388   // load the error string
389   char16_t* pFmt = nsImportStringBundle::GetStringByID(id, aBundle);
390   nsString pText;
391   nsTextFormatter::ssprintf(pText, pFmt, pName);
392   pStream->Append(pText);
393   free(pFmt);
394   pStream->Append(NS_ConvertASCIItoUTF16(MSG_LINEBREAK));
395 }
396 
SetLogs(nsString & success,nsString & error,nsISupportsString * pSuccess,nsISupportsString * pError)397 void nsImportGenericMail::SetLogs(nsString& success, nsString& error,
398                                   nsISupportsString* pSuccess,
399                                   nsISupportsString* pError) {
400   nsAutoString str;
401   if (pSuccess) {
402     pSuccess->GetData(str);
403     str.Append(success);
404     pSuccess->SetData(str);
405   }
406   if (pError) {
407     pError->GetData(str);
408     str.Append(error);
409     pError->SetData(str);
410   }
411 }
412 
CancelImport(void)413 NS_IMETHODIMP nsImportGenericMail::CancelImport(void) {
414   if (m_pThreadData) {
415     m_pThreadData->abort = true;
416     m_pThreadData->DriverAbort();
417     m_pThreadData = nullptr;
418   }
419 
420   return NS_OK;
421 }
422 
ImportThreadData()423 ImportThreadData::ImportThreadData() {
424   fatalError = false;
425   driverAlive = true;
426   threadAlive = true;
427   abort = false;
428   currentTotal = 0;
429   currentSize = 0;
430   destRoot = nullptr;
431   ownsDestRoot = false;
432 }
433 
~ImportThreadData()434 ImportThreadData::~ImportThreadData() {}
435 
DriverDelete(void)436 void ImportThreadData::DriverDelete(void) {
437   driverAlive = false;
438   if (!driverAlive && !threadAlive) delete this;
439 }
440 
ThreadDelete()441 void ImportThreadData::ThreadDelete() {
442   threadAlive = false;
443   if (!driverAlive && !threadAlive) delete this;
444 }
445 
DriverAbort()446 void ImportThreadData::DriverAbort() {
447   if (abort && !threadAlive && destRoot) {
448     if (ownsDestRoot) {
449       destRoot->RecursiveDelete(true, nullptr);
450     } else {
451       // FIXME: just delete the stuff we created?
452     }
453   } else
454     abort = true;
455   DriverDelete();
456 }
457 
ImportMailThread(void * stuff)458 static void ImportMailThread(void* stuff) {
459   ImportThreadData* pData = (ImportThreadData*)stuff;
460 
461   IMPORT_LOG0("ImportMailThread: Starting...");
462 
463   nsresult rv = NS_OK;
464 
465   nsCOMPtr<nsIMsgFolder> destRoot(pData->destRoot);
466 
467   uint32_t count = pData->boxes.Length();
468 
469   uint32_t size;
470   uint32_t depth = 1;
471   uint32_t newDepth;
472   nsString lastName;
473 
474   nsCOMPtr<nsIMsgFolder> curFolder(destRoot);
475 
476   nsCOMPtr<nsIMsgFolder> newFolder;
477   nsCOMPtr<nsIMsgFolder> subFolder;
478 
479   bool exists;
480 
481   nsString success;
482   nsString error;
483 
484   // GetSubFolders() will initialize folders if they are not already
485   // initialized.
486   ProxyGetSubFolders(curFolder);
487 
488   IMPORT_LOG1("ImportMailThread: Total number of folders to import = %d.",
489               count);
490 
491   // Note that the front-end js script only displays one import result string so
492   // we combine both good and bad import status into one string (in var
493   // 'success').
494 
495   for (uint32_t i = 0; (i < count) && !(pData->abort); i++) {
496     nsIImportMailboxDescriptor* box = pData->boxes[i];
497     pData->currentMailbox = i;
498 
499     bool doImport = false;
500     size = 0;
501     rv = box->GetImport(&doImport);
502     if (doImport) rv = box->GetSize(&size);
503     rv = box->GetDepth(&newDepth);
504     if (newDepth > depth) {
505       // OK, we are going to add a subfolder under the last/previous folder we
506       // processed, so find this folder (stored in 'lastName') who is going to
507       // be the new parent folder.
508       IMPORT_LOG1("ImportMailThread: Processing child folder '%s'.",
509                   NS_ConvertUTF16toUTF8(lastName).get());
510       rv = ProxyGetChildNamed(curFolder, lastName, getter_AddRefs(subFolder));
511       if (NS_FAILED(rv)) {
512         IMPORT_LOG1(
513             "*** ImportMailThread: Failed to get the interface for child "
514             "folder '%s'.",
515             NS_ConvertUTF16toUTF8(lastName).get());
516         nsImportGenericMail::ReportError(IMPORT_ERROR_MB_FINDCHILD,
517                                          lastName.get(), &error,
518                                          pData->stringBundle);
519         pData->fatalError = true;
520         break;
521       }
522       curFolder = subFolder;
523       // Make sure this new parent folder obj has the correct subfolder list
524       // so far.
525       rv = ProxyGetSubFolders(curFolder);
526     } else if (newDepth < depth) {
527       rv = NS_OK;
528       while ((newDepth < depth) && NS_SUCCEEDED(rv)) {
529         rv = curFolder->GetParent(getter_AddRefs(curFolder));
530         if (NS_FAILED(rv)) {
531           IMPORT_LOG1(
532               "*** ImportMailThread: Failed to get the interface for parent "
533               "folder '%s'.",
534               NS_ConvertUTF16toUTF8(lastName).get());
535           nsImportGenericMail::ReportError(IMPORT_ERROR_MB_FINDCHILD,
536                                            lastName.get(), &error,
537                                            pData->stringBundle);
538           pData->fatalError = true;
539           break;
540         }
541         depth--;
542       }
543       if (NS_FAILED(rv)) {
544         IMPORT_LOG1(
545             "*** ImportMailThread: Failed to get the proxy interface for "
546             "parent folder '%s'.",
547             NS_ConvertUTF16toUTF8(lastName).get());
548         nsImportStringBundle::GetStringByID(IMPORT_ERROR_MB_NOPROXY,
549                                             pData->stringBundle, error);
550         pData->fatalError = true;
551         break;
552       }
553     }
554     depth = newDepth;
555     char16_t* pName = nullptr;
556     box->GetDisplayName(&pName);
557     if (pName) {
558       lastName = pName;
559       free(pName);
560     } else
561       lastName.AssignLiteral("Unknown!");
562 
563     // translate the folder name if we are doing migration, but
564     // only for special folders which are at the root level
565     if (pData->performingMigration && depth == 1)
566       pData->mailImport->TranslateFolderName(lastName, lastName);
567 
568     exists = false;
569     rv = ProxyContainsChildNamed(curFolder, lastName, &exists);
570 
571     // If we are performing profile migration (as opposed to importing) then
572     // we are starting with empty local folders. In that case, always choose
573     // to over-write the existing local folder with this name. Don't create a
574     // unique subfolder name. Otherwise you end up with "Inbox, Inbox0" or
575     // "Unsent Folders, UnsentFolders0"
576     if (exists && !pData->performingMigration) {
577       nsString subName;
578       ProxyGenerateUniqueSubfolderName(curFolder, lastName, nullptr, subName);
579       if (!subName.IsEmpty()) lastName.Assign(subName);
580     }
581 
582     IMPORT_LOG1("ImportMailThread: Creating new import folder '%s'.",
583                 NS_ConvertUTF16toUTF8(lastName).get());
584     ProxyCreateSubfolder(
585         curFolder,
586         lastName);  // this may fail if the folder already exists..that's ok
587 
588     rv = ProxyGetChildNamed(curFolder, lastName, getter_AddRefs(newFolder));
589     if (NS_FAILED(rv)) {
590       IMPORT_LOG1(
591           "*** ImportMailThread: Failed to locate subfolder '%s' after it's "
592           "been created.",
593           NS_ConvertUTF16toUTF8(lastName).get());
594       nsImportGenericMail::ReportError(IMPORT_ERROR_MB_CREATE, lastName.get(),
595                                        &error, pData->stringBundle);
596     }
597 
598     if (size && doImport && newFolder && NS_SUCCEEDED(rv)) {
599       bool fatalError = false;
600       pData->currentSize = size;
601       char16_t* pSuccess = nullptr;
602       char16_t* pError = nullptr;
603       rv = pData->mailImport->ImportMailbox(box, newFolder, &pError, &pSuccess,
604                                             &fatalError);
605       if (pError) {
606         error.Append(pError);
607         free(pError);
608       }
609       if (pSuccess) {
610         success.Append(pSuccess);
611         free(pSuccess);
612       }
613 
614       pData->currentSize = 0;
615       pData->currentTotal += size;
616 
617       // commit to the db synchronously, but using a proxy since it doesn't
618       // like being used elsewhere than from the main thread. OK, we've copied
619       // the actual folder/file over if the folder size is not 0 (ie, the msg
620       // summary is no longer valid) so close the msg database so that when
621       // the folder is reopened the folder db can be reconstructed (which
622       // validates msg summary and forces folder to be reparsed).
623       rv = ProxyForceDBClosed(newFolder);
624       fatalError = NS_FAILED(rv);
625 
626       if (fatalError) {
627         IMPORT_LOG1(
628             "*** ImportMailThread: ImportMailbox returned fatalError, "
629             "mailbox #%d\n",
630             (int)i);
631         pData->fatalError = true;
632         break;
633       }
634     }
635   }
636 
637   // Now save the new acct info to pref file.
638   nsCOMPtr<nsIMsgAccountManager> accMgr =
639       do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
640   if (NS_SUCCEEDED(rv) && accMgr) {
641     rv = accMgr->SaveAccountInfo();
642     NS_ASSERTION(NS_SUCCEEDED(rv), "Can't save account info to pref file");
643   }
644 
645   nsImportGenericMail::SetLogs(success, error, pData->successLog,
646                                pData->errorLog);
647 
648   if (pData->abort || pData->fatalError) {
649     IMPORT_LOG0("*** ImportMailThread: Abort or fatalError flag was set\n");
650     if (pData->ownsDestRoot) {
651       IMPORT_LOG0("Calling destRoot->RecursiveDelete\n");
652       destRoot->RecursiveDelete(true, nullptr);
653     } else {
654       // FIXME: just delete the stuff we created?
655     }
656   }
657 
658   IMPORT_LOG1("Import mailbox thread done: %d\n", (int)pData->currentTotal);
659 
660   pData->ThreadDelete();
661 }
662 
663 // Creates a folder in Local Folders with the module name + mail
664 // for e.g: Outlook Mail
CreateFolder(nsIMsgFolder ** ppFolder)665 bool nsImportGenericMail::CreateFolder(nsIMsgFolder** ppFolder) {
666   nsresult rv;
667   *ppFolder = nullptr;
668 
669   nsCOMPtr<nsIStringBundle> bundle;
670   nsCOMPtr<nsIStringBundleService> bundleService =
671       mozilla::services::GetStringBundleService();
672   if (!bundleService) return false;
673   rv = bundleService->CreateBundle(IMPORT_MSGS_URL, getter_AddRefs(bundle));
674   if (NS_FAILED(rv)) return false;
675   nsString folderName;
676   if (!m_pName.IsEmpty()) {
677     AutoTArray<nsString, 1> moduleName = {m_pName};
678     rv = bundle->FormatStringFromName("ImportModuleFolderName", moduleName,
679                                       folderName);
680   } else {
681     rv = bundle->GetStringFromName("DefaultFolderName", folderName);
682   }
683   if (NS_FAILED(rv)) {
684     IMPORT_LOG0("*** Failed to get Folder Name!\n");
685     return false;
686   }
687   nsCOMPtr<nsIMsgAccountManager> accMgr =
688       do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
689   if (NS_FAILED(rv)) {
690     IMPORT_LOG0("*** Failed to create account manager!\n");
691     return false;
692   }
693 
694   nsCOMPtr<nsIMsgIncomingServer> server;
695   rv = accMgr->GetLocalFoldersServer(getter_AddRefs(server));
696   // if Local Folders does not exist already, create it
697   if (NS_FAILED(rv) || !server) {
698     rv = accMgr->CreateLocalMailAccount();
699     if (NS_FAILED(rv)) {
700       IMPORT_LOG0("*** Failed to create Local Folders!\n");
701       return false;
702     }
703 
704     rv = accMgr->GetLocalFoldersServer(getter_AddRefs(server));
705   }
706 
707   if (NS_SUCCEEDED(rv) && server) {
708     nsCOMPtr<nsIMsgFolder> localRootFolder;
709     rv = server->GetRootMsgFolder(getter_AddRefs(localRootFolder));
710     if (localRootFolder) {
711       // we need to call GetSubFolders() so that the folders get initialized
712       // if they are not initialized yet.
713       nsTArray<RefPtr<nsIMsgFolder>> dummy;
714       rv = localRootFolder->GetSubFolders(dummy);
715       if (NS_SUCCEEDED(rv)) {
716         // check if the folder name we picked already exists.
717         bool exists = false;
718         rv = localRootFolder->ContainsChildNamed(folderName, &exists);
719         if (exists) {
720           nsString name;
721           localRootFolder->GenerateUniqueSubfolderName(folderName, nullptr,
722                                                        name);
723           if (!name.IsEmpty())
724             folderName.Assign(name);
725           else {
726             IMPORT_LOG0("*** Failed to find a unique folder name!\n");
727             return false;
728           }
729         }
730         IMPORT_LOG1("Creating folder for importing mail: '%s'\n",
731                     NS_ConvertUTF16toUTF8(folderName).get());
732 
733         // Bug 564162 identifies a dataloss design flaw.
734         // A working Thunderbird client can have mail in Local Folders and a
735         // subsequent import 'Everything' will trigger a migration which
736         // overwrites existing mailboxes with the imported mailboxes.
737         rv = localRootFolder->CreateSubfolder(folderName, nullptr);
738         if (NS_SUCCEEDED(rv)) {
739           rv = localRootFolder->GetChildNamed(folderName, ppFolder);
740           if (*ppFolder) {
741             IMPORT_LOG1("Folder '%s' created successfully\n",
742                         NS_ConvertUTF16toUTF8(folderName).get());
743             return true;
744           }
745         }
746       }
747     }  // if localRootFolder
748   }    // if server
749   IMPORT_LOG0("****** FAILED TO CREATE FOLDER FOR IMPORT\n");
750   return false;
751 }
752 
753 /**
754  * These are the proxy objects we use to proxy nsIMsgFolder methods back
755  * the the main thread. Since there are only five, we can hand roll them.
756  * A better design might be a co-routine-ish design where the ui thread
757  * hands off each folder to the import thread and when the thread finishes
758  * the folder, the main thread hands it the next folder.
759  */
760 
761 class GetSubFoldersRunnable : public mozilla::Runnable {
762  public:
763   explicit GetSubFoldersRunnable(nsIMsgFolder* aFolder);
764   NS_DECL_NSIRUNNABLE
765   nsresult mResult;
766 
767  private:
768   nsCOMPtr<nsIMsgFolder> m_folder;
769 };
770 
GetSubFoldersRunnable(nsIMsgFolder * aFolder)771 GetSubFoldersRunnable::GetSubFoldersRunnable(nsIMsgFolder* aFolder)
772     : mozilla::Runnable("GetSubFoldersRunnable"), m_folder(aFolder) {}
773 
Run()774 NS_IMETHODIMP GetSubFoldersRunnable::Run() {
775   nsTArray<RefPtr<nsIMsgFolder>> dummy;
776   mResult = m_folder->GetSubFolders(dummy);
777   return NS_OK;  // Sync runnable must return OK.
778 }
779 
ProxyGetSubFolders(nsIMsgFolder * aFolder)780 nsresult ProxyGetSubFolders(nsIMsgFolder* aFolder) {
781   RefPtr<GetSubFoldersRunnable> getSubFolders =
782       new GetSubFoldersRunnable(aFolder);
783   nsresult rv = NS_DispatchToMainThread(getSubFolders, NS_DISPATCH_SYNC);
784   NS_ENSURE_SUCCESS(rv, rv);
785   return getSubFolders->mResult;
786 }
787 
788 class GetChildNamedRunnable : public mozilla::Runnable {
789  public:
790   GetChildNamedRunnable(nsIMsgFolder* aFolder, const nsAString& aName,
791                         nsIMsgFolder** aChild);
792   NS_DECL_NSIRUNNABLE
793   nsresult mResult;
794 
795  protected:
796   nsCOMPtr<nsIMsgFolder> m_folder;
797   nsString m_name;
798   nsIMsgFolder** m_child;
799 };
800 
GetChildNamedRunnable(nsIMsgFolder * aFolder,const nsAString & aName,nsIMsgFolder ** aChild)801 GetChildNamedRunnable::GetChildNamedRunnable(nsIMsgFolder* aFolder,
802                                              const nsAString& aName,
803                                              nsIMsgFolder** aChild)
804     : mozilla::Runnable("GetChildNamedRunnable"),
805       m_folder(aFolder),
806       m_name(aName),
807       m_child(aChild) {}
808 
Run()809 NS_IMETHODIMP GetChildNamedRunnable::Run() {
810   mResult = m_folder->GetChildNamed(m_name, m_child);
811   return NS_OK;  // Sync runnable must return OK.
812 }
813 
ProxyGetChildNamed(nsIMsgFolder * aFolder,const nsAString & aName,nsIMsgFolder ** aChild)814 nsresult ProxyGetChildNamed(nsIMsgFolder* aFolder, const nsAString& aName,
815                             nsIMsgFolder** aChild) {
816   RefPtr<GetChildNamedRunnable> getChildNamed =
817       new GetChildNamedRunnable(aFolder, aName, aChild);
818   nsresult rv = NS_DispatchToMainThread(getChildNamed, NS_DISPATCH_SYNC);
819   NS_ENSURE_SUCCESS(rv, rv);
820   return getChildNamed->mResult;
821 }
822 
823 class GetParentRunnable : public mozilla::Runnable {
824  public:
825   GetParentRunnable(nsIMsgFolder* aFolder, nsIMsgFolder** aParent);
826   NS_DECL_NSIRUNNABLE
827   nsresult mResult;
828 
829  protected:
830   nsCOMPtr<nsIMsgFolder> m_folder;
831   nsIMsgFolder** m_parent;
832 };
833 
GetParentRunnable(nsIMsgFolder * aFolder,nsIMsgFolder ** aParent)834 GetParentRunnable::GetParentRunnable(nsIMsgFolder* aFolder,
835                                      nsIMsgFolder** aParent)
836     : mozilla::Runnable("GetParentRunnable"),
837       m_folder(aFolder),
838       m_parent(aParent) {}
839 
Run()840 NS_IMETHODIMP GetParentRunnable::Run() {
841   mResult = m_folder->GetParent(m_parent);
842   return NS_OK;  // Sync runnable must return OK.
843 }
844 
ProxyGetParent(nsIMsgFolder * aFolder,nsIMsgFolder ** aParent)845 nsresult ProxyGetParent(nsIMsgFolder* aFolder, nsIMsgFolder** aParent) {
846   RefPtr<GetParentRunnable> getParent = new GetParentRunnable(aFolder, aParent);
847   nsresult rv = NS_DispatchToMainThread(getParent, NS_DISPATCH_SYNC);
848   NS_ENSURE_SUCCESS(rv, rv);
849   return getParent->mResult;
850 }
851 
852 class ContainsChildNamedRunnable : public mozilla::Runnable {
853  public:
854   ContainsChildNamedRunnable(nsIMsgFolder* aFolder, const nsAString& aName,
855                              bool* aResult);
856   NS_DECL_NSIRUNNABLE
857   nsresult mResult;
858 
859  protected:
860   nsCOMPtr<nsIMsgFolder> m_folder;
861   nsString m_name;
862   bool* m_result;
863 };
864 
ContainsChildNamedRunnable(nsIMsgFolder * aFolder,const nsAString & aName,bool * aResult)865 ContainsChildNamedRunnable::ContainsChildNamedRunnable(nsIMsgFolder* aFolder,
866                                                        const nsAString& aName,
867                                                        bool* aResult)
868     : mozilla::Runnable("ContainsChildNamedRunnable"),
869       m_folder(aFolder),
870       m_name(aName),
871       m_result(aResult) {}
872 
Run()873 NS_IMETHODIMP ContainsChildNamedRunnable::Run() {
874   mResult = m_folder->ContainsChildNamed(m_name, m_result);
875   return NS_OK;  // Sync runnable must return OK.
876 }
877 
ProxyContainsChildNamed(nsIMsgFolder * aFolder,const nsAString & aName,bool * aResult)878 nsresult ProxyContainsChildNamed(nsIMsgFolder* aFolder, const nsAString& aName,
879                                  bool* aResult) {
880   NS_ENSURE_ARG(aFolder);
881   RefPtr<ContainsChildNamedRunnable> containsChildNamed =
882       new ContainsChildNamedRunnable(aFolder, aName, aResult);
883   nsresult rv = NS_DispatchToMainThread(containsChildNamed, NS_DISPATCH_SYNC);
884   NS_ENSURE_SUCCESS(rv, rv);
885   return containsChildNamed->mResult;
886 }
887 
888 class GenerateUniqueSubfolderNameRunnable : public mozilla::Runnable {
889  public:
890   GenerateUniqueSubfolderNameRunnable(nsIMsgFolder* aFolder,
891                                       const nsAString& prefix,
892                                       nsIMsgFolder* otherFolder,
893                                       nsAString& name);
894   NS_DECL_NSIRUNNABLE
895   nsresult mResult;
896 
897  protected:
898   nsCOMPtr<nsIMsgFolder> m_folder;
899   nsString m_prefix;
900   nsCOMPtr<nsIMsgFolder> m_otherFolder;
901   nsString m_name;
902 };
903 
GenerateUniqueSubfolderNameRunnable(nsIMsgFolder * aFolder,const nsAString & aPrefix,nsIMsgFolder * aOtherFolder,nsAString & aName)904 GenerateUniqueSubfolderNameRunnable::GenerateUniqueSubfolderNameRunnable(
905     nsIMsgFolder* aFolder, const nsAString& aPrefix, nsIMsgFolder* aOtherFolder,
906     nsAString& aName)
907     : mozilla::Runnable("GenerateUniqueSubfolderNameRunnable"),
908       m_folder(aFolder),
909       m_prefix(aPrefix),
910       m_otherFolder(aOtherFolder),
911       m_name(aName) {}
912 
Run()913 NS_IMETHODIMP GenerateUniqueSubfolderNameRunnable::Run() {
914   mResult =
915       m_folder->GenerateUniqueSubfolderName(m_prefix, m_otherFolder, m_name);
916   return NS_OK;  // Sync runnable must return OK.
917 }
918 
ProxyGenerateUniqueSubfolderName(nsIMsgFolder * aFolder,const nsAString & aPrefix,nsIMsgFolder * aOtherFolder,nsAString & aName)919 nsresult ProxyGenerateUniqueSubfolderName(nsIMsgFolder* aFolder,
920                                           const nsAString& aPrefix,
921                                           nsIMsgFolder* aOtherFolder,
922                                           nsAString& aName)
923 
924 {
925   RefPtr<GenerateUniqueSubfolderNameRunnable> generateUniqueSubfolderName =
926       new GenerateUniqueSubfolderNameRunnable(aFolder, aPrefix, aOtherFolder,
927                                               aName);
928   nsresult rv =
929       NS_DispatchToMainThread(generateUniqueSubfolderName, NS_DISPATCH_SYNC);
930   NS_ENSURE_SUCCESS(rv, rv);
931   return generateUniqueSubfolderName->mResult;
932 }
933 
934 class CreateSubfolderRunnable : public mozilla::Runnable {
935  public:
936   CreateSubfolderRunnable(nsIMsgFolder* aFolder, const nsAString& aName);
937   NS_DECL_NSIRUNNABLE
938   nsresult mResult;
939 
940  protected:
941   nsCOMPtr<nsIMsgFolder> m_folder;
942   nsString m_name;
943 };
944 
CreateSubfolderRunnable(nsIMsgFolder * aFolder,const nsAString & aName)945 CreateSubfolderRunnable::CreateSubfolderRunnable(nsIMsgFolder* aFolder,
946                                                  const nsAString& aName)
947     : mozilla::Runnable("CreateSubfolderRunnable"),
948       m_folder(aFolder),
949       m_name(aName) {}
950 
Run()951 NS_IMETHODIMP CreateSubfolderRunnable::Run() {
952   mResult = m_folder->CreateSubfolder(m_name, nullptr);
953   return NS_OK;  // Sync runnable must return OK.
954 }
955 
ProxyCreateSubfolder(nsIMsgFolder * aFolder,const nsAString & aName)956 nsresult ProxyCreateSubfolder(nsIMsgFolder* aFolder, const nsAString& aName) {
957   RefPtr<CreateSubfolderRunnable> createSubfolder =
958       new CreateSubfolderRunnable(aFolder, aName);
959   nsresult rv = NS_DispatchToMainThread(createSubfolder, NS_DISPATCH_SYNC);
960   NS_ENSURE_SUCCESS(rv, rv);
961   return createSubfolder->mResult;
962 }
963 
964 class ForceDBClosedRunnable : public mozilla::Runnable {
965  public:
966   explicit ForceDBClosedRunnable(nsIMsgFolder* aFolder);
967   NS_DECL_NSIRUNNABLE
968   nsresult mResult;
969 
970  protected:
971   nsCOMPtr<nsIMsgFolder> m_folder;
972 };
973 
ForceDBClosedRunnable(nsIMsgFolder * aFolder)974 ForceDBClosedRunnable::ForceDBClosedRunnable(nsIMsgFolder* aFolder)
975     : mozilla::Runnable("ForceDBClosedRunnable"), m_folder(aFolder) {}
976 
Run()977 NS_IMETHODIMP ForceDBClosedRunnable::Run() {
978   mResult = m_folder->ForceDBClosed();
979   return NS_OK;  // Sync runnable must return OK.
980 }
981 
ProxyForceDBClosed(nsIMsgFolder * aFolder)982 nsresult ProxyForceDBClosed(nsIMsgFolder* aFolder) {
983   RefPtr<ForceDBClosedRunnable> forceDBClosed =
984       new ForceDBClosedRunnable(aFolder);
985   nsresult rv = NS_DispatchToMainThread(forceDBClosed, NS_DISPATCH_SYNC);
986   NS_ENSURE_SUCCESS(rv, rv);
987   return forceDBClosed->mResult;
988 }
989