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