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 "nsIPrefService.h"
7 #include "nsIPrefBranch.h"
8 #include "prlog.h"
9 
10 #include "msgCore.h"  // precompiled header...
11 #include "nsLocalMailFolder.h"
12 #include "nsMsgLocalFolderHdrs.h"
13 #include "nsMsgFolderFlags.h"
14 #include "nsMsgMessageFlags.h"
15 #include "prprf.h"
16 #include "prmem.h"
17 #include "nsITransactionManager.h"
18 #include "nsParseMailbox.h"
19 #include "nsIMsgAccountManager.h"
20 #include "nsIMsgWindow.h"
21 #include "nsCOMPtr.h"
22 #include "nsMsgDBCID.h"
23 #include "nsMsgUtils.h"
24 #include "nsLocalUtils.h"
25 #include "nsIPop3IncomingServer.h"
26 #include "nsILocalMailIncomingServer.h"
27 #include "nsIMsgIncomingServer.h"
28 #include "nsMsgBaseCID.h"
29 #include "nsMsgLocalCID.h"
30 #include "nsString.h"
31 #include "nsIMsgFolderCacheElement.h"
32 #include "nsUnicharUtils.h"
33 #include "nsMsgUtils.h"
34 #include "nsICopyMessageStreamListener.h"
35 #include "nsIMsgCopyService.h"
36 #include "nsMsgTxn.h"
37 #include "nsIMessenger.h"
38 #include "nsMsgBaseCID.h"
39 #include "nsNativeCharsetUtils.h"
40 #include "nsIDocShell.h"
41 #include "nsIPrompt.h"
42 #include "nsIPop3URL.h"
43 #include "nsIMsgMailSession.h"
44 #include "nsIMsgFolderCompactor.h"
45 #include "nsNetCID.h"
46 #include "nsISpamSettings.h"
47 #include "nsNativeCharsetUtils.h"
48 #include "nsMailHeaders.h"
49 #include "nsCOMArray.h"
50 #include "nsIRssIncomingServer.h"
51 #include "nsNetUtil.h"
52 #include "nsIMsgFolderNotificationService.h"
53 #include "nsReadLine.h"
54 #include "nsIStringEnumerator.h"
55 #include "nsIURIMutator.h"
56 #include "mozilla/Services.h"
57 #include "mozilla/UniquePtr.h"
58 
59 //////////////////////////////////////////////////////////////////////////////
60 // nsLocal
61 /////////////////////////////////////////////////////////////////////////////
62 
nsLocalMailCopyState()63 nsLocalMailCopyState::nsLocalMailCopyState()
64     : m_flags(0),
65       m_lastProgressTime(PR_IntervalToMilliseconds(PR_IntervalNow())),
66       m_curDstKey(nsMsgKey_None),
67       m_curCopyIndex(0),
68       m_totalMsgCount(0),
69       m_dataBufferSize(0),
70       m_leftOver(0),
71       m_isMove(false),
72       m_dummyEnvelopeNeeded(false),
73       m_fromLineSeen(false),
74       m_writeFailed(false),
75       m_notifyFolderLoaded(false) {}
76 
~nsLocalMailCopyState()77 nsLocalMailCopyState::~nsLocalMailCopyState() {
78   PR_Free(m_dataBuffer);
79   if (m_fileStream) m_fileStream->Close();
80   if (m_messageService) {
81     nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryInterface(m_srcSupport);
82     if (srcFolder && m_message) {
83       nsCString uri;
84       srcFolder->GetUriForMsg(m_message, uri);
85     }
86   }
87 }
88 
nsLocalFolderScanState()89 nsLocalFolderScanState::nsLocalFolderScanState() : m_uidl(nullptr) {}
90 
~nsLocalFolderScanState()91 nsLocalFolderScanState::~nsLocalFolderScanState() {}
92 
93 ///////////////////////////////////////////////////////////////////////////////
94 // nsMsgLocalMailFolder interface
95 ///////////////////////////////////////////////////////////////////////////////
96 
nsMsgLocalMailFolder(void)97 nsMsgLocalMailFolder::nsMsgLocalMailFolder(void)
98     : mCopyState(nullptr),
99       mHaveReadNameFromDB(false),
100       mInitialized(false),
101       mCheckForNewMessagesAfterParsing(false),
102       m_parsingFolder(false),
103       mDownloadState(DOWNLOAD_STATE_NONE) {}
104 
~nsMsgLocalMailFolder(void)105 nsMsgLocalMailFolder::~nsMsgLocalMailFolder(void) {}
106 
NS_IMPL_ISUPPORTS_INHERITED(nsMsgLocalMailFolder,nsMsgDBFolder,nsICopyMessageListener,nsIMsgLocalMailFolder)107 NS_IMPL_ISUPPORTS_INHERITED(nsMsgLocalMailFolder, nsMsgDBFolder,
108                             nsICopyMessageListener, nsIMsgLocalMailFolder)
109 
110 ////////////////////////////////////////////////////////////////////////////////
111 
112 nsresult nsMsgLocalMailFolder::CreateChildFromURI(const nsACString& uri,
113                                                   nsIMsgFolder** folder) {
114   nsMsgLocalMailFolder* newFolder = new nsMsgLocalMailFolder;
115   if (!newFolder) return NS_ERROR_OUT_OF_MEMORY;
116 
117   NS_ADDREF(*folder = newFolder);
118   newFolder->Init(uri);
119   return NS_OK;
120 }
121 
CreateLocalSubfolder(const nsAString & aFolderName,nsIMsgFolder ** aChild)122 NS_IMETHODIMP nsMsgLocalMailFolder::CreateLocalSubfolder(
123     const nsAString& aFolderName, nsIMsgFolder** aChild) {
124   NS_ENSURE_ARG_POINTER(aChild);
125   nsresult rv = CreateSubfolderInternal(aFolderName, nullptr, aChild);
126   NS_ENSURE_SUCCESS(rv, rv);
127 
128   nsCOMPtr<nsIMsgFolderNotificationService> notifier(
129       do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
130   if (notifier) notifier->NotifyFolderAdded(*aChild);
131 
132   return NS_OK;
133 }
134 
GetManyHeadersToDownload(bool * retval)135 NS_IMETHODIMP nsMsgLocalMailFolder::GetManyHeadersToDownload(bool* retval) {
136   bool isLocked;
137   // if the folder is locked, we're probably reparsing - let's build the
138   // view when we've finished reparsing.
139   GetLocked(&isLocked);
140   if (isLocked) {
141     *retval = true;
142     return NS_OK;
143   }
144 
145   return nsMsgDBFolder::GetManyHeadersToDownload(retval);
146 }
147 
148 // run the url to parse the mailbox
ParseFolder(nsIMsgWindow * aMsgWindow,nsIUrlListener * aListener)149 NS_IMETHODIMP nsMsgLocalMailFolder::ParseFolder(nsIMsgWindow* aMsgWindow,
150                                                 nsIUrlListener* aListener) {
151   nsCOMPtr<nsIMsgPluggableStore> msgStore;
152   nsresult rv = GetMsgStore(getter_AddRefs(msgStore));
153   NS_ENSURE_SUCCESS(rv, rv);
154   if (aListener != this) mReparseListener = aListener;
155   // if parsing is synchronous, we need to set m_parsingFolder to
156   // true before starting. And we need to open the db before
157   // setting m_parsingFolder to true.
158   //  OpenDatabase();
159   rv = msgStore->RebuildIndex(this, mDatabase, aMsgWindow, this);
160   if (NS_SUCCEEDED(rv)) m_parsingFolder = true;
161 
162   return rv;
163 }
164 
165 // this won't force a reparse of the folder if the db is invalid.
166 NS_IMETHODIMP
GetMsgDatabase(nsIMsgDatabase ** aMsgDatabase)167 nsMsgLocalMailFolder::GetMsgDatabase(nsIMsgDatabase** aMsgDatabase) {
168   return GetDatabaseWOReparse(aMsgDatabase);
169 }
170 
171 NS_IMETHODIMP
GetSubFolders(nsTArray<RefPtr<nsIMsgFolder>> & folders)172 nsMsgLocalMailFolder::GetSubFolders(nsTArray<RefPtr<nsIMsgFolder>>& folders) {
173   if (!mInitialized) {
174     nsCOMPtr<nsIMsgIncomingServer> server;
175     nsresult rv = GetServer(getter_AddRefs(server));
176     NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
177     nsCOMPtr<nsIMsgPluggableStore> msgStore;
178     // need to set this flag here to avoid infinite recursion
179     mInitialized = true;
180     rv = server->GetMsgStore(getter_AddRefs(msgStore));
181     NS_ENSURE_SUCCESS(rv, rv);
182     // This should add all existing folders as sub-folders of this folder.
183     rv = msgStore->DiscoverSubFolders(this, true);
184     NS_ENSURE_SUCCESS(rv, rv);
185 
186     nsCOMPtr<nsIFile> path;
187     rv = GetFilePath(getter_AddRefs(path));
188     if (NS_FAILED(rv)) return rv;
189 
190     bool directory;
191     path->IsDirectory(&directory);
192     if (directory) {
193       SetFlag(nsMsgFolderFlags::Mail | nsMsgFolderFlags::Elided |
194               nsMsgFolderFlags::Directory);
195 
196       bool isServer;
197       GetIsServer(&isServer);
198       if (isServer) {
199         nsCOMPtr<nsIMsgIncomingServer> server;
200         rv = GetServer(getter_AddRefs(server));
201         NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
202 
203         nsCOMPtr<nsILocalMailIncomingServer> localMailServer;
204         localMailServer = do_QueryInterface(server, &rv);
205         NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
206 
207         // first create the folders on disk (as empty files)
208         rv = localMailServer->CreateDefaultMailboxes();
209         if (NS_FAILED(rv) && rv != NS_MSG_FOLDER_EXISTS) return rv;
210 
211         // must happen after CreateSubFolders, or the folders won't exist.
212         rv = localMailServer->SetFlagsOnDefaultMailboxes();
213         if (NS_FAILED(rv)) return rv;
214       }
215     }
216     UpdateSummaryTotals(false);
217   }
218 
219   return nsMsgDBFolder::GetSubFolders(folders);
220 }
221 
GetDatabase()222 nsresult nsMsgLocalMailFolder::GetDatabase() {
223   nsCOMPtr<nsIMsgDatabase> msgDB;
224   return GetDatabaseWOReparse(getter_AddRefs(msgDB));
225 }
226 
227 // we treat failure as null db returned
GetDatabaseWOReparse(nsIMsgDatabase ** aDatabase)228 NS_IMETHODIMP nsMsgLocalMailFolder::GetDatabaseWOReparse(
229     nsIMsgDatabase** aDatabase) {
230   NS_ENSURE_ARG(aDatabase);
231   if (m_parsingFolder) return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
232 
233   nsresult rv = NS_OK;
234   if (!mDatabase) {
235     rv = OpenDatabase();
236     if (mDatabase) {
237       mDatabase->AddListener(this);
238       UpdateNewMessages();
239     }
240   }
241   NS_IF_ADDREF(*aDatabase = mDatabase);
242   if (mDatabase) mDatabase->SetLastUseTime(PR_Now());
243   return rv;
244 }
245 
246 // Makes sure the database is open and exists.  If the database is out of date,
247 // then this call will return NS_ERROR_NOT_INITIALIZED and run an async url
248 // to reparse the folder. The passed in url listener will get called when the
249 // url is done.
GetDatabaseWithReparse(nsIUrlListener * aReparseUrlListener,nsIMsgWindow * aMsgWindow,nsIMsgDatabase ** aMsgDatabase)250 NS_IMETHODIMP nsMsgLocalMailFolder::GetDatabaseWithReparse(
251     nsIUrlListener* aReparseUrlListener, nsIMsgWindow* aMsgWindow,
252     nsIMsgDatabase** aMsgDatabase) {
253   nsresult rv = NS_OK;
254   // if we're already reparsing, just remember the listener so we can notify it
255   // when we've finished.
256   if (m_parsingFolder) {
257     NS_ASSERTION(!mReparseListener, "can't have an existing listener");
258     mReparseListener = aReparseUrlListener;
259     return NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE;
260   }
261 
262   if (!mDatabase) {
263     nsCOMPtr<nsIFile> pathFile;
264     rv = GetFilePath(getter_AddRefs(pathFile));
265     if (NS_FAILED(rv)) return rv;
266 
267     bool exists;
268     rv = pathFile->Exists(&exists);
269     NS_ENSURE_SUCCESS(rv, rv);
270     if (!exists)
271       return NS_ERROR_NULL_POINTER;  // mDatabase will be null at this point.
272 
273     nsCOMPtr<nsIMsgDBService> msgDBService =
274         do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
275     NS_ENSURE_SUCCESS(rv, rv);
276 
277     nsresult folderOpen =
278         msgDBService->OpenFolderDB(this, true, getter_AddRefs(mDatabase));
279     if (folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE) {
280       nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
281       nsCOMPtr<nsIDBFolderInfo> transferInfo;
282       if (mDatabase) {
283         mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
284         if (dbFolderInfo) {
285           dbFolderInfo->SetNumMessages(0);
286           dbFolderInfo->SetNumUnreadMessages(0);
287           dbFolderInfo->GetTransferInfo(getter_AddRefs(transferInfo));
288         }
289         dbFolderInfo = nullptr;
290 
291         // A backup message database might have been created earlier, for
292         // example if the user requested a reindex. We'll use the earlier one if
293         // we can, otherwise we'll try to backup at this point.
294         if (NS_FAILED(OpenBackupMsgDatabase())) {
295           CloseAndBackupFolderDB(EmptyCString());
296           if (NS_FAILED(OpenBackupMsgDatabase()) && mBackupDatabase) {
297             mBackupDatabase->RemoveListener(this);
298             mBackupDatabase = nullptr;
299           }
300         } else
301           mDatabase->ForceClosed();
302 
303         mDatabase = nullptr;
304       }
305       nsCOMPtr<nsIFile> summaryFile;
306       rv = GetSummaryFileLocation(pathFile, getter_AddRefs(summaryFile));
307       NS_ENSURE_SUCCESS(rv, rv);
308       // Remove summary file.
309       summaryFile->Remove(false);
310 
311       // if it's out of date then reopen with upgrade.
312       rv = msgDBService->CreateNewDB(this, getter_AddRefs(mDatabase));
313       NS_ENSURE_SUCCESS(rv, rv);
314 
315       if (transferInfo && mDatabase) {
316         SetDBTransferInfo(transferInfo);
317         mDatabase->SetSummaryValid(false);
318       }
319     } else if (folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING) {
320       rv = msgDBService->CreateNewDB(this, getter_AddRefs(mDatabase));
321     }
322 
323     if (mDatabase) {
324       if (mAddListener) mDatabase->AddListener(this);
325 
326       // if we have to regenerate the folder, run the parser url.
327       if (folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING ||
328           folderOpen == NS_MSG_ERROR_FOLDER_SUMMARY_OUT_OF_DATE) {
329         if (NS_FAILED(rv = ParseFolder(aMsgWindow, aReparseUrlListener))) {
330           if (rv == NS_MSG_FOLDER_BUSY) {
331             // we need to null out the db so that parsing gets kicked off again.
332             mDatabase->RemoveListener(this);
333             mDatabase = nullptr;
334             ThrowAlertMsg("parsingFolderFailed", aMsgWindow);
335           }
336           return rv;
337         }
338 
339         return NS_ERROR_NOT_INITIALIZED;
340       }
341 
342       // We have a valid database so lets extract necessary info.
343       UpdateSummaryTotals(true);
344     }
345   }
346   NS_IF_ADDREF(*aMsgDatabase = mDatabase);
347   return rv;
348 }
349 
350 NS_IMETHODIMP
UpdateFolder(nsIMsgWindow * aWindow)351 nsMsgLocalMailFolder::UpdateFolder(nsIMsgWindow* aWindow) {
352   (void)RefreshSizeOnDisk();
353   nsresult rv;
354 
355   if (!PromptForMasterPasswordIfNecessary()) return NS_ERROR_FAILURE;
356 
357   // If we don't currently have a database, get it.  Otherwise, the folder has
358   // been updated (presumably this changes when we download headers when opening
359   // inbox).  If it's updated, send NotifyFolderLoaded.
360   if (!mDatabase) {
361     // return of NS_ERROR_NOT_INITIALIZED means running parsing URL
362     // We don't need the return value, and assigning it to mDatabase which
363     // is already set internally leaks.
364     nsCOMPtr<nsIMsgDatabase> returnedDb;
365     rv = GetDatabaseWithReparse(this, aWindow, getter_AddRefs(returnedDb));
366     if (NS_SUCCEEDED(rv)) NotifyFolderEvent(kFolderLoaded);
367   } else {
368     bool valid;
369     rv = mDatabase->GetSummaryValid(&valid);
370     // don't notify folder loaded or try compaction if db isn't valid
371     // (we're probably reparsing or copying msgs to it)
372     if (NS_SUCCEEDED(rv) && valid)
373       NotifyFolderEvent(kFolderLoaded);
374     else if (mCopyState)
375       mCopyState->m_notifyFolderLoaded =
376           true;                 // defer folder loaded notification
377     else if (!m_parsingFolder)  // if the db was already open, it's probably OK
378                                 // to load it if not parsing
379       NotifyFolderEvent(kFolderLoaded);
380   }
381   bool filtersRun;
382   bool hasNewMessages;
383   GetHasNewMessages(&hasNewMessages);
384   if (mDatabase) ApplyRetentionSettings();
385   // if we have new messages, try the filter plugins.
386   if (NS_SUCCEEDED(rv) && hasNewMessages)
387     (void)CallFilterPlugins(aWindow, &filtersRun);
388   // Callers should rely on folder loaded event to ensure completion of loading.
389   // So we'll return NS_OK even if parsing is still in progress
390   if (rv == NS_ERROR_NOT_INITIALIZED) rv = NS_OK;
391   return rv;
392 }
393 
GetFolderURL(nsACString & aUrl)394 NS_IMETHODIMP nsMsgLocalMailFolder::GetFolderURL(nsACString& aUrl) {
395   nsresult rv;
396   nsCOMPtr<nsIFile> path;
397   rv = GetFilePath(getter_AddRefs(path));
398   if (NS_FAILED(rv)) return rv;
399 
400   rv = NS_GetURLSpecFromFile(path, aUrl);
401   NS_ENSURE_SUCCESS(rv, rv);
402 
403   aUrl.Replace(0, strlen("file:"), "mailbox:");
404 
405   return NS_OK;
406 }
407 
CreateStorageIfMissing(nsIUrlListener * aUrlListener)408 NS_IMETHODIMP nsMsgLocalMailFolder::CreateStorageIfMissing(
409     nsIUrlListener* aUrlListener) {
410   nsresult rv = NS_OK;
411   nsCOMPtr<nsIMsgFolder> msgParent;
412   GetParent(getter_AddRefs(msgParent));
413 
414   // parent is probably not set because *this* was probably created by rdf
415   // and not by folder discovery. So, we have to compute the parent.
416   if (!msgParent) {
417     nsAutoCString folderName(mURI);
418     nsAutoCString uri;
419     int32_t leafPos = folderName.RFindChar('/');
420     nsAutoCString parentName(folderName);
421     if (leafPos > 0) {
422       // If there is a hierarchy, there is a parent.
423       // Don't strip off slash if it's the first character
424       parentName.SetLength(leafPos);
425       rv = GetOrCreateFolder(parentName, getter_AddRefs(msgParent));
426       NS_ENSURE_SUCCESS(rv, rv);
427     }
428   }
429 
430   if (msgParent) {
431     nsString folderName;
432     GetName(folderName);
433     rv = msgParent->CreateSubfolder(folderName, nullptr);
434     // by definition, this is OK.
435     if (rv == NS_MSG_FOLDER_EXISTS) return NS_OK;
436   }
437 
438   return rv;
439 }
440 
441 NS_IMETHODIMP
CreateSubfolder(const nsAString & folderName,nsIMsgWindow * msgWindow)442 nsMsgLocalMailFolder::CreateSubfolder(const nsAString& folderName,
443                                       nsIMsgWindow* msgWindow) {
444   nsCOMPtr<nsIMsgFolder> newFolder;
445   nsresult rv =
446       CreateSubfolderInternal(folderName, msgWindow, getter_AddRefs(newFolder));
447   NS_ENSURE_SUCCESS(rv, rv);
448 
449   nsCOMPtr<nsIMsgFolderNotificationService> notifier(
450       do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
451   if (notifier) notifier->NotifyFolderAdded(newFolder);
452 
453   return NS_OK;
454 }
455 
CreateSubfolderInternal(const nsAString & folderName,nsIMsgWindow * msgWindow,nsIMsgFolder ** aNewFolder)456 nsresult nsMsgLocalMailFolder::CreateSubfolderInternal(
457     const nsAString& folderName, nsIMsgWindow* msgWindow,
458     nsIMsgFolder** aNewFolder) {
459   nsresult rv = CheckIfFolderExists(folderName, this, msgWindow);
460   // No need for an assertion: we already throw an alert.
461   if (NS_FAILED(rv)) return rv;
462   nsCOMPtr<nsIMsgPluggableStore> msgStore;
463   rv = GetMsgStore(getter_AddRefs(msgStore));
464   NS_ENSURE_SUCCESS(rv, rv);
465   rv = msgStore->CreateFolder(this, folderName, aNewFolder);
466   if (rv == NS_MSG_ERROR_INVALID_FOLDER_NAME) {
467     ThrowAlertMsg("folderCreationFailed", msgWindow);
468   } else if (rv == NS_MSG_FOLDER_EXISTS) {
469     ThrowAlertMsg("folderExists", msgWindow);
470   }
471 
472   if (NS_SUCCEEDED(rv)) {
473     // we need to notify explicitly the flag change because it failed when we
474     // did AddSubfolder
475     (*aNewFolder)->OnFlagChange(mFlags);
476     (*aNewFolder)
477         ->SetPrettyName(
478             folderName);  // because empty trash will create a new trash folder
479     NotifyItemAdded(*aNewFolder);
480   }
481 
482   return rv;
483 }
484 
CompactAll(nsIUrlListener * aListener,nsIMsgWindow * aMsgWindow,bool aCompactOfflineAlso)485 NS_IMETHODIMP nsMsgLocalMailFolder::CompactAll(nsIUrlListener* aListener,
486                                                nsIMsgWindow* aMsgWindow,
487                                                bool aCompactOfflineAlso) {
488   nsresult rv = NS_OK;
489   nsCOMPtr<nsIMsgFolder> rootFolder;
490   rv = GetRootFolder(getter_AddRefs(rootFolder));
491   nsCOMPtr<nsIMsgPluggableStore> msgStore;
492   GetMsgStore(getter_AddRefs(msgStore));
493   bool storeSupportsCompaction;
494   msgStore->GetSupportsCompaction(&storeSupportsCompaction);
495   if (!storeSupportsCompaction) return NotifyCompactCompleted();
496 
497   nsTArray<RefPtr<nsIMsgFolder>> folderArray;
498   if (NS_SUCCEEDED(rv) && rootFolder) {
499     nsTArray<RefPtr<nsIMsgFolder>> allDescendants;
500     rv = rootFolder->GetDescendants(allDescendants);
501     NS_ENSURE_SUCCESS(rv, rv);
502     int64_t expungedBytes = 0;
503     for (auto folder : allDescendants) {
504       expungedBytes = 0;
505       if (folder) rv = folder->GetExpungedBytes(&expungedBytes);
506 
507       NS_ENSURE_SUCCESS(rv, rv);
508 
509       if (expungedBytes > 0) folderArray.AppendElement(folder);
510     }
511     if (folderArray.IsEmpty()) return NotifyCompactCompleted();
512   }
513   nsCOMPtr<nsIMsgFolderCompactor> folderCompactor =
514       do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
515   NS_ENSURE_SUCCESS(rv, rv);
516   return folderCompactor->CompactFolders(folderArray, {}, aListener,
517                                          aMsgWindow);
518 }
519 
Compact(nsIUrlListener * aListener,nsIMsgWindow * aMsgWindow)520 NS_IMETHODIMP nsMsgLocalMailFolder::Compact(nsIUrlListener* aListener,
521                                             nsIMsgWindow* aMsgWindow) {
522   nsCOMPtr<nsIMsgPluggableStore> msgStore;
523   nsresult rv = GetMsgStore(getter_AddRefs(msgStore));
524   NS_ENSURE_SUCCESS(rv, rv);
525   bool supportsCompaction;
526   msgStore->GetSupportsCompaction(&supportsCompaction);
527   if (!supportsCompaction) {
528     if (aListener) {
529       aListener->OnStopRunningUrl(nullptr, NS_OK);
530     }
531     return NS_OK;
532   }
533   return msgStore->CompactFolder(this, aListener, aMsgWindow);
534 }
535 
EmptyTrash(nsIMsgWindow * msgWindow,nsIUrlListener * aListener)536 NS_IMETHODIMP nsMsgLocalMailFolder::EmptyTrash(nsIMsgWindow* msgWindow,
537                                                nsIUrlListener* aListener) {
538   nsresult rv;
539   nsCOMPtr<nsIMsgFolder> trashFolder;
540   rv = GetTrashFolder(getter_AddRefs(trashFolder));
541   if (NS_SUCCEEDED(rv)) {
542     uint32_t flags;
543     trashFolder->GetFlags(&flags);
544     int32_t totalMessages = 0;
545     rv = trashFolder->GetTotalMessages(true, &totalMessages);
546     if (totalMessages <= 0) {
547       // Any folders to deal with?
548       nsTArray<RefPtr<nsIMsgFolder>> subFolders;
549       rv = trashFolder->GetSubFolders(subFolders);
550       NS_ENSURE_SUCCESS(rv, rv);
551       if (subFolders.IsEmpty()) {
552         return NS_OK;
553       }
554     }
555     nsCOMPtr<nsIMsgFolder> parentFolder;
556     rv = trashFolder->GetParent(getter_AddRefs(parentFolder));
557     if (NS_SUCCEEDED(rv) && parentFolder) {
558       nsCOMPtr<nsIDBFolderInfo> transferInfo;
559       trashFolder->GetDBTransferInfo(getter_AddRefs(transferInfo));
560       trashFolder->SetParent(nullptr);
561       parentFolder->PropagateDelete(trashFolder, true, msgWindow);
562       parentFolder->CreateSubfolder(u"Trash"_ns, nullptr);
563       nsCOMPtr<nsIMsgFolder> newTrashFolder;
564       rv = GetTrashFolder(getter_AddRefs(newTrashFolder));
565       if (NS_SUCCEEDED(rv) && newTrashFolder) {
566         nsCOMPtr<nsIMsgLocalMailFolder> localTrash =
567             do_QueryInterface(newTrashFolder);
568         newTrashFolder->SetDBTransferInfo(transferInfo);
569         if (localTrash) localTrash->RefreshSizeOnDisk();
570         // update the summary totals so the front end will
571         // show the right thing for the new trash folder
572         // see bug #161999
573         nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
574         nsCOMPtr<nsIMsgDatabase> db;
575         newTrashFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo),
576                                              getter_AddRefs(db));
577         if (dbFolderInfo) {
578           dbFolderInfo->SetNumUnreadMessages(0);
579           dbFolderInfo->SetNumMessages(0);
580         }
581         newTrashFolder->UpdateSummaryTotals(true);
582       }
583     }
584   }
585   return rv;
586 }
587 
IsChildOfTrash(bool * result)588 nsresult nsMsgLocalMailFolder::IsChildOfTrash(bool* result) {
589   NS_ENSURE_ARG_POINTER(result);
590   uint32_t parentFlags = 0;
591   *result = false;
592   bool isServer;
593   nsresult rv = GetIsServer(&isServer);
594   if (NS_FAILED(rv) || isServer) return NS_OK;
595 
596   rv = GetFlags(&parentFlags);  // this is the parent folder
597   if (parentFlags & nsMsgFolderFlags::Trash) {
598     *result = true;
599     return rv;
600   }
601 
602   nsCOMPtr<nsIMsgFolder> parentFolder;
603   nsCOMPtr<nsIMsgFolder> thisFolder;
604   rv = QueryInterface(NS_GET_IID(nsIMsgFolder), getter_AddRefs(thisFolder));
605 
606   while (!isServer) {
607     thisFolder->GetParent(getter_AddRefs(parentFolder));
608     if (!parentFolder) return NS_OK;
609 
610     rv = parentFolder->GetIsServer(&isServer);
611     if (NS_FAILED(rv) || isServer) return NS_OK;
612 
613     rv = parentFolder->GetFlags(&parentFlags);
614     if (NS_FAILED(rv)) return NS_OK;
615 
616     if (parentFlags & nsMsgFolderFlags::Trash) {
617       *result = true;
618       return rv;
619     }
620 
621     thisFolder = parentFolder;
622   }
623   return rv;
624 }
625 
DeleteSelf(nsIMsgWindow * msgWindow)626 NS_IMETHODIMP nsMsgLocalMailFolder::DeleteSelf(nsIMsgWindow* msgWindow) {
627   nsresult rv;
628   bool isChildOfTrash;
629   IsChildOfTrash(&isChildOfTrash);
630 
631   uint32_t folderFlags = 0;
632   GetFlags(&folderFlags);
633   // when deleting from trash, or virtual folder, just delete it.
634   if (isChildOfTrash || folderFlags & nsMsgFolderFlags::Virtual)
635     return nsMsgDBFolder::DeleteSelf(msgWindow);
636 
637   nsCOMPtr<nsIMsgFolder> trashFolder;
638   rv = GetTrashFolder(getter_AddRefs(trashFolder));
639   if (NS_SUCCEEDED(rv)) {
640     nsCOMPtr<nsIMsgCopyService> copyService(
641         do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv));
642     NS_ENSURE_SUCCESS(rv, rv);
643     rv = copyService->CopyFolder(this, trashFolder, true, nullptr, msgWindow);
644   }
645   return rv;
646 }
647 
ConfirmFolderDeletion(nsIMsgWindow * aMsgWindow,nsIMsgFolder * aFolder,bool * aResult)648 nsresult nsMsgLocalMailFolder::ConfirmFolderDeletion(nsIMsgWindow* aMsgWindow,
649                                                      nsIMsgFolder* aFolder,
650                                                      bool* aResult) {
651   NS_ENSURE_ARG(aResult);
652   NS_ENSURE_ARG(aMsgWindow);
653   NS_ENSURE_ARG(aFolder);
654   nsCOMPtr<nsIDocShell> docShell;
655   aMsgWindow->GetRootDocShell(getter_AddRefs(docShell));
656   if (docShell) {
657     bool confirmDeletion = true;
658     nsresult rv;
659     nsCOMPtr<nsIPrefBranch> pPrefBranch(
660         do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
661     NS_ENSURE_SUCCESS(rv, rv);
662     pPrefBranch->GetBoolPref("mailnews.confirm.moveFoldersToTrash",
663                              &confirmDeletion);
664     if (confirmDeletion) {
665       nsCOMPtr<nsIStringBundleService> bundleService =
666           mozilla::services::GetStringBundleService();
667       NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
668       nsCOMPtr<nsIStringBundle> bundle;
669       rv = bundleService->CreateBundle(
670           "chrome://messenger/locale/localMsgs.properties",
671           getter_AddRefs(bundle));
672       NS_ENSURE_SUCCESS(rv, rv);
673 
674       nsAutoString folderName;
675       rv = aFolder->GetName(folderName);
676       NS_ENSURE_SUCCESS(rv, rv);
677       AutoTArray<nsString, 1> formatStrings = {folderName};
678 
679       nsAutoString deleteFolderDialogTitle;
680       rv = bundle->GetStringFromName("pop3DeleteFolderDialogTitle",
681                                      deleteFolderDialogTitle);
682       NS_ENSURE_SUCCESS(rv, rv);
683 
684       nsAutoString deleteFolderButtonLabel;
685       rv = bundle->GetStringFromName("pop3DeleteFolderButtonLabel",
686                                      deleteFolderButtonLabel);
687       NS_ENSURE_SUCCESS(rv, rv);
688 
689       nsAutoString confirmationStr;
690       rv = bundle->FormatStringFromName("pop3MoveFolderToTrash", formatStrings,
691                                         confirmationStr);
692       NS_ENSURE_SUCCESS(rv, rv);
693 
694       nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
695       if (dialog) {
696         int32_t buttonPressed = 0;
697         // Default the dialog to "cancel".
698         const uint32_t buttonFlags =
699             (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) +
700             (nsIPrompt::BUTTON_TITLE_CANCEL * nsIPrompt::BUTTON_POS_1);
701         bool dummyValue = false;
702         rv = dialog->ConfirmEx(deleteFolderDialogTitle.get(),
703                                confirmationStr.get(), buttonFlags,
704                                deleteFolderButtonLabel.get(), nullptr, nullptr,
705                                nullptr, &dummyValue, &buttonPressed);
706         NS_ENSURE_SUCCESS(rv, rv);
707         *aResult = !buttonPressed;  // "ok" is in position 0
708       }
709     } else
710       *aResult = true;
711   }
712   return NS_OK;
713 }
714 
Rename(const nsAString & aNewName,nsIMsgWindow * msgWindow)715 NS_IMETHODIMP nsMsgLocalMailFolder::Rename(const nsAString& aNewName,
716                                            nsIMsgWindow* msgWindow) {
717   // Renaming to the same name is easy
718   if (mName.Equals(aNewName)) return NS_OK;
719 
720   nsCOMPtr<nsIMsgFolder> parentFolder;
721   nsresult rv = GetParent(getter_AddRefs(parentFolder));
722   if (!parentFolder) return NS_ERROR_NULL_POINTER;
723 
724   rv = CheckIfFolderExists(aNewName, parentFolder, msgWindow);
725   if (NS_FAILED(rv)) return rv;
726 
727   nsCOMPtr<nsIMsgPluggableStore> msgStore;
728   nsCOMPtr<nsIMsgFolder> newFolder;
729   rv = GetMsgStore(getter_AddRefs(msgStore));
730   NS_ENSURE_SUCCESS(rv, rv);
731   rv = msgStore->RenameFolder(this, aNewName, getter_AddRefs(newFolder));
732   if (NS_FAILED(rv)) {
733     if (msgWindow)
734       (void)ThrowAlertMsg(
735           (rv == NS_MSG_FOLDER_EXISTS) ? "folderExists" : "folderRenameFailed",
736           msgWindow);
737     return rv;
738   }
739 
740   int32_t count = mSubFolders.Count();
741   if (newFolder) {
742     // Because we just renamed the db, w/o setting the pretty name in it,
743     // we need to force the pretty name to be correct.
744     // SetPrettyName won't write the name to the db if it doesn't think the
745     // name has changed. This hack forces the pretty name to get set in the db.
746     // We could set the new pretty name on the db before renaming the .msf file,
747     // but if the rename failed, it would be out of sync.
748     newFolder->SetPrettyName(EmptyString());
749     newFolder->SetPrettyName(aNewName);
750     bool changed = false;
751     MatchOrChangeFilterDestination(newFolder, true /*case-insensitive*/,
752                                    &changed);
753     if (changed) AlertFilterChanged(msgWindow);
754 
755     if (count > 0) newFolder->RenameSubFolders(msgWindow, this);
756 
757     // Discover the subfolders inside this folder (this is recursive)
758     nsTArray<RefPtr<nsIMsgFolder>> dummy;
759     newFolder->GetSubFolders(dummy);
760 
761     // the newFolder should have the same flags
762     newFolder->SetFlags(mFlags);
763     if (parentFolder) {
764       SetParent(nullptr);
765       parentFolder->PropagateDelete(this, false, msgWindow);
766       parentFolder->NotifyItemAdded(newFolder);
767     }
768     // Forget our path, since this folder object renamed itself.
769     SetFilePath(nullptr);
770     newFolder->NotifyFolderEvent(kRenameCompleted);
771 
772     nsCOMPtr<nsIMsgFolderNotificationService> notifier(
773         do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
774     if (notifier) notifier->NotifyFolderRenamed(this, newFolder);
775   }
776   return rv;
777 }
778 
RenameSubFolders(nsIMsgWindow * msgWindow,nsIMsgFolder * oldFolder)779 NS_IMETHODIMP nsMsgLocalMailFolder::RenameSubFolders(nsIMsgWindow* msgWindow,
780                                                      nsIMsgFolder* oldFolder) {
781   nsresult rv = NS_OK;
782   mInitialized = true;
783 
784   uint32_t flags;
785   oldFolder->GetFlags(&flags);
786   SetFlags(flags);
787 
788   nsTArray<RefPtr<nsIMsgFolder>> subFolders;
789   rv = oldFolder->GetSubFolders(subFolders);
790   NS_ENSURE_SUCCESS(rv, rv);
791 
792   for (nsIMsgFolder* msgFolder : subFolders) {
793     nsString folderName;
794     rv = msgFolder->GetName(folderName);
795     nsCOMPtr<nsIMsgFolder> newFolder;
796     AddSubfolder(folderName, getter_AddRefs(newFolder));
797     if (newFolder) {
798       newFolder->SetPrettyName(folderName);
799       bool changed = false;
800       msgFolder->MatchOrChangeFilterDestination(
801           newFolder, true /*case-insensitive*/, &changed);
802       if (changed) msgFolder->AlertFilterChanged(msgWindow);
803       newFolder->RenameSubFolders(msgWindow, msgFolder);
804     }
805   }
806   return NS_OK;
807 }
808 
GetPrettyName(nsAString & prettyName)809 NS_IMETHODIMP nsMsgLocalMailFolder::GetPrettyName(nsAString& prettyName) {
810   return nsMsgDBFolder::GetPrettyName(prettyName);
811 }
812 
SetPrettyName(const nsAString & aName)813 NS_IMETHODIMP nsMsgLocalMailFolder::SetPrettyName(const nsAString& aName) {
814   nsresult rv = nsMsgDBFolder::SetPrettyName(aName);
815   NS_ENSURE_SUCCESS(rv, rv);
816   nsCString folderName;
817   rv = GetStringProperty("folderName", folderName);
818   NS_ConvertUTF16toUTF8 utf8FolderName(mName);
819   return NS_FAILED(rv) || !folderName.Equals(utf8FolderName)
820              ? SetStringProperty("folderName", utf8FolderName)
821              : rv;
822 }
823 
GetName(nsAString & aName)824 NS_IMETHODIMP nsMsgLocalMailFolder::GetName(nsAString& aName) {
825   ReadDBFolderInfo(false);
826   return nsMsgDBFolder::GetName(aName);
827 }
828 
OpenDatabase()829 nsresult nsMsgLocalMailFolder::OpenDatabase() {
830   nsresult rv;
831   nsCOMPtr<nsIMsgDBService> msgDBService =
832       do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
833   NS_ENSURE_SUCCESS(rv, rv);
834 
835   nsCOMPtr<nsIFile> file;
836   rv = GetFilePath(getter_AddRefs(file));
837 
838   rv = msgDBService->OpenFolderDB(this, true, getter_AddRefs(mDatabase));
839   if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING) {
840     // check if we're a real folder by looking at the parent folder.
841     nsCOMPtr<nsIMsgFolder> parent;
842     GetParent(getter_AddRefs(parent));
843     if (parent) {
844       // This little dance creates an empty .msf file and then checks
845       // if the db is valid - this works if the folder is empty, which
846       // we don't have a direct way of checking.
847       nsCOMPtr<nsIMsgDatabase> db;
848       rv = msgDBService->CreateNewDB(this, getter_AddRefs(db));
849       if (db) {
850         UpdateSummaryTotals(true);
851         db->Close(true);
852         mDatabase = nullptr;
853         db = nullptr;
854         rv = msgDBService->OpenFolderDB(this, false, getter_AddRefs(mDatabase));
855         if (NS_FAILED(rv)) mDatabase = nullptr;
856       }
857     }
858   } else if (NS_FAILED(rv))
859     mDatabase = nullptr;
860 
861   return rv;
862 }
863 
864 NS_IMETHODIMP
GetDBFolderInfoAndDB(nsIDBFolderInfo ** folderInfo,nsIMsgDatabase ** db)865 nsMsgLocalMailFolder::GetDBFolderInfoAndDB(nsIDBFolderInfo** folderInfo,
866                                            nsIMsgDatabase** db) {
867   if (!db || !folderInfo || !mPath || mIsServer)
868     return NS_ERROR_NULL_POINTER;  // ducarroz: should we use
869                                    // NS_ERROR_INVALID_ARG?
870 
871   nsresult rv;
872   if (mDatabase)
873     rv = NS_OK;
874   else {
875     rv = OpenDatabase();
876 
877     if (mAddListener && mDatabase) mDatabase->AddListener(this);
878   }
879 
880   NS_IF_ADDREF(*db = mDatabase);
881   if (NS_SUCCEEDED(rv) && *db) rv = (*db)->GetDBFolderInfo(folderInfo);
882   return rv;
883 }
884 
ReadFromFolderCacheElem(nsIMsgFolderCacheElement * element)885 NS_IMETHODIMP nsMsgLocalMailFolder::ReadFromFolderCacheElem(
886     nsIMsgFolderCacheElement* element) {
887   NS_ENSURE_ARG_POINTER(element);
888   nsresult rv = nsMsgDBFolder::ReadFromFolderCacheElem(element);
889   NS_ENSURE_SUCCESS(rv, rv);
890   nsCString utf8Name;
891   rv = element->GetStringProperty("folderName", utf8Name);
892   NS_ENSURE_SUCCESS(rv, rv);
893   CopyUTF8toUTF16(utf8Name, mName);
894   return rv;
895 }
896 
WriteToFolderCacheElem(nsIMsgFolderCacheElement * element)897 NS_IMETHODIMP nsMsgLocalMailFolder::WriteToFolderCacheElem(
898     nsIMsgFolderCacheElement* element) {
899   NS_ENSURE_ARG_POINTER(element);
900   nsMsgDBFolder::WriteToFolderCacheElem(element);
901   return element->SetStringProperty("folderName", NS_ConvertUTF16toUTF8(mName));
902 }
903 
GetDeletable(bool * deletable)904 NS_IMETHODIMP nsMsgLocalMailFolder::GetDeletable(bool* deletable) {
905   NS_ENSURE_ARG_POINTER(deletable);
906 
907   bool isServer;
908   GetIsServer(&isServer);
909   *deletable = !(isServer || (mFlags & nsMsgFolderFlags::SpecialUse));
910   return NS_OK;
911 }
912 
RefreshSizeOnDisk()913 NS_IMETHODIMP nsMsgLocalMailFolder::RefreshSizeOnDisk() {
914   int64_t oldFolderSize = mFolderSize;
915   // we set this to unknown to force it to get recalculated from disk
916   mFolderSize = kSizeUnknown;
917   if (NS_SUCCEEDED(GetSizeOnDisk(&mFolderSize)))
918     NotifyIntPropertyChanged(kFolderSize, oldFolderSize, mFolderSize);
919   return NS_OK;
920 }
921 
GetSizeOnDisk(int64_t * aSize)922 NS_IMETHODIMP nsMsgLocalMailFolder::GetSizeOnDisk(int64_t* aSize) {
923   NS_ENSURE_ARG_POINTER(aSize);
924 
925   bool isServer = false;
926   nsresult rv = GetIsServer(&isServer);
927   // If this is the rootFolder, return 0 as a safe value.
928   if (NS_FAILED(rv) || isServer) mFolderSize = 0;
929 
930   if (mFolderSize == kSizeUnknown) {
931     nsCOMPtr<nsIFile> file;
932     rv = GetFilePath(getter_AddRefs(file));
933     NS_ENSURE_SUCCESS(rv, rv);
934     // Use a temporary variable so that we keep mFolderSize on kSizeUnknown
935     // if GetFileSize() fails.
936     int64_t folderSize;
937     rv = file->GetFileSize(&folderSize);
938     NS_ENSURE_SUCCESS(rv, rv);
939 
940     mFolderSize = folderSize;
941   }
942   *aSize = mFolderSize;
943   return NS_OK;
944 }
945 
GetTrashFolder(nsIMsgFolder ** result)946 nsresult nsMsgLocalMailFolder::GetTrashFolder(nsIMsgFolder** result) {
947   NS_ENSURE_ARG_POINTER(result);
948   nsresult rv;
949   nsCOMPtr<nsIMsgFolder> rootFolder;
950   rv = GetRootFolder(getter_AddRefs(rootFolder));
951   if (NS_SUCCEEDED(rv)) {
952     rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Trash, result);
953     if (!*result) rv = NS_ERROR_FAILURE;
954   }
955   return rv;
956 }
957 
958 NS_IMETHODIMP
DeleteMessages(nsTArray<RefPtr<nsIMsgDBHdr>> const & msgHeaders,nsIMsgWindow * msgWindow,bool deleteStorage,bool isMove,nsIMsgCopyServiceListener * listener,bool allowUndo)959 nsMsgLocalMailFolder::DeleteMessages(
960     nsTArray<RefPtr<nsIMsgDBHdr>> const& msgHeaders, nsIMsgWindow* msgWindow,
961     bool deleteStorage, bool isMove, nsIMsgCopyServiceListener* listener,
962     bool allowUndo) {
963   nsresult rv;
964 
965   // shift delete case - (delete to trash is handled in EndMove)
966   // this is also the case when applying retention settings.
967   if (deleteStorage && !isMove) {
968     MarkMsgsOnPop3Server(msgHeaders, POP3_DELETE);
969   }
970 
971   bool isTrashFolder = mFlags & nsMsgFolderFlags::Trash;
972 
973   // notify on delete from trash and shift-delete
974   if (!isMove && (deleteStorage || isTrashFolder)) {
975     nsCOMPtr<nsIMsgFolderNotificationService> notifier(
976         do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
977     if (notifier) {
978       if (listener) {
979         listener->OnStartCopy();
980         listener->OnStopCopy(NS_OK);
981       }
982       notifier->NotifyMsgsDeleted(msgHeaders);
983     }
984   }
985 
986   if (!deleteStorage && !isTrashFolder) {
987     // We're moving the messages to trash folder. Start by kicking off a copy.
988     nsCOMPtr<nsIMsgFolder> trashFolder;
989     rv = GetTrashFolder(getter_AddRefs(trashFolder));
990     if (NS_SUCCEEDED(rv)) {
991       nsCOMPtr<nsIMsgCopyService> copyService =
992           do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
993       NS_ENSURE_SUCCESS(rv, rv);
994       // When the copy completes, DeleteMessages() will be called again to
995       // perform the actual delete.
996       return copyService->CopyMessages(this, msgHeaders, trashFolder, true,
997                                        listener, msgWindow, allowUndo);
998     }
999   } else {
1000     // Performing an _actual_ delete. There are two ways we got here:
1001     // 1) We're deleting messages without moving to trash.
1002     // 2) We're in the second phase of a Move (to trash or elsewhere). The
1003     //    copy succeeded, and now we need to delete the source messages.
1004     nsCOMPtr<nsIMsgDatabase> msgDB;
1005     rv = GetDatabaseWOReparse(getter_AddRefs(msgDB));
1006     if (NS_SUCCEEDED(rv)) {
1007       if (deleteStorage && isMove && GetDeleteFromServerOnMove())
1008         MarkMsgsOnPop3Server(msgHeaders, POP3_DELETE);
1009 
1010       nsCOMPtr<nsISupports> msgSupport;
1011       rv = EnableNotifications(allMessageCountNotifications, false);
1012       if (NS_SUCCEEDED(rv)) {
1013         // First, delete the actual messages in the store.
1014         nsCOMPtr<nsIMsgPluggableStore> msgStore;
1015         rv = GetMsgStore(getter_AddRefs(msgStore));
1016         if (NS_SUCCEEDED(rv)) {
1017           // Second, remove the message entries from the DB.
1018           rv = msgStore->DeleteMessages(msgHeaders);
1019           for (auto hdr : msgHeaders) {
1020             rv = msgDB->DeleteHeader(hdr, nullptr, false, true);
1021           }
1022         }
1023       } else if (rv == NS_MSG_FOLDER_BUSY) {
1024         ThrowAlertMsg("deletingMsgsFailed", msgWindow);
1025       }
1026 
1027       // Let everyone know the operation has finished.
1028       NotifyFolderEvent(NS_SUCCEEDED(rv) ? kDeleteOrMoveMsgCompleted
1029                                          : kDeleteOrMoveMsgFailed);
1030       // NOTE: This reenabling also forces immediate recount + notification.
1031       EnableNotifications(allMessageCountNotifications, true);
1032       if (msgWindow) {
1033         AutoCompact(msgWindow);
1034       }
1035     }
1036   }
1037 
1038   if (msgWindow && !isMove && (deleteStorage || isTrashFolder)) {
1039     // Clear undo and redo stack.
1040     nsCOMPtr<nsITransactionManager> txnMgr;
1041     msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
1042     if (txnMgr) {
1043       txnMgr->Clear();
1044     }
1045   }
1046   return rv;
1047 }
1048 
1049 NS_IMETHODIMP
AddMessageDispositionState(nsIMsgDBHdr * aMessage,nsMsgDispositionState aDispositionFlag)1050 nsMsgLocalMailFolder::AddMessageDispositionState(
1051     nsIMsgDBHdr* aMessage, nsMsgDispositionState aDispositionFlag) {
1052   nsMsgMessageFlagType msgFlag = 0;
1053   switch (aDispositionFlag) {
1054     case nsIMsgFolder::nsMsgDispositionState_Replied:
1055       msgFlag = nsMsgMessageFlags::Replied;
1056       break;
1057     case nsIMsgFolder::nsMsgDispositionState_Forwarded:
1058       msgFlag = nsMsgMessageFlags::Forwarded;
1059       break;
1060     case nsIMsgFolder::nsMsgDispositionState_Redirected:
1061       msgFlag = nsMsgMessageFlags::Redirected;
1062       break;
1063     default:
1064       return NS_ERROR_UNEXPECTED;
1065   }
1066 
1067   nsresult rv =
1068       nsMsgDBFolder::AddMessageDispositionState(aMessage, aDispositionFlag);
1069   NS_ENSURE_SUCCESS(rv, rv);
1070 
1071   nsCOMPtr<nsIMsgPluggableStore> msgStore;
1072   rv = GetMsgStore(getter_AddRefs(msgStore));
1073   NS_ENSURE_SUCCESS(rv, rv);
1074   return msgStore->ChangeFlags({aMessage}, msgFlag, true);
1075 }
1076 
1077 NS_IMETHODIMP
MarkMessagesRead(const nsTArray<RefPtr<nsIMsgDBHdr>> & aMessages,bool aMarkRead)1078 nsMsgLocalMailFolder::MarkMessagesRead(
1079     const nsTArray<RefPtr<nsIMsgDBHdr>>& aMessages, bool aMarkRead) {
1080   nsresult rv = nsMsgDBFolder::MarkMessagesRead(aMessages, aMarkRead);
1081   NS_ENSURE_SUCCESS(rv, rv);
1082   nsCOMPtr<nsIMsgPluggableStore> msgStore;
1083   rv = GetMsgStore(getter_AddRefs(msgStore));
1084   NS_ENSURE_SUCCESS(rv, rv);
1085   return msgStore->ChangeFlags(aMessages, nsMsgMessageFlags::Read, aMarkRead);
1086 }
1087 
1088 NS_IMETHODIMP
MarkMessagesFlagged(const nsTArray<RefPtr<nsIMsgDBHdr>> & aMessages,bool aMarkFlagged)1089 nsMsgLocalMailFolder::MarkMessagesFlagged(
1090     const nsTArray<RefPtr<nsIMsgDBHdr>>& aMessages, bool aMarkFlagged) {
1091   nsresult rv = nsMsgDBFolder::MarkMessagesFlagged(aMessages, aMarkFlagged);
1092   NS_ENSURE_SUCCESS(rv, rv);
1093   nsCOMPtr<nsIMsgPluggableStore> msgStore;
1094   rv = GetMsgStore(getter_AddRefs(msgStore));
1095   NS_ENSURE_SUCCESS(rv, rv);
1096   return msgStore->ChangeFlags(aMessages, nsMsgMessageFlags::Marked,
1097                                aMarkFlagged);
1098 }
1099 
1100 NS_IMETHODIMP
MarkAllMessagesRead(nsIMsgWindow * aMsgWindow)1101 nsMsgLocalMailFolder::MarkAllMessagesRead(nsIMsgWindow* aMsgWindow) {
1102   nsresult rv = GetDatabase();
1103   NS_ENSURE_SUCCESS(rv, rv);
1104 
1105   nsTArray<nsMsgKey> thoseMarked;
1106   EnableNotifications(allMessageCountNotifications, false);
1107   rv = mDatabase->MarkAllRead(thoseMarked);
1108   EnableNotifications(allMessageCountNotifications, true);
1109   NS_ENSURE_SUCCESS(rv, rv);
1110 
1111   if (thoseMarked.IsEmpty()) {
1112     return NS_OK;
1113   }
1114 
1115   nsTArray<RefPtr<nsIMsgDBHdr>> messages;
1116   rv = MsgGetHeadersFromKeys(mDatabase, thoseMarked, messages);
1117   NS_ENSURE_SUCCESS(rv, rv);
1118 
1119   nsCOMPtr<nsIMsgPluggableStore> msgStore;
1120   rv = GetMsgStore(getter_AddRefs(msgStore));
1121   NS_ENSURE_SUCCESS(rv, rv);
1122 
1123   rv = msgStore->ChangeFlags(messages, nsMsgMessageFlags::Read, true);
1124   NS_ENSURE_SUCCESS(rv, rv);
1125 
1126   mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
1127 
1128   // Setup a undo-state
1129   if (aMsgWindow)
1130     rv = AddMarkAllReadUndoAction(aMsgWindow, thoseMarked.Elements(),
1131                                   thoseMarked.Length());
1132 
1133   return rv;
1134 }
1135 
MarkThreadRead(nsIMsgThread * thread)1136 NS_IMETHODIMP nsMsgLocalMailFolder::MarkThreadRead(nsIMsgThread* thread) {
1137   nsresult rv = GetDatabase();
1138   NS_ENSURE_SUCCESS(rv, rv);
1139 
1140   nsTArray<nsMsgKey> thoseMarked;
1141   rv = mDatabase->MarkThreadRead(thread, nullptr, thoseMarked);
1142   NS_ENSURE_SUCCESS(rv, rv);
1143   if (thoseMarked.IsEmpty()) {
1144     return NS_OK;
1145   }
1146 
1147   nsTArray<RefPtr<nsIMsgDBHdr>> messages;
1148   rv = MsgGetHeadersFromKeys(mDatabase, thoseMarked, messages);
1149   NS_ENSURE_SUCCESS(rv, rv);
1150 
1151   nsCOMPtr<nsIMsgPluggableStore> msgStore;
1152   rv = GetMsgStore(getter_AddRefs(msgStore));
1153   NS_ENSURE_SUCCESS(rv, rv);
1154 
1155   rv = msgStore->ChangeFlags(messages, nsMsgMessageFlags::Read, true);
1156   NS_ENSURE_SUCCESS(rv, rv);
1157 
1158   mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
1159 
1160   return rv;
1161 }
1162 
InitCopyState(nsISupports * aSupport,nsTArray<RefPtr<nsIMsgDBHdr>> const & messages,bool isMove,nsIMsgCopyServiceListener * listener,nsIMsgWindow * msgWindow,bool isFolder,bool allowUndo)1163 nsresult nsMsgLocalMailFolder::InitCopyState(
1164     nsISupports* aSupport, nsTArray<RefPtr<nsIMsgDBHdr>> const& messages,
1165     bool isMove, nsIMsgCopyServiceListener* listener, nsIMsgWindow* msgWindow,
1166     bool isFolder, bool allowUndo) {
1167   nsCOMPtr<nsIFile> path;
1168 
1169   NS_ASSERTION(!mCopyState, "already copying a msg into this folder");
1170   if (mCopyState) return NS_ERROR_FAILURE;  // already has a  copy in progress
1171 
1172   // get mDatabase set, so we can use it to add new hdrs to this db.
1173   // calling GetDatabase will set mDatabase - we use the comptr
1174   // here to avoid doubling the refcnt on mDatabase. We don't care if this
1175   // fails - we just want to give it a chance. It will definitely fail in
1176   // nsLocalMailFolder::EndCopy because we will have written data to the folder
1177   // and changed its size.
1178   nsCOMPtr<nsIMsgDatabase> msgDB;
1179   GetDatabaseWOReparse(getter_AddRefs(msgDB));
1180   bool isLocked;
1181 
1182   GetLocked(&isLocked);
1183   if (isLocked) return NS_MSG_FOLDER_BUSY;
1184 
1185   AcquireSemaphore(static_cast<nsIMsgLocalMailFolder*>(this));
1186 
1187   mCopyState = new nsLocalMailCopyState();
1188   NS_ENSURE_TRUE(mCopyState, NS_ERROR_OUT_OF_MEMORY);
1189 
1190   mCopyState->m_dataBuffer = (char*)PR_CALLOC(COPY_BUFFER_SIZE + 1);
1191   NS_ENSURE_TRUE(mCopyState->m_dataBuffer, NS_ERROR_OUT_OF_MEMORY);
1192 
1193   mCopyState->m_dataBufferSize = COPY_BUFFER_SIZE;
1194   mCopyState->m_destDB = msgDB;
1195 
1196   // Before we continue we should verify that there is enough diskspace.
1197   // XXX How do we do this?
1198   nsresult rv;
1199   mCopyState->m_srcSupport = do_QueryInterface(aSupport, &rv);
1200   NS_ENSURE_SUCCESS(rv, rv);
1201   mCopyState->m_messages = messages.Clone();
1202   mCopyState->m_curCopyIndex = 0;
1203   mCopyState->m_isMove = isMove;
1204   mCopyState->m_isFolder = isFolder;
1205   mCopyState->m_allowUndo = allowUndo;
1206   mCopyState->m_msgWindow = msgWindow;
1207   mCopyState->m_totalMsgCount = messages.Length();
1208   if (listener) mCopyState->m_listener = listener;
1209   mCopyState->m_copyingMultipleMessages = false;
1210   mCopyState->m_wholeMsgInStream = false;
1211 
1212   return NS_OK;
1213 }
1214 
OnAnnouncerGoingAway(nsIDBChangeAnnouncer * instigator)1215 NS_IMETHODIMP nsMsgLocalMailFolder::OnAnnouncerGoingAway(
1216     nsIDBChangeAnnouncer* instigator) {
1217   if (mCopyState) mCopyState->m_destDB = nullptr;
1218   return nsMsgDBFolder::OnAnnouncerGoingAway(instigator);
1219 }
1220 
1221 NS_IMETHODIMP
OnCopyCompleted(nsISupports * srcSupport,bool moveCopySucceeded)1222 nsMsgLocalMailFolder::OnCopyCompleted(nsISupports* srcSupport,
1223                                       bool moveCopySucceeded) {
1224   if (mCopyState && mCopyState->m_notifyFolderLoaded)
1225     NotifyFolderEvent(kFolderLoaded);
1226 
1227   (void)RefreshSizeOnDisk();
1228   // we are the destination folder for a move/copy
1229   bool haveSemaphore;
1230   nsresult rv =
1231       TestSemaphore(static_cast<nsIMsgLocalMailFolder*>(this), &haveSemaphore);
1232   if (NS_SUCCEEDED(rv) && haveSemaphore)
1233     ReleaseSemaphore(static_cast<nsIMsgLocalMailFolder*>(this));
1234 
1235   if (mCopyState && !mCopyState->m_newMsgKeywords.IsEmpty() &&
1236       mCopyState->m_newHdr) {
1237     AddKeywordsToMessages({&*mCopyState->m_newHdr},
1238                           mCopyState->m_newMsgKeywords);
1239   }
1240   if (moveCopySucceeded && mDatabase) {
1241     mDatabase->SetSummaryValid(true);
1242     (void)CloseDBIfFolderNotOpen();
1243   }
1244 
1245   delete mCopyState;
1246   mCopyState = nullptr;
1247   nsCOMPtr<nsIMsgCopyService> copyService =
1248       do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
1249   NS_ENSURE_SUCCESS(rv, rv);
1250   return copyService->NotifyCompletion(
1251       srcSupport, this, moveCopySucceeded ? NS_OK : NS_ERROR_FAILURE);
1252 }
1253 
CheckIfSpaceForCopy(nsIMsgWindow * msgWindow,nsIMsgFolder * srcFolder,nsISupports * srcSupports,bool isMove,int64_t totalMsgSize)1254 bool nsMsgLocalMailFolder::CheckIfSpaceForCopy(nsIMsgWindow* msgWindow,
1255                                                nsIMsgFolder* srcFolder,
1256                                                nsISupports* srcSupports,
1257                                                bool isMove,
1258                                                int64_t totalMsgSize) {
1259   bool spaceNotAvailable = true;
1260   nsresult rv =
1261       WarnIfLocalFileTooBig(msgWindow, totalMsgSize, &spaceNotAvailable);
1262   if (NS_FAILED(rv) || spaceNotAvailable) {
1263     if (isMove && srcFolder)
1264       srcFolder->NotifyFolderEvent(kDeleteOrMoveMsgFailed);
1265     OnCopyCompleted(srcSupports, false);
1266     return false;
1267   }
1268   return true;
1269 }
1270 
1271 NS_IMETHODIMP
CopyMessages(nsIMsgFolder * srcFolder,nsTArray<RefPtr<nsIMsgDBHdr>> const & srcHdrs,bool isMove,nsIMsgWindow * msgWindow,nsIMsgCopyServiceListener * listener,bool isFolder,bool allowUndo)1272 nsMsgLocalMailFolder::CopyMessages(nsIMsgFolder* srcFolder,
1273                                    nsTArray<RefPtr<nsIMsgDBHdr>> const& srcHdrs,
1274                                    bool isMove, nsIMsgWindow* msgWindow,
1275                                    nsIMsgCopyServiceListener* listener,
1276                                    bool isFolder, bool allowUndo) {
1277   nsCOMPtr<nsISupports> srcSupport = do_QueryInterface(srcFolder);
1278   bool isServer;
1279   nsresult rv = GetIsServer(&isServer);
1280   if (NS_SUCCEEDED(rv) && isServer) {
1281     NS_ERROR("Destination is the root folder. Cannot move/copy here");
1282     if (isMove) srcFolder->NotifyFolderEvent(kDeleteOrMoveMsgFailed);
1283     return OnCopyCompleted(srcSupport, false);
1284   }
1285 
1286   UpdateTimestamps(allowUndo);
1287   nsCString protocolType;
1288   rv = srcFolder->GetURI(protocolType);
1289   protocolType.SetLength(protocolType.FindChar(':'));
1290 
1291   // If we're offline and the source folder is imap or news, to do the
1292   // copy the message bodies MUST reside in offline storage.
1293   bool needOfflineBodies =
1294       (WeAreOffline() && (protocolType.LowerCaseEqualsLiteral("imap") ||
1295                           protocolType.LowerCaseEqualsLiteral("news")));
1296   int64_t totalMsgSize = 0;
1297   bool allMsgsHaveOfflineStore = true;
1298   for (auto message : srcHdrs) {
1299     nsMsgKey key;
1300     uint32_t msgSize;
1301     message->GetMessageSize(&msgSize);
1302 
1303     /* 200 is a per-message overhead to account for any extra data added
1304        to the message.
1305     */
1306     totalMsgSize += msgSize + 200;
1307 
1308     // Check if each source folder message has offline storage regardless
1309     // of whether we're online or offline.
1310     message->GetMessageKey(&key);
1311     bool hasMsgOffline = false;
1312     srcFolder->HasMsgOffline(key, &hasMsgOffline);
1313     allMsgsHaveOfflineStore = allMsgsHaveOfflineStore && hasMsgOffline;
1314 
1315     // If we're offline and not all messages are in offline storage, the copy
1316     // or move can't occur and a notification for the user to download the
1317     // messages is posted.
1318     if (needOfflineBodies && !hasMsgOffline) {
1319       if (isMove) srcFolder->NotifyFolderEvent(kDeleteOrMoveMsgFailed);
1320       ThrowAlertMsg("cantMoveMsgWOBodyOffline", msgWindow);
1321       return OnCopyCompleted(srcSupport, false);
1322     }
1323   }
1324 
1325   if (!CheckIfSpaceForCopy(msgWindow, srcFolder, srcSupport, isMove,
1326                            totalMsgSize))
1327     return NS_OK;
1328 
1329   NS_ENSURE_SUCCESS(rv, rv);
1330   bool storeDidCopy = false;
1331   nsCOMPtr<nsIMsgPluggableStore> msgStore;
1332   rv = GetMsgStore(getter_AddRefs(msgStore));
1333   NS_ENSURE_SUCCESS(rv, rv);
1334   nsCOMPtr<nsITransaction> undoTxn;
1335   nsTArray<RefPtr<nsIMsgDBHdr>> dstHdrs;
1336   rv = msgStore->CopyMessages(isMove, srcHdrs, this, listener, dstHdrs,
1337                               getter_AddRefs(undoTxn), &storeDidCopy);
1338   if (storeDidCopy) {
1339     NS_ASSERTION(undoTxn, "if store does copy, it needs to add undo action");
1340     if (msgWindow && undoTxn) {
1341       nsCOMPtr<nsITransactionManager> txnMgr;
1342       msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
1343       if (txnMgr) txnMgr->DoTransaction(undoTxn);
1344     }
1345     if (isMove)
1346       srcFolder->NotifyFolderEvent(NS_SUCCEEDED(rv) ? kDeleteOrMoveMsgCompleted
1347                                                     : kDeleteOrMoveMsgFailed);
1348 
1349     if (NS_SUCCEEDED(rv)) {
1350       // If the store did the copy, like maildir, we need to mark messages on
1351       // the server. Otherwise that's done in EndMove().
1352       nsCOMPtr<nsIMsgLocalMailFolder> localDstFolder;
1353       QueryInterface(NS_GET_IID(nsIMsgLocalMailFolder),
1354                      getter_AddRefs(localDstFolder));
1355       if (localDstFolder) {
1356         // If we are the trash and a local msg is being moved to us, mark the
1357         // source for delete from server, if so configured.
1358         if (mFlags & nsMsgFolderFlags::Trash) {
1359           // If we're deleting on all moves, we'll mark this message for
1360           // deletion when we call DeleteMessages on the source folder. So don't
1361           // mark it for deletion here, in that case.
1362           if (!GetDeleteFromServerOnMove()) {
1363             localDstFolder->MarkMsgsOnPop3Server(dstHdrs, POP3_DELETE);
1364           }
1365         }
1366       }
1367     }
1368     return rv;
1369   }
1370   // If the store doesn't do the copy, we'll stream the source messages into
1371   // the target folder, using getMsgInputStream and getNewMsgOutputStream.
1372 
1373   // don't update the counts in the dest folder until it is all over
1374   EnableNotifications(allMessageCountNotifications, false);
1375 
1376   // sort the message array by key
1377   nsTArray<nsMsgKey> keyArray(srcHdrs.Length());
1378   nsTArray<RefPtr<nsIMsgDBHdr>> sortedMsgs(srcHdrs.Length());
1379   for (nsIMsgDBHdr* aMessage : srcHdrs) {
1380     nsMsgKey key;
1381     aMessage->GetMessageKey(&key);
1382     keyArray.AppendElement(key);
1383   }
1384   keyArray.Sort();
1385   rv = MessagesInKeyOrder(keyArray, srcFolder, sortedMsgs);
1386   NS_ENSURE_SUCCESS(rv, rv);
1387 
1388   rv = InitCopyState(srcSupport, sortedMsgs, isMove, listener, msgWindow,
1389                      isFolder, allowUndo);
1390 
1391   if (NS_FAILED(rv)) {
1392     ThrowAlertMsg("operationFailedFolderBusy", msgWindow);
1393     (void)OnCopyCompleted(srcSupport, false);
1394     return rv;
1395   }
1396 
1397   if (!protocolType.LowerCaseEqualsLiteral("mailbox")) {
1398     mCopyState->m_dummyEnvelopeNeeded = true;
1399     nsParseMailMessageState* parseMsgState = new nsParseMailMessageState();
1400     if (parseMsgState) {
1401       nsCOMPtr<nsIMsgDatabase> msgDb;
1402       mCopyState->m_parseMsgState = parseMsgState;
1403       GetDatabaseWOReparse(getter_AddRefs(msgDb));
1404       if (msgDb) parseMsgState->SetMailDB(msgDb);
1405     }
1406   }
1407 
1408   // undo stuff
1409   if (allowUndo)  // no undo for folder move/copy or or move/copy from search
1410                   // window
1411   {
1412     RefPtr<nsLocalMoveCopyMsgTxn> msgTxn = new nsLocalMoveCopyMsgTxn;
1413     if (msgTxn && NS_SUCCEEDED(msgTxn->Init(srcFolder, this, isMove))) {
1414       msgTxn->SetMsgWindow(msgWindow);
1415       if (isMove) {
1416         if (mFlags & nsMsgFolderFlags::Trash)
1417           msgTxn->SetTransactionType(nsIMessenger::eDeleteMsg);
1418         else
1419           msgTxn->SetTransactionType(nsIMessenger::eMoveMsg);
1420       } else
1421         msgTxn->SetTransactionType(nsIMessenger::eCopyMsg);
1422       msgTxn.swap(mCopyState->m_undoMsgTxn);
1423     }
1424   }
1425 
1426   if (srcHdrs.Length() > 1 &&
1427       ((protocolType.LowerCaseEqualsLiteral("imap") &&
1428         !allMsgsHaveOfflineStore) ||
1429        protocolType.LowerCaseEqualsLiteral("mailbox"))) {
1430     // For an imap source folder with more than one message to be copied that
1431     // are not all in offline storage, this fetches all the messages from the
1432     // imap server to do the copy. When source folder is "mailbox", this is not
1433     // a concern since source messages are in local storage.
1434     mCopyState->m_copyingMultipleMessages = true;
1435     rv = CopyMessagesTo(keyArray, msgWindow, isMove);
1436     if (NS_FAILED(rv)) {
1437       NS_ERROR("copy message failed");
1438       (void)OnCopyCompleted(srcSupport, false);
1439     }
1440   } else {
1441     // This obtains the source messages from local/offline storage to do the
1442     // copy. Note: CopyMessageTo() actually handles one or more messages.
1443     nsIMsgDBHdr* msgSupport = mCopyState->m_messages[0];
1444     if (msgSupport) {
1445       rv = CopyMessageTo(msgSupport, msgWindow, isMove);
1446       if (NS_FAILED(rv)) {
1447         NS_ASSERTION(false, "copy message failed");
1448         (void)OnCopyCompleted(srcSupport, false);
1449       }
1450     }
1451   }
1452   // if this failed immediately, need to turn back on notifications and inform
1453   // FE.
1454   if (NS_FAILED(rv)) {
1455     if (isMove) srcFolder->NotifyFolderEvent(kDeleteOrMoveMsgFailed);
1456     EnableNotifications(allMessageCountNotifications, true);
1457   }
1458   return rv;
1459 }
1460 
1461 // for srcFolder that are on different server than the dstFolder.
1462 // "this" is the parent of the new dest folder.
CopyFolderAcrossServer(nsIMsgFolder * srcFolder,nsIMsgWindow * msgWindow,nsIMsgCopyServiceListener * listener)1463 nsresult nsMsgLocalMailFolder::CopyFolderAcrossServer(
1464     nsIMsgFolder* srcFolder, nsIMsgWindow* msgWindow,
1465     nsIMsgCopyServiceListener* listener) {
1466   mInitialized = true;
1467 
1468   nsString folderName;
1469   srcFolder->GetName(folderName);
1470 
1471   nsCOMPtr<nsIMsgFolder> newMsgFolder;
1472   nsresult rv = CreateSubfolderInternal(folderName, msgWindow,
1473                                         getter_AddRefs(newMsgFolder));
1474   NS_ENSURE_SUCCESS(rv, rv);
1475 
1476   nsCOMPtr<nsIMsgEnumerator> messages;
1477   rv = srcFolder->GetMessages(getter_AddRefs(messages));
1478   NS_ENSURE_SUCCESS(rv, rv);
1479 
1480   nsTArray<RefPtr<nsIMsgDBHdr>> msgArray;
1481   bool hasMoreElements = false;
1482 
1483   if (messages) rv = messages->HasMoreElements(&hasMoreElements);
1484 
1485   while (NS_SUCCEEDED(rv) && hasMoreElements) {
1486     nsCOMPtr<nsIMsgDBHdr> msg;
1487     rv = messages->GetNext(getter_AddRefs(msg));
1488     NS_ENSURE_SUCCESS(rv, rv);
1489 
1490     msgArray.AppendElement(msg);
1491     rv = messages->HasMoreElements(&hasMoreElements);
1492     NS_ENSURE_SUCCESS(rv, rv);
1493   }
1494 
1495   if (msgArray.Length() > 0)  // if only srcFolder has messages..
1496     newMsgFolder->CopyMessages(srcFolder, msgArray, false, msgWindow, listener,
1497                                true /* is folder*/, false /* allowUndo */);
1498   else {
1499     nsCOMPtr<nsIMsgLocalMailFolder> localFolder =
1500         do_QueryInterface(newMsgFolder);
1501     if (localFolder) {
1502       // normally these would get called from ::EndCopy when the last message
1503       // was finished copying. But since there are no messages, we have to call
1504       // them explicitly.
1505       nsCOMPtr<nsISupports> srcSupports = do_QueryInterface(srcFolder);
1506       localFolder->CopyAllSubFolders(srcFolder, msgWindow, listener);
1507       return localFolder->OnCopyCompleted(srcSupports, true);
1508     }
1509   }
1510   return NS_OK;  // otherwise the front-end will say Exception::CopyFolder
1511 }
1512 
1513 nsresult  // copy the sub folders
CopyAllSubFolders(nsIMsgFolder * srcFolder,nsIMsgWindow * msgWindow,nsIMsgCopyServiceListener * listener)1514 nsMsgLocalMailFolder::CopyAllSubFolders(nsIMsgFolder* srcFolder,
1515                                         nsIMsgWindow* msgWindow,
1516                                         nsIMsgCopyServiceListener* listener) {
1517   nsTArray<RefPtr<nsIMsgFolder>> subFolders;
1518   nsresult rv = srcFolder->GetSubFolders(subFolders);
1519   NS_ENSURE_SUCCESS(rv, rv);
1520 
1521   for (nsIMsgFolder* folder : subFolders) {
1522     CopyFolderAcrossServer(folder, msgWindow, listener);
1523   }
1524   return NS_OK;
1525 }
1526 
1527 NS_IMETHODIMP
CopyFolder(nsIMsgFolder * srcFolder,bool isMoveFolder,nsIMsgWindow * msgWindow,nsIMsgCopyServiceListener * listener)1528 nsMsgLocalMailFolder::CopyFolder(nsIMsgFolder* srcFolder, bool isMoveFolder,
1529                                  nsIMsgWindow* msgWindow,
1530                                  nsIMsgCopyServiceListener* listener) {
1531   NS_ENSURE_ARG_POINTER(srcFolder);
1532   // isMoveFolder == true when "this" and srcFolder are on same server
1533   return isMoveFolder
1534              ? CopyFolderLocal(srcFolder, isMoveFolder, msgWindow, listener)
1535              : CopyFolderAcrossServer(srcFolder, msgWindow, listener);
1536 }
1537 
1538 NS_IMETHODIMP
CopyFolderLocal(nsIMsgFolder * srcFolder,bool isMoveFolder,nsIMsgWindow * msgWindow,nsIMsgCopyServiceListener * aListener)1539 nsMsgLocalMailFolder::CopyFolderLocal(nsIMsgFolder* srcFolder,
1540                                       bool isMoveFolder,
1541                                       nsIMsgWindow* msgWindow,
1542                                       nsIMsgCopyServiceListener* aListener) {
1543   mInitialized = true;
1544   bool isChildOfTrash;
1545   nsresult rv = IsChildOfTrash(&isChildOfTrash);
1546   if (NS_SUCCEEDED(rv) && isChildOfTrash) {
1547     // do it just for the parent folder (isMoveFolder is true for parent only)
1548     // if we are deleting/moving a folder tree don't confirm for rss folders.
1549     if (isMoveFolder) {
1550       // if there's a msgWindow, confirm the deletion
1551       if (msgWindow) {
1552         bool okToDelete = false;
1553         ConfirmFolderDeletion(msgWindow, srcFolder, &okToDelete);
1554         if (!okToDelete) return NS_MSG_ERROR_COPY_FOLDER_ABORTED;
1555       }
1556       // if we are moving a favorite folder to trash, we should clear the
1557       // favorites flag so it gets removed from the view.
1558       srcFolder->ClearFlag(nsMsgFolderFlags::Favorite);
1559     }
1560 
1561     bool match = false;
1562     rv = srcFolder->MatchOrChangeFilterDestination(nullptr, false, &match);
1563     if (match && msgWindow) {
1564       bool confirmed = false;
1565       srcFolder->ConfirmFolderDeletionForFilter(msgWindow, &confirmed);
1566       if (!confirmed) return NS_MSG_ERROR_COPY_FOLDER_ABORTED;
1567     }
1568   }
1569 
1570   nsAutoString newFolderName;
1571   nsAutoString folderName;
1572   rv = srcFolder->GetName(folderName);
1573   if (NS_WARN_IF(NS_FAILED(rv))) {
1574     return rv;
1575   }
1576 
1577   if (!isMoveFolder) {
1578     rv = CheckIfFolderExists(folderName, this, msgWindow);
1579     if (NS_WARN_IF(NS_FAILED(rv))) {
1580       return rv;
1581     }
1582   } else {
1583     // If folder name already exists in destination, generate a new unique name.
1584     bool containsChild = true;
1585     uint32_t i;
1586     for (i = 1; containsChild; i++) {
1587       newFolderName.Assign(folderName);
1588       if (i > 1) {
1589         // This could be localizable but Toolkit is fine without it, see
1590         // mozilla/toolkit/content/contentAreaUtils.js::uniqueFile()
1591         newFolderName.Append('(');
1592         newFolderName.AppendInt(i);
1593         newFolderName.Append(')');
1594       }
1595       rv = ContainsChildNamed(newFolderName, &containsChild);
1596       if (NS_WARN_IF(NS_FAILED(rv))) {
1597         return rv;
1598       }
1599     }
1600 
1601     // 'i' is one more than the number of iterations done
1602     // and the number tacked onto the name of the folder.
1603     if (i > 2 && !isChildOfTrash) {
1604       // Folder name already exists, ask if rename is OK.
1605       // If moving to Trash, don't ask and do it.
1606       if (!ConfirmAutoFolderRename(msgWindow, folderName, newFolderName))
1607         return NS_MSG_ERROR_COPY_FOLDER_ABORTED;
1608     }
1609   }
1610 
1611   nsCOMPtr<nsIMsgPluggableStore> msgStore;
1612   rv = GetMsgStore(getter_AddRefs(msgStore));
1613   if (NS_WARN_IF(NS_FAILED(rv))) {
1614     return rv;
1615   }
1616 
1617   return msgStore->CopyFolder(srcFolder, this, isMoveFolder, msgWindow,
1618                               aListener, newFolderName);
1619 }
1620 
1621 NS_IMETHODIMP
CopyFileMessage(nsIFile * aFile,nsIMsgDBHdr * msgToReplace,bool isDraftOrTemplate,uint32_t newMsgFlags,const nsACString & aNewMsgKeywords,nsIMsgWindow * msgWindow,nsIMsgCopyServiceListener * listener)1622 nsMsgLocalMailFolder::CopyFileMessage(nsIFile* aFile, nsIMsgDBHdr* msgToReplace,
1623                                       bool isDraftOrTemplate,
1624                                       uint32_t newMsgFlags,
1625                                       const nsACString& aNewMsgKeywords,
1626                                       nsIMsgWindow* msgWindow,
1627                                       nsIMsgCopyServiceListener* listener) {
1628   NS_ENSURE_ARG_POINTER(aFile);
1629   nsresult rv = NS_ERROR_NULL_POINTER;
1630   nsParseMailMessageState* parseMsgState = nullptr;
1631   int64_t fileSize = 0;
1632 
1633   nsCOMPtr<nsISupports> fileSupport(do_QueryInterface(aFile, &rv));
1634 
1635   aFile->GetFileSize(&fileSize);
1636   if (!CheckIfSpaceForCopy(msgWindow, nullptr, fileSupport, false, fileSize))
1637     return NS_OK;
1638 
1639   nsTArray<RefPtr<nsIMsgDBHdr>> messages;
1640   if (msgToReplace) messages.AppendElement(msgToReplace);
1641 
1642   rv = InitCopyState(fileSupport, messages, msgToReplace ? true : false,
1643                      listener, msgWindow, false, false);
1644   if (NS_SUCCEEDED(rv)) {
1645     if (mCopyState) mCopyState->m_newMsgKeywords = aNewMsgKeywords;
1646 
1647     parseMsgState = new nsParseMailMessageState();
1648     NS_ENSURE_TRUE(parseMsgState, NS_ERROR_OUT_OF_MEMORY);
1649     nsCOMPtr<nsIMsgDatabase> msgDb;
1650     mCopyState->m_parseMsgState = parseMsgState;
1651     GetDatabaseWOReparse(getter_AddRefs(msgDb));
1652     if (msgDb) parseMsgState->SetMailDB(msgDb);
1653 
1654     nsCOMPtr<nsIInputStream> inputStream;
1655     rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), aFile);
1656 
1657     // All or none for adding a message file to the store
1658     if (NS_SUCCEEDED(rv) && fileSize > PR_INT32_MAX)
1659       rv = NS_ERROR_ILLEGAL_VALUE;  // may need error code for max msg size
1660 
1661     if (NS_SUCCEEDED(rv) && inputStream) {
1662       char buffer[5];
1663       uint32_t readCount;
1664       rv = inputStream->Read(buffer, 5, &readCount);
1665       if (NS_SUCCEEDED(rv)) {
1666         if (strncmp(buffer, "From ", 5))
1667           mCopyState->m_dummyEnvelopeNeeded = true;
1668         nsCOMPtr<nsISeekableStream> seekableStream =
1669             do_QueryInterface(inputStream, &rv);
1670         if (NS_SUCCEEDED(rv))
1671           seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
1672       }
1673     }
1674 
1675     mCopyState->m_wholeMsgInStream = true;
1676     if (NS_SUCCEEDED(rv)) rv = BeginCopy(nullptr);
1677 
1678     if (NS_SUCCEEDED(rv)) rv = CopyData(inputStream, (int32_t)fileSize);
1679 
1680     if (NS_SUCCEEDED(rv)) rv = EndCopy(true);
1681 
1682     // mDatabase should have been initialized above - if we got msgDb
1683     // If we were going to delete, here is where we would do it. But because
1684     // existing code already supports doing those deletes, we are just going
1685     // to end the copy.
1686     if (NS_SUCCEEDED(rv) && msgToReplace && mDatabase)
1687       rv = OnCopyCompleted(fileSupport, true);
1688 
1689     if (inputStream) inputStream->Close();
1690   }
1691 
1692   if (NS_FAILED(rv)) (void)OnCopyCompleted(fileSupport, false);
1693 
1694   return rv;
1695 }
1696 
DeleteMessage(nsISupports * message,nsIMsgWindow * msgWindow,bool deleteStorage,bool commit)1697 nsresult nsMsgLocalMailFolder::DeleteMessage(nsISupports* message,
1698                                              nsIMsgWindow* msgWindow,
1699                                              bool deleteStorage, bool commit) {
1700   nsresult rv = NS_OK;
1701   if (deleteStorage) {
1702     nsCOMPtr<nsIMsgDBHdr> msgDBHdr(do_QueryInterface(message, &rv));
1703 
1704     if (NS_SUCCEEDED(rv)) {
1705       GetDatabase();
1706       if (mDatabase)
1707         rv = mDatabase->DeleteHeader(msgDBHdr, nullptr, commit, true);
1708     }
1709   }
1710   return rv;
1711 }
1712 
GetNewMessages(nsIMsgWindow * aWindow,nsIUrlListener * aListener)1713 NS_IMETHODIMP nsMsgLocalMailFolder::GetNewMessages(nsIMsgWindow* aWindow,
1714                                                    nsIUrlListener* aListener) {
1715   nsCOMPtr<nsIMsgIncomingServer> server;
1716   nsresult rv = GetServer(getter_AddRefs(server));
1717   NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
1718 
1719   nsCOMPtr<nsILocalMailIncomingServer> localMailServer =
1720       do_QueryInterface(server, &rv);
1721   NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
1722 
1723   // XXX todo, move all this into nsILocalMailIncomingServer's GetNewMail
1724   // so that we don't have to have RSS foo here.
1725   nsCOMPtr<nsIRssIncomingServer> rssServer = do_QueryInterface(server, &rv);
1726   mozilla::Unused << rssServer;
1727   if (NS_SUCCEEDED(rv))
1728     return localMailServer->GetNewMail(aWindow, aListener, this, nullptr);
1729 
1730   nsCOMPtr<nsIMsgFolder> inbox;
1731   nsCOMPtr<nsIMsgFolder> rootFolder;
1732   rv = server->GetRootMsgFolder(getter_AddRefs(rootFolder));
1733   if (NS_SUCCEEDED(rv) && rootFolder) {
1734     rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox,
1735                                    getter_AddRefs(inbox));
1736   }
1737   nsCOMPtr<nsIMsgLocalMailFolder> localInbox = do_QueryInterface(inbox, &rv);
1738   if (NS_SUCCEEDED(rv)) {
1739     bool valid = false;
1740     nsCOMPtr<nsIMsgDatabase> db;
1741     // this will kick off a reparse if the db is out of date.
1742     rv = localInbox->GetDatabaseWithReparse(nullptr, aWindow,
1743                                             getter_AddRefs(db));
1744     if (NS_SUCCEEDED(rv)) {
1745       db->GetSummaryValid(&valid);
1746       rv = valid
1747                ? localMailServer->GetNewMail(aWindow, aListener, inbox, nullptr)
1748                : localInbox->SetCheckForNewMessagesAfterParsing(true);
1749     }
1750   }
1751   return rv;
1752 }
1753 
WriteStartOfNewMessage()1754 nsresult nsMsgLocalMailFolder::WriteStartOfNewMessage() {
1755   nsCOMPtr<nsISeekableStream> seekableStream =
1756       do_QueryInterface(mCopyState->m_fileStream);
1757   int64_t filePos;
1758   seekableStream->Tell(&filePos);
1759 
1760   // CopyFileMessage() and CopyMessages() from servers other than pop3
1761   if (mCopyState->m_parseMsgState) {
1762     if (mCopyState->m_parseMsgState->m_newMsgHdr)
1763       mCopyState->m_parseMsgState->m_newMsgHdr->GetMessageKey(
1764           &mCopyState->m_curDstKey);
1765     mCopyState->m_parseMsgState->SetEnvelopePos(filePos);
1766     mCopyState->m_parseMsgState->SetState(
1767         nsIMsgParseMailMsgState::ParseHeadersState);
1768   }
1769   if (mCopyState->m_dummyEnvelopeNeeded) {
1770     nsCString result;
1771     nsAutoCString nowStr;
1772     MsgGenerateNowStr(nowStr);
1773     result.AppendLiteral("From - ");
1774     result.Append(nowStr);
1775     result.Append(MSG_LINEBREAK);
1776 
1777     // *** jt - hard code status line for now; come back later
1778     char statusStrBuf[50];
1779     if (mCopyState->m_curCopyIndex < mCopyState->m_messages.Length()) {
1780       uint32_t dbFlags = 0;
1781       mCopyState->m_messages[mCopyState->m_curCopyIndex]->GetFlags(&dbFlags);
1782 
1783       // write out x-mozilla-status, but make sure we don't write out
1784       // nsMsgMessageFlags::Offline
1785       PR_snprintf(
1786           statusStrBuf, sizeof(statusStrBuf),
1787           X_MOZILLA_STATUS_FORMAT MSG_LINEBREAK,
1788           dbFlags &
1789               ~(nsMsgMessageFlags::RuntimeOnly | nsMsgMessageFlags::Offline) &
1790               0x0000FFFF);
1791     } else
1792       strcpy(statusStrBuf, "X-Mozilla-Status: 0001" MSG_LINEBREAK);
1793     uint32_t bytesWritten;
1794     mCopyState->m_fileStream->Write(result.get(), result.Length(),
1795                                     &bytesWritten);
1796     if (mCopyState->m_parseMsgState)
1797       mCopyState->m_parseMsgState->ParseAFolderLine(result.get(),
1798                                                     result.Length());
1799     mCopyState->m_fileStream->Write(statusStrBuf, strlen(statusStrBuf),
1800                                     &bytesWritten);
1801     if (mCopyState->m_parseMsgState)
1802       mCopyState->m_parseMsgState->ParseAFolderLine(statusStrBuf,
1803                                                     strlen(statusStrBuf));
1804     result = "X-Mozilla-Status2: 00000000" MSG_LINEBREAK;
1805     mCopyState->m_fileStream->Write(result.get(), result.Length(),
1806                                     &bytesWritten);
1807     if (mCopyState->m_parseMsgState)
1808       mCopyState->m_parseMsgState->ParseAFolderLine(result.get(),
1809                                                     result.Length());
1810     result = X_MOZILLA_KEYWORDS;
1811     mCopyState->m_fileStream->Write(result.get(), result.Length(),
1812                                     &bytesWritten);
1813     if (mCopyState->m_parseMsgState)
1814       mCopyState->m_parseMsgState->ParseAFolderLine(result.get(),
1815                                                     result.Length());
1816     mCopyState->m_fromLineSeen = true;
1817   } else
1818     mCopyState->m_fromLineSeen = false;
1819 
1820   mCopyState->m_curCopyIndex++;
1821   return NS_OK;
1822 }
1823 
InitCopyMsgHdrAndFileStream()1824 nsresult nsMsgLocalMailFolder::InitCopyMsgHdrAndFileStream() {
1825   nsresult rv = GetMsgStore(getter_AddRefs(mCopyState->m_msgStore));
1826   NS_ENSURE_SUCCESS(rv, rv);
1827   bool reusable;
1828   rv = mCopyState->m_msgStore->GetNewMsgOutputStream(
1829       this, getter_AddRefs(mCopyState->m_newHdr), &reusable,
1830       getter_AddRefs(mCopyState->m_fileStream));
1831   NS_ENSURE_SUCCESS(rv, rv);
1832   if (mCopyState->m_parseMsgState)
1833     mCopyState->m_parseMsgState->SetNewMsgHdr(mCopyState->m_newHdr);
1834   return rv;
1835 }
1836 
1837 // nsICopyMessageListener
BeginCopy(nsIMsgDBHdr * message)1838 NS_IMETHODIMP nsMsgLocalMailFolder::BeginCopy(nsIMsgDBHdr* message) {
1839   if (!mCopyState) return NS_ERROR_NULL_POINTER;
1840 
1841   nsresult rv;
1842   if (!mCopyState->m_copyingMultipleMessages) {
1843     rv = InitCopyMsgHdrAndFileStream();
1844     NS_ENSURE_SUCCESS(rv, rv);
1845   }
1846   nsCOMPtr<nsISeekableStream> seekableStream =
1847       do_QueryInterface(mCopyState->m_fileStream, &rv);
1848 
1849   //  XXX ToDo: When copying multiple messages from a non-offline-enabled IMAP
1850   //  server, this fails. (The copy succeeds because the file stream is created
1851   //  subsequently in StartMessage) We should not be warning on an expected
1852   //  error. Perhaps there are unexpected consequences of returning early?
1853   NS_ENSURE_SUCCESS(rv, rv);
1854   seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
1855 
1856   int32_t messageIndex = (mCopyState->m_copyingMultipleMessages)
1857                              ? mCopyState->m_curCopyIndex - 1
1858                              : mCopyState->m_curCopyIndex;
1859   NS_ASSERTION(!mCopyState->m_copyingMultipleMessages || messageIndex >= 0,
1860                "messageIndex invalid");
1861   // by the time we get here, m_curCopyIndex is 1 relative because
1862   // WriteStartOfNewMessage increments it
1863   if (messageIndex < (int32_t)mCopyState->m_messages.Length()) {
1864     mCopyState->m_message = mCopyState->m_messages[messageIndex];
1865   } else {
1866     mCopyState->m_message = nullptr;
1867   }
1868   // The flags of the source message can get changed when it is deleted, so
1869   // save them here.
1870   if (mCopyState->m_message)
1871     mCopyState->m_message->GetFlags(&(mCopyState->m_flags));
1872   DisplayMoveCopyStatusMsg();
1873   if (mCopyState->m_listener)
1874     mCopyState->m_listener->OnProgress(mCopyState->m_curCopyIndex,
1875                                        mCopyState->m_totalMsgCount);
1876   // if we're copying more than one message, StartMessage will handle this.
1877   return !mCopyState->m_copyingMultipleMessages ? WriteStartOfNewMessage() : rv;
1878 }
1879 
CopyData(nsIInputStream * aIStream,int32_t aLength)1880 NS_IMETHODIMP nsMsgLocalMailFolder::CopyData(nsIInputStream* aIStream,
1881                                              int32_t aLength) {
1882   // check to make sure we have control of the write.
1883   bool haveSemaphore;
1884   nsresult rv = NS_OK;
1885 
1886   rv = TestSemaphore(static_cast<nsIMsgLocalMailFolder*>(this), &haveSemaphore);
1887   if (NS_FAILED(rv)) return rv;
1888   if (!haveSemaphore) return NS_MSG_FOLDER_BUSY;
1889 
1890   if (!mCopyState) return NS_ERROR_OUT_OF_MEMORY;
1891 
1892   uint32_t readCount;
1893   // allocate one extra byte for '\0' at the end and another extra byte at the
1894   // front to insert a '>' if we have a "From" line
1895   // allocate 2 more for crlf that may be needed for those without crlf at end
1896   // of file
1897   if (aLength + mCopyState->m_leftOver + 4 > mCopyState->m_dataBufferSize) {
1898     char* newBuffer = (char*)PR_REALLOC(mCopyState->m_dataBuffer,
1899                                         aLength + mCopyState->m_leftOver + 4);
1900     if (!newBuffer) return NS_ERROR_OUT_OF_MEMORY;
1901 
1902     mCopyState->m_dataBuffer = newBuffer;
1903     mCopyState->m_dataBufferSize = aLength + mCopyState->m_leftOver + 3;
1904   }
1905 
1906   nsCOMPtr<nsISeekableStream> seekableStream =
1907       do_QueryInterface(mCopyState->m_fileStream, &rv);
1908   NS_ENSURE_SUCCESS(rv, rv);
1909   seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
1910 
1911   rv = aIStream->Read(mCopyState->m_dataBuffer + mCopyState->m_leftOver + 1,
1912                       aLength, &readCount);
1913   NS_ENSURE_SUCCESS(rv, rv);
1914   mCopyState->m_leftOver += readCount;
1915   mCopyState->m_dataBuffer[mCopyState->m_leftOver + 1] = '\0';
1916   char* start = mCopyState->m_dataBuffer + 1;
1917   char* endBuffer = mCopyState->m_dataBuffer + mCopyState->m_leftOver + 1;
1918 
1919   uint32_t lineLength;
1920   uint32_t bytesWritten;
1921 
1922   while (1) {
1923     char* end = PL_strnpbrk(start, "\r\n", endBuffer - start);
1924     if (!end) {
1925       mCopyState->m_leftOver -= (start - mCopyState->m_dataBuffer - 1);
1926       // In CopyFileMessage, a complete message is being copied in a single
1927       // call to CopyData, and if it does not have a LINEBREAK at the EOF,
1928       // then end will be null after reading the last line, and we need
1929       // to append the LINEBREAK to the buffer to enable transfer of the last
1930       // line.
1931       if (mCopyState->m_wholeMsgInStream) {
1932         end = start + mCopyState->m_leftOver;
1933         memcpy(end, MSG_LINEBREAK "\0", MSG_LINEBREAK_LEN + 1);
1934       } else {
1935         memmove(mCopyState->m_dataBuffer + 1, start, mCopyState->m_leftOver);
1936         break;
1937       }
1938     }
1939 
1940     // need to set the linebreak_len each time
1941     uint32_t linebreak_len = 1;  // assume CR or LF
1942     if (*end == '\r' && *(end + 1) == '\n') linebreak_len = 2;  // CRLF
1943 
1944     if (!mCopyState->m_fromLineSeen) {
1945       mCopyState->m_fromLineSeen = true;
1946       NS_ASSERTION(strncmp(start, "From ", 5) == 0,
1947                    "Fatal ... bad message format\n");
1948     } else if (strncmp(start, "From ", 5) == 0) {
1949       // if we're at the beginning of the buffer, we've reserved a byte to
1950       // insert a '>'.  If we're in the middle, we're overwriting the previous
1951       // line ending, but we've already written it to m_fileStream, so it's OK.
1952       *--start = '>';
1953     }
1954 
1955     if (!mCopyState->m_fileStream) {
1956       ThrowAlertMsg("copyMsgWriteFailed", mCopyState->m_msgWindow);
1957       mCopyState->m_writeFailed = true;
1958       return NS_ERROR_UNEXPECTED;
1959     }
1960 
1961     lineLength = end - start + linebreak_len;
1962     rv = mCopyState->m_fileStream->Write(start, lineLength, &bytesWritten);
1963     if (bytesWritten != lineLength || NS_FAILED(rv)) {
1964       ThrowAlertMsg("copyMsgWriteFailed", mCopyState->m_msgWindow);
1965       mCopyState->m_writeFailed = true;
1966       return NS_MSG_ERROR_WRITING_MAIL_FOLDER;
1967     }
1968 
1969     if (mCopyState->m_parseMsgState)
1970       mCopyState->m_parseMsgState->ParseAFolderLine(start, lineLength);
1971 
1972     start = end + linebreak_len;
1973     if (start >= endBuffer) {
1974       mCopyState->m_leftOver = 0;
1975       break;
1976     }
1977   }
1978   return rv;
1979 }
1980 
CopyPropertiesToMsgHdr(nsIMsgDBHdr * destHdr,nsIMsgDBHdr * srcHdr,bool aIsMove)1981 void nsMsgLocalMailFolder::CopyPropertiesToMsgHdr(nsIMsgDBHdr* destHdr,
1982                                                   nsIMsgDBHdr* srcHdr,
1983                                                   bool aIsMove) {
1984   nsresult rv;
1985   nsCOMPtr<nsIPrefBranch> prefBranch(
1986       do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
1987   NS_ENSURE_SUCCESS_VOID(rv);
1988 
1989   nsCString dontPreserve;
1990 
1991   // These preferences exist so that extensions can control which properties
1992   // are preserved in the database when a message is moved or copied. All
1993   // properties are preserved except those listed in these preferences
1994   if (aIsMove)
1995     prefBranch->GetCharPref("mailnews.database.summary.dontPreserveOnMove",
1996                             dontPreserve);
1997   else
1998     prefBranch->GetCharPref("mailnews.database.summary.dontPreserveOnCopy",
1999                             dontPreserve);
2000 
2001   CopyHdrPropertiesWithSkipList(destHdr, srcHdr, dontPreserve);
2002 }
2003 
CopyHdrPropertiesWithSkipList(nsIMsgDBHdr * destHdr,nsIMsgDBHdr * srcHdr,const nsCString & skipList)2004 void nsMsgLocalMailFolder::CopyHdrPropertiesWithSkipList(
2005     nsIMsgDBHdr* destHdr, nsIMsgDBHdr* srcHdr, const nsCString& skipList) {
2006   nsCOMPtr<nsIUTF8StringEnumerator> propertyEnumerator;
2007   nsresult rv =
2008       srcHdr->GetPropertyEnumerator(getter_AddRefs(propertyEnumerator));
2009   NS_ENSURE_SUCCESS_VOID(rv);
2010 
2011   // We'll add spaces at beginning and end so we can search for space-name-space
2012   nsCString dontPreserveEx(" "_ns);
2013   dontPreserveEx.Append(skipList);
2014   dontPreserveEx.Append(' ');
2015 
2016   nsAutoCString property;
2017   nsCString sourceString;
2018   bool hasMore;
2019   while (NS_SUCCEEDED(propertyEnumerator->HasMore(&hasMore)) && hasMore) {
2020     propertyEnumerator->GetNext(property);
2021     nsAutoCString propertyEx(" "_ns);
2022     propertyEx.Append(property);
2023     propertyEx.Append(' ');
2024     if (dontPreserveEx.Find(propertyEx) != -1)  // -1 is not found
2025       continue;
2026 
2027     srcHdr->GetStringProperty(property.get(), getter_Copies(sourceString));
2028     destHdr->SetStringProperty(property.get(), sourceString.get());
2029   }
2030 
2031   nsMsgLabelValue label = 0;
2032   srcHdr->GetLabel(&label);
2033   destHdr->SetLabel(label);
2034 }
2035 
2036 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
EndCopy(bool aCopySucceeded)2037 nsMsgLocalMailFolder::EndCopy(bool aCopySucceeded) {
2038   if (!mCopyState) return NS_OK;
2039 
2040   // we are the destination folder for a move/copy
2041   nsresult rv = aCopySucceeded ? NS_OK : NS_ERROR_FAILURE;
2042 
2043   if (!aCopySucceeded || mCopyState->m_writeFailed) {
2044     if (mCopyState->m_fileStream) {
2045       if (mCopyState->m_curDstKey != nsMsgKey_None)
2046         mCopyState->m_msgStore->DiscardNewMessage(mCopyState->m_fileStream,
2047                                                   mCopyState->m_newHdr);
2048       mCopyState->m_fileStream->Close();
2049     }
2050 
2051     if (!mCopyState->m_isMove) {
2052       // passing true because the messages that have been successfully
2053       // copied have their corresponding hdrs in place. The message that has
2054       // failed has been truncated so the msf file and berkeley mailbox
2055       // are in sync.
2056       (void)OnCopyCompleted(mCopyState->m_srcSupport, true);
2057       // enable the dest folder
2058       EnableNotifications(allMessageCountNotifications, true);
2059     }
2060     return NS_OK;
2061   }
2062 
2063   bool multipleCopiesFinished =
2064       (mCopyState->m_curCopyIndex >= mCopyState->m_totalMsgCount);
2065 
2066   RefPtr<nsLocalMoveCopyMsgTxn> localUndoTxn = mCopyState->m_undoMsgTxn;
2067 
2068   NS_ASSERTION(mCopyState->m_leftOver == 0,
2069                "whoops, something wrong with previous copy");
2070   mCopyState->m_leftOver = 0;  // reset to 0.
2071   // need to reset this in case we're move/copying multiple msgs.
2072   mCopyState->m_fromLineSeen = false;
2073 
2074   // flush the copied message. We need a close at the end to get the
2075   // file size and time updated correctly.
2076   //
2077   // These filestream closes are handled inconsistently in the code. In some
2078   // cases, this is done in EndMessage, while in others it is done here in
2079   // EndCopy. When we do the close in EndMessage, we'll set
2080   // mCopyState->m_fileStream to null since it is no longer needed, and detect
2081   // here the null stream so we know that we don't have to close it here.
2082   //
2083   // Similarly, m_parseMsgState->GetNewMsgHdr() returns a null hdr if the hdr
2084   // has already been processed by EndMessage so it is not doubly added here.
2085 
2086   nsCOMPtr<nsISeekableStream> seekableStream(
2087       do_QueryInterface(mCopyState->m_fileStream));
2088   if (seekableStream) {
2089     seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
2090     rv = FinishNewLocalMessage(mCopyState->m_fileStream, mCopyState->m_newHdr,
2091                                mCopyState->m_msgStore,
2092                                mCopyState->m_parseMsgState);
2093     if (NS_SUCCEEDED(rv) && mCopyState->m_newHdr)
2094       mCopyState->m_newHdr->GetMessageKey(&mCopyState->m_curDstKey);
2095     if (multipleCopiesFinished)
2096       mCopyState->m_fileStream->Close();
2097     else
2098       mCopyState->m_fileStream->Flush();
2099   }
2100   // Copy the header to the new database
2101   if (mCopyState->m_message) {
2102     //  CopyMessages() goes here, and CopyFileMessages() with metadata to save;
2103     nsCOMPtr<nsIMsgDBHdr> newHdr;
2104     if (!mCopyState->m_parseMsgState) {
2105       if (mCopyState->m_destDB) {
2106         if (mCopyState->m_newHdr) {
2107           newHdr = mCopyState->m_newHdr;
2108           CopyHdrPropertiesWithSkipList(newHdr, mCopyState->m_message,
2109                                         "storeToken msgOffset"_ns);
2110           //          UpdateNewMsgHdr(mCopyState->m_message, newHdr);
2111           // We need to copy more than just what UpdateNewMsgHdr does. In fact,
2112           // I think we want to copy almost every property other than
2113           // storeToken and msgOffset.
2114           mCopyState->m_destDB->AddNewHdrToDB(newHdr, true);
2115         } else {
2116           rv = mCopyState->m_destDB->CopyHdrFromExistingHdr(
2117               mCopyState->m_curDstKey, mCopyState->m_message, true,
2118               getter_AddRefs(newHdr));
2119         }
2120         uint32_t newHdrFlags;
2121         if (newHdr) {
2122           // turn off offline flag - it's not valid for local mail folders.
2123           newHdr->AndFlags(~nsMsgMessageFlags::Offline, &newHdrFlags);
2124           mCopyState->m_destMessages.AppendElement(newHdr);
2125         }
2126       }
2127       // we can do undo with the dest folder db, see bug #198909
2128       // else
2129       //   mCopyState->m_undoMsgTxn = nullptr;  // null out the transaction
2130       //                                        // because we can't undo w/o
2131       //                                        // the msg db
2132     }
2133 
2134     // if we plan on allowing undo, (if we have a mCopyState->m_parseMsgState or
2135     // not) we need to save the source and dest keys on the undo txn. see bug
2136     // #179856 for details
2137     bool isImap;
2138     if (NS_SUCCEEDED(rv) && localUndoTxn) {
2139       localUndoTxn->GetSrcIsImap(&isImap);
2140       if (!isImap || !mCopyState->m_copyingMultipleMessages) {
2141         nsMsgKey aKey;
2142         uint32_t statusOffset;
2143         mCopyState->m_message->GetMessageKey(&aKey);
2144         mCopyState->m_message->GetStatusOffset(&statusOffset);
2145         localUndoTxn->AddSrcKey(aKey);
2146         localUndoTxn->AddSrcStatusOffset(statusOffset);
2147         localUndoTxn->AddDstKey(mCopyState->m_curDstKey);
2148       }
2149     }
2150   }
2151   nsCOMPtr<nsIMsgDBHdr> newHdr;
2152   // CopyFileMessage() and CopyMessages() from servers other than mailbox
2153   if (mCopyState->m_parseMsgState) {
2154     nsCOMPtr<nsIMsgDatabase> msgDb;
2155     mCopyState->m_parseMsgState->FinishHeader();
2156     GetDatabaseWOReparse(getter_AddRefs(msgDb));
2157     if (msgDb) {
2158       nsresult result =
2159           mCopyState->m_parseMsgState->GetNewMsgHdr(getter_AddRefs(newHdr));
2160       // we need to copy newHdr because mCopyState will get cleared
2161       // in OnCopyCompleted, but we need OnCopyCompleted to know about
2162       // the newHdr, via mCopyState. And we send a notification about newHdr
2163       // after OnCopyCompleted.
2164       mCopyState->m_newHdr = newHdr;
2165       if (NS_SUCCEEDED(result) && newHdr) {
2166         // Copy message metadata.
2167         if (mCopyState->m_message) {
2168           // Propagate the new flag on an imap to local folder filter action
2169           // Flags may get changed when deleting the original source message in
2170           // IMAP. We have a copy of the original flags, but parseMsgState has
2171           // already tried to decide what those flags should be. Who to believe?
2172           // Let's deal here with the flags that might get changed, Read and
2173           // New, and trust upstream code for everything else. However,
2174           // we need to carry over HasRe since the subject is copied over
2175           // from the original.
2176           uint32_t carryOver = nsMsgMessageFlags::New |
2177                                nsMsgMessageFlags::Read |
2178                                nsMsgMessageFlags::HasRe;
2179           uint32_t newFlags;
2180           newHdr->GetFlags(&newFlags);
2181           newHdr->SetFlags((newFlags & ~carryOver) |
2182                            ((mCopyState->m_flags) & carryOver));
2183 
2184           // Copy other message properties.
2185           CopyPropertiesToMsgHdr(newHdr, mCopyState->m_message,
2186                                  mCopyState->m_isMove);
2187         }
2188         msgDb->AddNewHdrToDB(newHdr, true);
2189         if (localUndoTxn) {
2190           // ** jt - recording the message size for possible undo use; the
2191           // message size is different for pop3 and imap4 messages
2192           uint32_t msgSize;
2193           newHdr->GetMessageSize(&msgSize);
2194           localUndoTxn->AddDstMsgSize(msgSize);
2195         }
2196 
2197         mCopyState->m_destMessages.AppendElement(newHdr);
2198       }
2199       // msgDb->SetSummaryValid(true);
2200       // msgDb->Commit(nsMsgDBCommitType::kLargeCommit);
2201     } else
2202       mCopyState->m_undoMsgTxn = nullptr;  // null out the transaction because
2203                                            // we can't undo w/o the msg db
2204 
2205     mCopyState->m_parseMsgState->Clear();
2206     if (mCopyState->m_listener)  // CopyFileMessage() only
2207       mCopyState->m_listener->SetMessageKey(mCopyState->m_curDstKey);
2208   }
2209 
2210   if (!multipleCopiesFinished && !mCopyState->m_copyingMultipleMessages) {
2211     // CopyMessages() goes here; CopyFileMessage() never gets in here because
2212     // curCopyIndex will always be less than the mCopyState->m_totalMsgCount
2213     nsIMsgDBHdr* aSupport = mCopyState->m_messages[mCopyState->m_curCopyIndex];
2214     rv = CopyMessageTo(aSupport, mCopyState->m_msgWindow, mCopyState->m_isMove);
2215   } else {
2216     // If we have some headers, then there is a source, so notify
2217     // itemMoveCopyCompleted. If we don't have any headers already, (eg save as
2218     // draft, send) then notify itemAdded. This notification is done after the
2219     // messages are deleted, so that saving a new draft of a message works
2220     // correctly -- first an itemDeleted is sent for the old draft, then an
2221     // itemAdded for the new draft.
2222     uint32_t numHdrs = mCopyState->m_messages.Length();
2223 
2224     if (multipleCopiesFinished && numHdrs && !mCopyState->m_isFolder) {
2225       // we need to send this notification before we delete the source messages,
2226       // because deleting the source messages clears out the src msg db hdr.
2227       nsCOMPtr<nsIMsgFolderNotificationService> notifier(
2228           do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
2229       if (notifier) {
2230         notifier->NotifyMsgsMoveCopyCompleted(mCopyState->m_isMove,
2231                                               mCopyState->m_messages, this,
2232                                               mCopyState->m_destMessages);
2233       }
2234     }
2235 
2236     if (!mCopyState->m_isMove) {
2237       if (multipleCopiesFinished) {
2238         nsCOMPtr<nsIMsgFolder> srcFolder;
2239         srcFolder = do_QueryInterface(mCopyState->m_srcSupport);
2240         if (mCopyState->m_isFolder)
2241           CopyAllSubFolders(
2242               srcFolder, nullptr,
2243               nullptr);  // Copy all subfolders then notify completion
2244 
2245         if (mCopyState->m_msgWindow && mCopyState->m_undoMsgTxn) {
2246           nsCOMPtr<nsITransactionManager> txnMgr;
2247           mCopyState->m_msgWindow->GetTransactionManager(
2248               getter_AddRefs(txnMgr));
2249           if (txnMgr) {
2250             RefPtr<nsLocalMoveCopyMsgTxn> txn = mCopyState->m_undoMsgTxn;
2251             txnMgr->DoTransaction(txn);
2252           }
2253         }
2254 
2255         // enable the dest folder
2256         EnableNotifications(allMessageCountNotifications, true);
2257         if (srcFolder && !mCopyState->m_isFolder) {
2258           // I'm not too sure of the proper location of this event. It seems to
2259           // need to be after the EnableNotifications, or the folder counts can
2260           // be incorrect during the kDeleteOrMoveMsgCompleted call.
2261           srcFolder->NotifyFolderEvent(kDeleteOrMoveMsgCompleted);
2262         }
2263         (void)OnCopyCompleted(mCopyState->m_srcSupport, true);
2264       }
2265     }
2266     // Send the itemAdded notification in case we didn't send the
2267     // itemMoveCopyCompleted notification earlier. Posting news messages
2268     // involves this, yet doesn't have the newHdr initialized, so don't send any
2269     // notifications in that case.
2270     if (!numHdrs && newHdr) {
2271       nsCOMPtr<nsIMsgFolderNotificationService> notifier(
2272           do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
2273       if (notifier) {
2274         notifier->NotifyMsgAdded(newHdr);
2275         // We do not appear to trigger classification in this case, so let's
2276         // paper over the abyss by just sending the classification notification.
2277         notifier->NotifyMsgsClassified({&*newHdr}, false, false);
2278         // (We do not add the NotReportedClassified processing flag since we
2279         // just reported it!)
2280       }
2281     }
2282   }
2283   return rv;
2284 }
2285 
2286 static bool gGotGlobalPrefs;
2287 static bool gDeleteFromServerOnMove;
2288 
GetDeleteFromServerOnMove()2289 bool nsMsgLocalMailFolder::GetDeleteFromServerOnMove() {
2290   if (!gGotGlobalPrefs) {
2291     nsCOMPtr<nsIPrefBranch> pPrefBranch(
2292         do_GetService(NS_PREFSERVICE_CONTRACTID));
2293     if (pPrefBranch) {
2294       pPrefBranch->GetBoolPref("mail.pop3.deleteFromServerOnMove",
2295                                &gDeleteFromServerOnMove);
2296       gGotGlobalPrefs = true;
2297     }
2298   }
2299   return gDeleteFromServerOnMove;
2300 }
2301 
2302 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHODIMP
EndMove(bool moveSucceeded)2303 nsMsgLocalMailFolder::EndMove(bool moveSucceeded) {
2304   nsresult rv;
2305   if (!mCopyState) return NS_OK;
2306 
2307   if (!moveSucceeded || mCopyState->m_writeFailed) {
2308     // Notify that a completion finished.
2309     nsCOMPtr<nsIMsgFolder> srcFolder =
2310         do_QueryInterface(mCopyState->m_srcSupport, &rv);
2311     NS_ENSURE_SUCCESS(rv, rv);
2312     srcFolder->NotifyFolderEvent(kDeleteOrMoveMsgFailed);
2313 
2314     /* passing true because the messages that have been successfully copied have
2315        their corresponding hdrs in place. The message that has failed has been
2316        truncated so the msf file and berkeley mailbox are in sync*/
2317 
2318     (void)OnCopyCompleted(mCopyState->m_srcSupport, true);
2319     // enable the dest folder
2320     EnableNotifications(allMessageCountNotifications, true);
2321     return NS_OK;
2322   }
2323 
2324   if (mCopyState && mCopyState->m_curCopyIndex >= mCopyState->m_totalMsgCount) {
2325     // Notify that a completion finished.
2326     nsCOMPtr<nsIMsgFolder> srcFolder =
2327         do_QueryInterface(mCopyState->m_srcSupport, &rv);
2328     NS_ENSURE_SUCCESS(rv, rv);
2329     nsCOMPtr<nsIMsgLocalMailFolder> localSrcFolder =
2330         do_QueryInterface(srcFolder);
2331     if (localSrcFolder) {
2332       // if we are the trash and a local msg is being moved to us, mark the
2333       // source for delete from server, if so configured.
2334       if (mFlags & nsMsgFolderFlags::Trash) {
2335         // if we're deleting on all moves, we'll mark this message for deletion
2336         // when we call DeleteMessages on the source folder. So don't mark it
2337         // for deletion here, in that case.
2338         if (!GetDeleteFromServerOnMove()) {
2339           localSrcFolder->MarkMsgsOnPop3Server(mCopyState->m_messages,
2340                                                POP3_DELETE);
2341         }
2342       }
2343     }
2344     // lets delete these all at once - much faster that way
2345     rv = srcFolder->DeleteMessages(mCopyState->m_messages,
2346                                    mCopyState->m_msgWindow, true, true, nullptr,
2347                                    mCopyState->m_allowUndo);
2348     AutoCompact(mCopyState->m_msgWindow);
2349 
2350     // enable the dest folder
2351     EnableNotifications(allMessageCountNotifications, true);
2352     // I'm not too sure of the proper location of this event. It seems to need
2353     // to be after the EnableNotifications, or the folder counts can be
2354     // incorrect during the kDeleteOrMoveMsgCompleted call.
2355     srcFolder->NotifyFolderEvent(NS_SUCCEEDED(rv) ? kDeleteOrMoveMsgCompleted
2356                                                   : kDeleteOrMoveMsgFailed);
2357 
2358     if (NS_SUCCEEDED(rv) && mCopyState->m_msgWindow &&
2359         mCopyState->m_undoMsgTxn) {
2360       nsCOMPtr<nsITransactionManager> txnMgr;
2361       mCopyState->m_msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
2362       if (txnMgr) {
2363         RefPtr<nsLocalMoveCopyMsgTxn> txn = mCopyState->m_undoMsgTxn;
2364         txnMgr->DoTransaction(txn);
2365       }
2366     }
2367     (void)OnCopyCompleted(
2368         mCopyState->m_srcSupport,
2369         NS_SUCCEEDED(rv)
2370             ? true
2371             : false);  // clear the copy state so that the next message from a
2372                        // different folder can be move
2373   }
2374 
2375   return NS_OK;
2376 }
2377 
2378 // this is the beginning of the next message copied
StartMessage()2379 NS_IMETHODIMP nsMsgLocalMailFolder::StartMessage() {
2380   // We get crashes that we don't understand (bug 284876), so stupidly prevent
2381   // that.
2382   NS_ENSURE_ARG_POINTER(mCopyState);
2383   nsresult rv = InitCopyMsgHdrAndFileStream();
2384   NS_ENSURE_SUCCESS(rv, rv);
2385   return WriteStartOfNewMessage();
2386 }
2387 
2388 // just finished the current message.
EndMessage(nsMsgKey key)2389 NS_IMETHODIMP nsMsgLocalMailFolder::EndMessage(nsMsgKey key) {
2390   NS_ENSURE_ARG_POINTER(mCopyState);
2391 
2392   RefPtr<nsLocalMoveCopyMsgTxn> localUndoTxn = mCopyState->m_undoMsgTxn;
2393   nsCOMPtr<nsIMsgWindow> msgWindow;
2394   nsresult rv;
2395 
2396   if (localUndoTxn) {
2397     localUndoTxn->GetMsgWindow(getter_AddRefs(msgWindow));
2398     localUndoTxn->AddSrcKey(key);
2399     localUndoTxn->AddDstKey(mCopyState->m_curDstKey);
2400   }
2401 
2402   // I think this is always true for online to offline copy
2403   mCopyState->m_dummyEnvelopeNeeded = true;
2404   nsCOMPtr<nsISeekableStream> seekableStream =
2405       do_QueryInterface(mCopyState->m_fileStream, &rv);
2406   NS_ENSURE_SUCCESS(rv, rv);
2407   seekableStream->Seek(nsISeekableStream::NS_SEEK_END, 0);
2408   rv = FinishNewLocalMessage(mCopyState->m_fileStream, mCopyState->m_newHdr,
2409                              mCopyState->m_msgStore,
2410                              mCopyState->m_parseMsgState);
2411   mCopyState->m_fileStream->Close();
2412   mCopyState->m_fileStream = nullptr;  // all done with the file stream
2413 
2414   // CopyFileMessage() and CopyMessages() from servers other than mailbox
2415   if (mCopyState->m_parseMsgState) {
2416     nsCOMPtr<nsIMsgDatabase> msgDb;
2417     nsCOMPtr<nsIMsgDBHdr> newHdr;
2418 
2419     mCopyState->m_parseMsgState->FinishHeader();
2420 
2421     rv = mCopyState->m_parseMsgState->GetNewMsgHdr(getter_AddRefs(newHdr));
2422     if (NS_SUCCEEDED(rv) && newHdr) {
2423       nsCOMPtr<nsIMsgFolder> srcFolder =
2424           do_QueryInterface(mCopyState->m_srcSupport, &rv);
2425       NS_ENSURE_SUCCESS(rv, rv);
2426       nsCOMPtr<nsIMsgDatabase> srcDB;
2427       srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
2428       if (srcDB) {
2429         nsCOMPtr<nsIMsgDBHdr> srcMsgHdr;
2430         srcDB->GetMsgHdrForKey(key, getter_AddRefs(srcMsgHdr));
2431         if (srcMsgHdr)
2432           CopyPropertiesToMsgHdr(newHdr, srcMsgHdr, mCopyState->m_isMove);
2433       }
2434       rv = GetDatabaseWOReparse(getter_AddRefs(msgDb));
2435       if (NS_SUCCEEDED(rv) && msgDb) {
2436         msgDb->AddNewHdrToDB(newHdr, true);
2437         if (localUndoTxn) {
2438           // ** jt - recording the message size for possible undo use; the
2439           // message size is different for pop3 and imap4 messages
2440           uint32_t msgSize;
2441           newHdr->GetMessageSize(&msgSize);
2442           localUndoTxn->AddDstMsgSize(msgSize);
2443         }
2444       } else
2445         mCopyState->m_undoMsgTxn = nullptr;  // null out the transaction because
2446                                              // we can't undo w/o the msg db
2447     }
2448     mCopyState->m_parseMsgState->Clear();
2449 
2450     if (mCopyState->m_listener)  // CopyFileMessage() only
2451       mCopyState->m_listener->SetMessageKey(mCopyState->m_curDstKey);
2452   }
2453 
2454   if (mCopyState->m_fileStream) mCopyState->m_fileStream->Flush();
2455   return NS_OK;
2456 }
2457 
CopyMessagesTo(nsTArray<nsMsgKey> & keyArray,nsIMsgWindow * aMsgWindow,bool isMove)2458 nsresult nsMsgLocalMailFolder::CopyMessagesTo(nsTArray<nsMsgKey>& keyArray,
2459                                               nsIMsgWindow* aMsgWindow,
2460                                               bool isMove) {
2461   if (!mCopyState) return NS_ERROR_OUT_OF_MEMORY;
2462 
2463   nsresult rv;
2464 
2465   nsCOMPtr<nsICopyMessageStreamListener> copyStreamListener =
2466       do_CreateInstance(NS_COPYMESSAGESTREAMLISTENER_CONTRACTID, &rv);
2467   NS_ENSURE_SUCCESS(rv, rv);
2468 
2469   nsCOMPtr<nsIMsgFolder> srcFolder(
2470       do_QueryInterface(mCopyState->m_srcSupport, &rv));
2471   NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
2472 
2473   rv = copyStreamListener->Init(srcFolder, this, nullptr);
2474   NS_ENSURE_SUCCESS(rv, rv);
2475 
2476   if (!mCopyState->m_messageService) {
2477     nsCString uri;
2478     srcFolder->GetURI(uri);
2479     rv = GetMessageServiceFromURI(uri,
2480                                   getter_AddRefs(mCopyState->m_messageService));
2481   }
2482 
2483   if (NS_SUCCEEDED(rv) && mCopyState->m_messageService) {
2484     nsCOMPtr<nsIStreamListener> streamListener(
2485         do_QueryInterface(copyStreamListener, &rv));
2486     NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
2487 
2488     mCopyState->m_curCopyIndex = 0;
2489     // we need to kick off the first message - subsequent messages
2490     // are kicked off by nsMailboxProtocol when it finishes a message
2491     // before starting the next message. Only do this if the source folder
2492     // is a local folder, however. IMAP will handle calling StartMessage for
2493     // each message that gets downloaded, and news doesn't go through here
2494     // because news only downloads one message at a time, and this routine
2495     // is for multiple message copy.
2496     nsCOMPtr<nsIMsgLocalMailFolder> srcLocalFolder =
2497         do_QueryInterface(srcFolder);
2498     if (srcLocalFolder) {
2499       StartMessage();
2500     }
2501     nsCOMPtr<nsIURI> dummyNull;
2502     rv = mCopyState->m_messageService->CopyMessages(
2503         keyArray, srcFolder, streamListener, isMove, nullptr, aMsgWindow,
2504         getter_AddRefs(dummyNull));
2505   }
2506   return rv;
2507 }
2508 
CopyMessageTo(nsISupports * message,nsIMsgWindow * aMsgWindow,bool isMove)2509 nsresult nsMsgLocalMailFolder::CopyMessageTo(nsISupports* message,
2510                                              nsIMsgWindow* aMsgWindow,
2511                                              bool isMove) {
2512   if (!mCopyState) return NS_ERROR_OUT_OF_MEMORY;
2513 
2514   nsresult rv;
2515   nsCOMPtr<nsIMsgDBHdr> msgHdr(do_QueryInterface(message, &rv));
2516   NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
2517 
2518   mCopyState->m_message = msgHdr;
2519 
2520   nsCOMPtr<nsIMsgFolder> srcFolder(
2521       do_QueryInterface(mCopyState->m_srcSupport, &rv));
2522   NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
2523   nsCString uri;
2524   srcFolder->GetUriForMsg(msgHdr, uri);
2525 
2526   nsCOMPtr<nsICopyMessageStreamListener> copyStreamListener =
2527       do_CreateInstance(NS_COPYMESSAGESTREAMLISTENER_CONTRACTID, &rv);
2528   NS_ENSURE_SUCCESS(rv, rv);
2529 
2530   rv = copyStreamListener->Init(srcFolder, this, nullptr);
2531   if (NS_FAILED(rv)) return rv;
2532 
2533   if (!mCopyState->m_messageService)
2534     rv = GetMessageServiceFromURI(uri,
2535                                   getter_AddRefs(mCopyState->m_messageService));
2536 
2537   if (NS_SUCCEEDED(rv) && mCopyState->m_messageService) {
2538     nsCOMPtr<nsIStreamListener> streamListener(
2539         do_QueryInterface(copyStreamListener, &rv));
2540     NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
2541     nsCOMPtr<nsIURI> dummyNull;
2542     rv = mCopyState->m_messageService->CopyMessage(uri, streamListener, isMove,
2543                                                    nullptr, aMsgWindow,
2544                                                    getter_AddRefs(dummyNull));
2545   }
2546   return rv;
2547 }
2548 
2549 // A message is being deleted from a POP3 mail file, so check and see if we have
2550 // the message being deleted in the server. If so, then we need to remove the
2551 // message from the server as well. We have saved the UIDL of the message in the
2552 // popstate.dat file and we must match this uidl, so read the message headers
2553 // and see if we have it, then mark the message for deletion from the server.
2554 // The next time we look at mail the message will be deleted from the server.
2555 
2556 NS_IMETHODIMP
MarkMsgsOnPop3Server(const nsTArray<RefPtr<nsIMsgDBHdr>> & aMessages,int32_t aMark)2557 nsMsgLocalMailFolder::MarkMsgsOnPop3Server(
2558     const nsTArray<RefPtr<nsIMsgDBHdr>>& aMessages, int32_t aMark) {
2559   nsLocalFolderScanState folderScanState;
2560   nsCOMPtr<nsIPop3IncomingServer> curFolderPop3MailServer;
2561   nsCOMArray<nsIPop3IncomingServer>
2562       pop3Servers;  // servers with msgs deleted...
2563 
2564   nsCOMPtr<nsIMsgIncomingServer> incomingServer;
2565   nsresult rv = GetServer(getter_AddRefs(incomingServer));
2566   NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
2567 
2568   nsCOMPtr<nsIMsgAccountManager> accountManager =
2569       do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
2570   NS_ENSURE_SUCCESS(rv, rv);
2571 
2572   // I wonder if we should run through the pop3 accounts and see if any of them
2573   // have leave on server set. If not, we could short-circuit some of this.
2574 
2575   curFolderPop3MailServer = do_QueryInterface(incomingServer, &rv);
2576   rv = GetFolderScanState(&folderScanState);
2577   NS_ENSURE_SUCCESS(rv, rv);
2578 
2579   // Filter delete requests are always honored, others are subject
2580   // to the deleteMailLeftOnServer preference.
2581   int32_t mark;
2582   mark = (aMark == POP3_FORCE_DEL) ? POP3_DELETE : aMark;
2583 
2584   for (auto msgDBHdr : aMessages) {
2585     uint32_t flags = 0;
2586     if (msgDBHdr) {
2587       msgDBHdr->GetFlags(&flags);
2588       nsCOMPtr<nsIPop3IncomingServer> msgPop3Server = curFolderPop3MailServer;
2589       bool leaveOnServer = false;
2590       bool deleteMailLeftOnServer = false;
2591       // set up defaults, in case there's no x-mozilla-account header
2592       if (curFolderPop3MailServer) {
2593         curFolderPop3MailServer->GetDeleteMailLeftOnServer(
2594             &deleteMailLeftOnServer);
2595         curFolderPop3MailServer->GetLeaveMessagesOnServer(&leaveOnServer);
2596       }
2597 
2598       rv = GetUidlFromFolder(&folderScanState, msgDBHdr);
2599       if (!NS_SUCCEEDED(rv)) continue;
2600 
2601       if (folderScanState.m_uidl) {
2602         nsCOMPtr<nsIMsgAccount> account;
2603         rv = accountManager->GetAccount(folderScanState.m_accountKey,
2604                                         getter_AddRefs(account));
2605         if (NS_SUCCEEDED(rv) && account) {
2606           account->GetIncomingServer(getter_AddRefs(incomingServer));
2607           nsCOMPtr<nsIPop3IncomingServer> curMsgPop3MailServer =
2608               do_QueryInterface(incomingServer);
2609           if (curMsgPop3MailServer) {
2610             msgPop3Server = curMsgPop3MailServer;
2611             msgPop3Server->GetDeleteMailLeftOnServer(&deleteMailLeftOnServer);
2612             msgPop3Server->GetLeaveMessagesOnServer(&leaveOnServer);
2613           }
2614         }
2615       }
2616       // ignore this header if not partial and leaveOnServer not set...
2617       // or if we can't find the pop3 server.
2618       if (!msgPop3Server ||
2619           (!(flags & nsMsgMessageFlags::Partial) && !leaveOnServer))
2620         continue;
2621       // if marking deleted, ignore header if we're not deleting from
2622       // server when deleting locally.
2623       if (aMark == POP3_DELETE && leaveOnServer && !deleteMailLeftOnServer)
2624         continue;
2625       if (folderScanState.m_uidl) {
2626         msgPop3Server->AddUidlToMark(folderScanState.m_uidl, mark);
2627         // remember this pop server in list of servers with msgs deleted
2628         if (pop3Servers.IndexOfObject(msgPop3Server) == -1)
2629           pop3Servers.AppendObject(msgPop3Server);
2630       }
2631     }
2632   }
2633   if (folderScanState.m_inputStream) folderScanState.m_inputStream->Close();
2634   // need to do this for all pop3 mail servers that had messages deleted.
2635   uint32_t serverCount = pop3Servers.Count();
2636   for (uint32_t index = 0; index < serverCount; index++)
2637     pop3Servers[index]->MarkMessages();
2638 
2639   return rv;
2640 }
2641 
DeleteDownloadMsg(nsIMsgDBHdr * aMsgHdr,bool * aDoSelect)2642 NS_IMETHODIMP nsMsgLocalMailFolder::DeleteDownloadMsg(nsIMsgDBHdr* aMsgHdr,
2643                                                       bool* aDoSelect) {
2644   uint32_t numMsgs;
2645   char* newMsgId;
2646 
2647   // This method is only invoked through DownloadMessagesForOffline()
2648   if (mDownloadState != DOWNLOAD_STATE_NONE) {
2649     // We only remember the first key, no matter how many
2650     // messages were originally selected.
2651     if (mDownloadState == DOWNLOAD_STATE_INITED) {
2652       aMsgHdr->GetMessageKey(&mDownloadSelectKey);
2653       mDownloadState = DOWNLOAD_STATE_GOTMSG;
2654     }
2655 
2656     aMsgHdr->GetMessageId(&newMsgId);
2657 
2658     // Walk through all the selected headers, looking for a matching
2659     // Message-ID.
2660     numMsgs = mDownloadMessages.Length();
2661     for (uint32_t i = 0; i < numMsgs; i++) {
2662       nsresult rv;
2663       nsCOMPtr<nsIMsgDBHdr> msgDBHdr = mDownloadMessages[i];
2664       char* oldMsgId = nullptr;
2665       msgDBHdr->GetMessageId(&oldMsgId);
2666 
2667       // Delete the first match and remove it from the array
2668       if (!PL_strcmp(newMsgId, oldMsgId)) {
2669         rv = GetDatabase();
2670         if (!mDatabase) return rv;
2671 
2672         UpdateNewMsgHdr(msgDBHdr, aMsgHdr);
2673 
2674 #if DOWNLOAD_NOTIFY_STYLE == DOWNLOAD_NOTIFY_LAST
2675         msgDBHdr->GetMessageKey(&mDownloadOldKey);
2676         msgDBHdr->GetThreadParent(&mDownloadOldParent);
2677         msgDBHdr->GetFlags(&mDownloadOldFlags);
2678         mDatabase->DeleteHeader(msgDBHdr, nullptr, false, false);
2679         // Tell caller we want to select this message
2680         if (aDoSelect) *aDoSelect = true;
2681 #else
2682         mDatabase->DeleteHeader(msgDBHdr, nullptr, false, true);
2683         // Tell caller we want to select this message
2684         if (aDoSelect && mDownloadState == DOWNLOAD_STATE_GOTMSG)
2685           *aDoSelect = true;
2686 #endif
2687         mDownloadMessages.RemoveElementAt(i);
2688         break;
2689       }
2690     }
2691   }
2692 
2693   return NS_OK;
2694 }
2695 
SelectDownloadMsg()2696 NS_IMETHODIMP nsMsgLocalMailFolder::SelectDownloadMsg() {
2697 #if DOWNLOAD_NOTIFY_STYLE == DOWNLOAD_NOTIFY_LAST
2698   if (mDownloadState >= DOWNLOAD_STATE_GOTMSG) {
2699     nsresult rv = GetDatabase();
2700     if (!mDatabase) return rv;
2701   }
2702   mDatabase->NotifyKeyDeletedAll(mDownloadOldKey, mDownloadOldParent,
2703                                  mDownloadOldFlags, nullptr);
2704 }
2705 #endif
2706 
2707 if (mDownloadState == DOWNLOAD_STATE_GOTMSG && mDownloadWindow) {
2708   nsAutoCString newuri;
2709   nsBuildLocalMessageURI(mBaseMessageURI, mDownloadSelectKey, newuri);
2710   nsCOMPtr<nsIMsgWindowCommands> windowCommands;
2711   mDownloadWindow->GetWindowCommands(getter_AddRefs(windowCommands));
2712   if (windowCommands) windowCommands->SelectMessage(newuri);
2713   mDownloadState = DOWNLOAD_STATE_DIDSEL;
2714 }
2715 return NS_OK;
2716 }
2717 
2718 NS_IMETHODIMP nsMsgLocalMailFolder::DownloadMessagesForOffline(
2719     nsTArray<RefPtr<nsIMsgDBHdr>> const& aMessages, nsIMsgWindow* aWindow) {
2720   if (mDownloadState != DOWNLOAD_STATE_NONE)
2721     return NS_ERROR_FAILURE;  // already has a download in progress
2722 
2723   // We're starting a download...
2724   mDownloadState = DOWNLOAD_STATE_INITED;
2725 
2726   MarkMsgsOnPop3Server(aMessages, POP3_FETCH_BODY);
2727 
2728   // Pull out all the PARTIAL messages into a new array
2729   nsresult rv;
2730   for (nsIMsgDBHdr* hdr : aMessages) {
2731     uint32_t flags = 0;
2732     hdr->GetFlags(&flags);
2733     if (flags & nsMsgMessageFlags::Partial) {
2734       mDownloadMessages.AppendElement(hdr);
2735     }
2736   }
2737   mDownloadWindow = aWindow;
2738 
2739   nsCOMPtr<nsIMsgIncomingServer> server;
2740   rv = GetServer(getter_AddRefs(server));
2741   NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
2742 
2743   nsCOMPtr<nsILocalMailIncomingServer> localMailServer =
2744       do_QueryInterface(server, &rv);
2745   NS_ENSURE_SUCCESS(rv, NS_MSG_INVALID_OR_MISSING_SERVER);
2746   return localMailServer->GetNewMail(aWindow, this, this, nullptr);
2747 }
2748 
2749 NS_IMETHODIMP nsMsgLocalMailFolder::NotifyDelete() {
2750   NotifyFolderEvent(kDeleteOrMoveMsgCompleted);
2751   return NS_OK;
2752 }
2753 
2754 // TODO:  once we move certain code into the IncomingServer (search for TODO)
2755 // this method will go away.
2756 // sometimes this gets called when we don't have the server yet, so
2757 // that's why we're not calling GetServer()
2758 NS_IMETHODIMP
2759 nsMsgLocalMailFolder::GetIncomingServerType(nsACString& aServerType) {
2760   nsresult rv;
2761   if (mType.IsEmpty()) {
2762     nsCOMPtr<nsIURL> url;
2763     rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
2764              .SetSpec(mURI)
2765              .Finalize(url);
2766     if (NS_FAILED(rv)) return rv;
2767 
2768     nsCOMPtr<nsIMsgAccountManager> accountManager =
2769         do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
2770     if (NS_FAILED(rv)) return rv;
2771 
2772     nsCOMPtr<nsIMsgIncomingServer> server;
2773     // try "none" first
2774     rv = NS_MutateURI(url).SetScheme("none"_ns).Finalize(url);
2775     NS_ENSURE_SUCCESS(rv, rv);
2776     rv = accountManager->FindServerByURI(url, false, getter_AddRefs(server));
2777     if (NS_SUCCEEDED(rv) && server)
2778       mType.AssignLiteral("none");
2779     else {
2780       // next try "pop3"
2781       rv = NS_MutateURI(url).SetScheme("pop3"_ns).Finalize(url);
2782       NS_ENSURE_SUCCESS(rv, rv);
2783       rv = accountManager->FindServerByURI(url, false, getter_AddRefs(server));
2784       if (NS_SUCCEEDED(rv) && server)
2785         mType.AssignLiteral("pop3");
2786       else {
2787         // next try "rss"
2788         rv = NS_MutateURI(url).SetScheme("rss"_ns).Finalize(url);
2789         NS_ENSURE_SUCCESS(rv, rv);
2790         rv =
2791             accountManager->FindServerByURI(url, false, getter_AddRefs(server));
2792         if (NS_SUCCEEDED(rv) && server)
2793           mType.AssignLiteral("rss");
2794         else {
2795         }
2796       }
2797     }
2798   }
2799   aServerType = mType;
2800   return NS_OK;
2801 }
2802 
2803 nsresult nsMsgLocalMailFolder::CreateBaseMessageURI(const nsACString& aURI) {
2804   return nsCreateLocalBaseMessageURI(aURI, mBaseMessageURI);
2805 }
2806 
2807 NS_IMETHODIMP
2808 nsMsgLocalMailFolder::OnStartRunningUrl(nsIURI* aUrl) {
2809   nsresult rv;
2810   nsCOMPtr<nsIPop3URL> popurl = do_QueryInterface(aUrl, &rv);
2811   if (NS_SUCCEEDED(rv)) {
2812     nsAutoCString aSpec;
2813     rv = aUrl->GetSpec(aSpec);
2814     NS_ENSURE_SUCCESS(rv, rv);
2815     if (strstr(aSpec.get(), "uidl=")) {
2816       nsCOMPtr<nsIPop3Sink> popsink;
2817       rv = popurl->GetPop3Sink(getter_AddRefs(popsink));
2818       if (NS_SUCCEEDED(rv)) {
2819         popsink->SetBaseMessageUri(mBaseMessageURI);
2820         nsCString messageuri;
2821         popurl->GetMessageUri(messageuri);
2822         popsink->SetOrigMessageUri(messageuri);
2823       }
2824     }
2825   }
2826   return nsMsgDBFolder::OnStartRunningUrl(aUrl);
2827 }
2828 
2829 NS_IMETHODIMP
2830 nsMsgLocalMailFolder::OnStopRunningUrl(nsIURI* aUrl, nsresult aExitCode) {
2831   // If we just finished a DownloadMessages call, reset...
2832   if (mDownloadState != DOWNLOAD_STATE_NONE) {
2833     mDownloadState = DOWNLOAD_STATE_NONE;
2834     mDownloadMessages.Clear();
2835     mDownloadWindow = nullptr;
2836     return nsMsgDBFolder::OnStopRunningUrl(aUrl, aExitCode);
2837   }
2838 
2839   nsresult rv;
2840   if (NS_SUCCEEDED(aExitCode)) {
2841     nsCOMPtr<nsIMsgMailSession> mailSession =
2842         do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
2843     NS_ENSURE_SUCCESS(rv, rv);
2844     nsCOMPtr<nsIMsgWindow> msgWindow;
2845     rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
2846     nsAutoCString aSpec;
2847     if (aUrl) {
2848       rv = aUrl->GetSpec(aSpec);
2849       NS_ENSURE_SUCCESS(rv, rv);
2850     }
2851 
2852     if (strstr(aSpec.get(), "uidl=")) {
2853       nsCOMPtr<nsIPop3URL> popurl = do_QueryInterface(aUrl, &rv);
2854       if (NS_SUCCEEDED(rv)) {
2855         nsCString messageuri;
2856         rv = popurl->GetMessageUri(messageuri);
2857         if (NS_SUCCEEDED(rv)) {
2858           NS_ENSURE_SUCCESS(rv, rv);
2859           nsCOMPtr<nsIMsgDBHdr> msgDBHdr;
2860           rv = GetMsgDBHdrFromURI(messageuri, getter_AddRefs(msgDBHdr));
2861           if (NS_SUCCEEDED(rv)) {
2862             GetDatabase();
2863             if (mDatabase)
2864               mDatabase->DeleteHeader(msgDBHdr, nullptr, true, true);
2865           }
2866 
2867           nsCOMPtr<nsIPop3Sink> pop3sink;
2868           nsCString newMessageUri;
2869           rv = popurl->GetPop3Sink(getter_AddRefs(pop3sink));
2870           if (NS_SUCCEEDED(rv)) {
2871             pop3sink->GetMessageUri(newMessageUri);
2872             if (msgWindow) {
2873               nsCOMPtr<nsIMsgWindowCommands> windowCommands;
2874               msgWindow->GetWindowCommands(getter_AddRefs(windowCommands));
2875               if (windowCommands) windowCommands->SelectMessage(newMessageUri);
2876             }
2877           }
2878         }
2879       }
2880     }
2881 
2882     if (mFlags & nsMsgFolderFlags::Inbox) {
2883       if (mDatabase && mCheckForNewMessagesAfterParsing) {
2884         bool valid =
2885             false;  // GetSummaryValid may return without setting valid.
2886         mDatabase->GetSummaryValid(&valid);
2887         if (valid && msgWindow) rv = GetNewMessages(msgWindow, nullptr);
2888         mCheckForNewMessagesAfterParsing = false;
2889       }
2890     }
2891   }
2892 
2893   if (m_parsingFolder) {
2894     // Clear this before calling OnStopRunningUrl, in case the url listener
2895     // tries to get the database.
2896     m_parsingFolder = false;
2897 
2898     // TODO: Updating the size should be pushed down into the msg store backend
2899     // so that the size is recalculated as part of parsing the folder data
2900     // (important for maildir), once GetSizeOnDisk is pushed into the msgStores
2901     // (bug 1032360).
2902     (void)RefreshSizeOnDisk();
2903 
2904     // Update the summary totals so the front end will
2905     // show the right thing.
2906     UpdateSummaryTotals(true);
2907 
2908     if (mReparseListener) {
2909       nsCOMPtr<nsIUrlListener> saveReparseListener = mReparseListener;
2910       mReparseListener = nullptr;
2911       saveReparseListener->OnStopRunningUrl(aUrl, aExitCode);
2912     }
2913   }
2914   if (mFlags & nsMsgFolderFlags::Inbox) {
2915     // if we are the inbox and running pop url
2916     nsCOMPtr<nsIPop3URL> popurl = do_QueryInterface(aUrl, &rv);
2917     mozilla::Unused << popurl;
2918     if (NS_SUCCEEDED(rv)) {
2919       nsCOMPtr<nsIMsgIncomingServer> server;
2920       GetServer(getter_AddRefs(server));
2921       // this is the deferred to account, in the global inbox case
2922       if (server) server->SetPerformingBiff(false);  // biff is over
2923     }
2924   }
2925   return nsMsgDBFolder::OnStopRunningUrl(aUrl, aExitCode);
2926 }
2927 
2928 nsresult nsMsgLocalMailFolder::DisplayMoveCopyStatusMsg() {
2929   nsresult rv = NS_OK;
2930   if (mCopyState) {
2931     if (!mCopyState->m_statusFeedback) {
2932       // get msgWindow from undo txn
2933       nsCOMPtr<nsIMsgWindow> msgWindow;
2934       if (mCopyState->m_undoMsgTxn)
2935         mCopyState->m_undoMsgTxn->GetMsgWindow(getter_AddRefs(msgWindow));
2936       if (!msgWindow) return NS_OK;  // not a fatal error.
2937 
2938       msgWindow->GetStatusFeedback(
2939           getter_AddRefs(mCopyState->m_statusFeedback));
2940     }
2941 
2942     if (!mCopyState->m_stringBundle) {
2943       nsCOMPtr<nsIStringBundleService> bundleService =
2944           mozilla::services::GetStringBundleService();
2945       NS_ENSURE_TRUE(bundleService, NS_ERROR_UNEXPECTED);
2946       rv = bundleService->CreateBundle(
2947           "chrome://messenger/locale/localMsgs.properties",
2948           getter_AddRefs(mCopyState->m_stringBundle));
2949       NS_ENSURE_SUCCESS(rv, rv);
2950     }
2951     if (mCopyState->m_statusFeedback && mCopyState->m_stringBundle) {
2952       nsString folderName;
2953       GetName(folderName);
2954       nsAutoString numMsgSoFarString;
2955       numMsgSoFarString.AppendInt((mCopyState->m_copyingMultipleMessages)
2956                                       ? mCopyState->m_curCopyIndex
2957                                       : 1);
2958 
2959       nsAutoString totalMessagesString;
2960       totalMessagesString.AppendInt(mCopyState->m_totalMsgCount);
2961       nsString finalString;
2962       AutoTArray<nsString, 3> stringArray = {numMsgSoFarString,
2963                                              totalMessagesString, folderName};
2964       rv = mCopyState->m_stringBundle->FormatStringFromName(
2965           (mCopyState->m_isMove) ? "movingMessagesStatus"
2966                                  : "copyingMessagesStatus",
2967           stringArray, finalString);
2968       int64_t nowMS = PR_IntervalToMilliseconds(PR_IntervalNow());
2969 
2970       // only update status/progress every half second
2971       if (nowMS - mCopyState->m_lastProgressTime < 500 &&
2972           mCopyState->m_curCopyIndex < mCopyState->m_totalMsgCount)
2973         return NS_OK;
2974 
2975       mCopyState->m_lastProgressTime = nowMS;
2976       mCopyState->m_statusFeedback->ShowStatusString(finalString);
2977       mCopyState->m_statusFeedback->ShowProgress(
2978           mCopyState->m_curCopyIndex * 100 / mCopyState->m_totalMsgCount);
2979     }
2980   }
2981   return rv;
2982 }
2983 
2984 NS_IMETHODIMP
2985 nsMsgLocalMailFolder::SetFlagsOnDefaultMailboxes(uint32_t flags) {
2986   if (flags & nsMsgFolderFlags::Inbox)
2987     setSubfolderFlag(u"Inbox"_ns, nsMsgFolderFlags::Inbox);
2988 
2989   if (flags & nsMsgFolderFlags::SentMail)
2990     setSubfolderFlag(u"Sent"_ns, nsMsgFolderFlags::SentMail);
2991 
2992   if (flags & nsMsgFolderFlags::Drafts)
2993     setSubfolderFlag(u"Drafts"_ns, nsMsgFolderFlags::Drafts);
2994 
2995   if (flags & nsMsgFolderFlags::Templates)
2996     setSubfolderFlag(u"Templates"_ns, nsMsgFolderFlags::Templates);
2997 
2998   if (flags & nsMsgFolderFlags::Trash)
2999     setSubfolderFlag(u"Trash"_ns, nsMsgFolderFlags::Trash);
3000 
3001   if (flags & nsMsgFolderFlags::Queue)
3002     setSubfolderFlag(u"Unsent Messages"_ns, nsMsgFolderFlags::Queue);
3003 
3004   if (flags & nsMsgFolderFlags::Junk)
3005     setSubfolderFlag(u"Junk"_ns, nsMsgFolderFlags::Junk);
3006 
3007   if (flags & nsMsgFolderFlags::Archive)
3008     setSubfolderFlag(u"Archives"_ns, nsMsgFolderFlags::Archive);
3009 
3010   return NS_OK;
3011 }
3012 
3013 nsresult nsMsgLocalMailFolder::setSubfolderFlag(const nsAString& aFolderName,
3014                                                 uint32_t flags) {
3015   // FindSubFolder() expects the folder name to be escaped
3016   // see bug #192043
3017   nsAutoCString escapedFolderName;
3018   nsresult rv = NS_MsgEscapeEncodeURLPath(aFolderName, escapedFolderName);
3019   NS_ENSURE_SUCCESS(rv, rv);
3020   nsCOMPtr<nsIMsgFolder> msgFolder;
3021   rv = FindSubFolder(escapedFolderName, getter_AddRefs(msgFolder));
3022   NS_ENSURE_SUCCESS(rv, rv);
3023 
3024   // we only want to do this if the folder *really* exists,
3025   // so check if it has a parent. Otherwise, we'll create the
3026   // .msf file when we don't want to.
3027   nsCOMPtr<nsIMsgFolder> parent;
3028   msgFolder->GetParent(getter_AddRefs(parent));
3029   if (!parent) return NS_ERROR_FAILURE;
3030 
3031   rv = msgFolder->SetFlag(flags);
3032   NS_ENSURE_SUCCESS(rv, rv);
3033   return msgFolder->SetPrettyName(aFolderName);
3034 }
3035 
3036 NS_IMETHODIMP
3037 nsMsgLocalMailFolder::GetCheckForNewMessagesAfterParsing(
3038     bool* aCheckForNewMessagesAfterParsing) {
3039   NS_ENSURE_ARG_POINTER(aCheckForNewMessagesAfterParsing);
3040   *aCheckForNewMessagesAfterParsing = mCheckForNewMessagesAfterParsing;
3041   return NS_OK;
3042 }
3043 
3044 NS_IMETHODIMP
3045 nsMsgLocalMailFolder::SetCheckForNewMessagesAfterParsing(
3046     bool aCheckForNewMessagesAfterParsing) {
3047   mCheckForNewMessagesAfterParsing = aCheckForNewMessagesAfterParsing;
3048   return NS_OK;
3049 }
3050 
3051 NS_IMETHODIMP
3052 nsMsgLocalMailFolder::NotifyCompactCompleted() {
3053   mExpungedBytes = 0;
3054   m_newMsgs.Clear();  // if compacted, m_newMsgs probably aren't valid.
3055   // if compacted, processing flags probably also aren't valid.
3056   ClearProcessingFlags();
3057   (void)RefreshSizeOnDisk();
3058   (void)CloseDBIfFolderNotOpen();
3059   NotifyFolderEvent(kCompactCompleted);
3060   return NS_OK;
3061 }
3062 
3063 NS_IMETHODIMP nsMsgLocalMailFolder::Shutdown(bool shutdownChildren) {
3064   mInitialized = false;
3065   return nsMsgDBFolder::Shutdown(shutdownChildren);
3066 }
3067 
3068 NS_IMETHODIMP
3069 nsMsgLocalMailFolder::OnMessageClassified(const nsACString& aMsgURI,
3070                                           nsMsgJunkStatus aClassification,
3071                                           uint32_t aJunkPercent)
3072 
3073 {
3074   nsCOMPtr<nsIMsgIncomingServer> server;
3075   nsresult rv = GetServer(getter_AddRefs(server));
3076   NS_ENSURE_SUCCESS(rv, rv);
3077 
3078   nsCOMPtr<nsISpamSettings> spamSettings;
3079   rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
3080   NS_ENSURE_SUCCESS(rv, rv);
3081 
3082   nsCString spamFolderURI;
3083   rv = spamSettings->GetSpamFolderURI(spamFolderURI);
3084   NS_ENSURE_SUCCESS(rv, rv);
3085 
3086   if (!aMsgURI.IsEmpty())  // not end of batch
3087   {
3088     nsCOMPtr<nsIMsgDBHdr> msgHdr;
3089     rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
3090     NS_ENSURE_SUCCESS(rv, rv);
3091 
3092     nsMsgKey msgKey;
3093     rv = msgHdr->GetMessageKey(&msgKey);
3094     NS_ENSURE_SUCCESS(rv, rv);
3095 
3096     // check if this message needs junk classification
3097     uint32_t processingFlags;
3098     GetProcessingFlags(msgKey, &processingFlags);
3099 
3100     if (processingFlags & nsMsgProcessingFlags::ClassifyJunk) {
3101       nsMsgDBFolder::OnMessageClassified(aMsgURI, aClassification,
3102                                          aJunkPercent);
3103 
3104       if (aClassification == nsIJunkMailPlugin::JUNK) {
3105         bool willMoveMessage = false;
3106 
3107         // don't do the move when we are opening up
3108         // the junk mail folder or the trash folder
3109         // or when manually classifying messages in those folders
3110         if (!(mFlags & nsMsgFolderFlags::Junk ||
3111               mFlags & nsMsgFolderFlags::Trash)) {
3112           bool moveOnSpam = false;
3113           rv = spamSettings->GetMoveOnSpam(&moveOnSpam);
3114           NS_ENSURE_SUCCESS(rv, rv);
3115           if (moveOnSpam) {
3116             nsCOMPtr<nsIMsgFolder> folder;
3117             rv = FindFolder(spamFolderURI, getter_AddRefs(folder));
3118             NS_ENSURE_SUCCESS(rv, rv);
3119             if (folder) {
3120               rv = folder->SetFlag(nsMsgFolderFlags::Junk);
3121               NS_ENSURE_SUCCESS(rv, rv);
3122               mSpamKeysToMove.AppendElement(msgKey);
3123               willMoveMessage = true;
3124             } else {
3125               // XXX TODO
3126               // JUNK MAIL RELATED
3127               // the listener should do
3128               // rv = folder->SetFlag(nsMsgFolderFlags::Junk);
3129               // NS_ENSURE_SUCCESS(rv,rv);
3130               // mSpamKeysToMove.AppendElement(msgKey);
3131               // willMoveMessage = true;
3132               rv =
3133                   GetOrCreateJunkFolder(spamFolderURI, nullptr /* aListener */);
3134               NS_ASSERTION(NS_SUCCEEDED(rv), "GetOrCreateJunkFolder failed");
3135             }
3136           }
3137         }
3138         rv = spamSettings->LogJunkHit(msgHdr, willMoveMessage);
3139         NS_ENSURE_SUCCESS(rv, rv);
3140       }
3141     }
3142   }
3143 
3144   else  // end of batch
3145   {
3146     // Parent will apply post bayes filters.
3147     nsMsgDBFolder::OnMessageClassified(EmptyCString(),
3148                                        nsIJunkMailPlugin::UNCLASSIFIED, 0);
3149     nsTArray<RefPtr<nsIMsgDBHdr>> messages;
3150     if (!mSpamKeysToMove.IsEmpty()) {
3151       nsCOMPtr<nsIMsgFolder> folder;
3152       if (!spamFolderURI.IsEmpty()) {
3153         rv = FindFolder(spamFolderURI, getter_AddRefs(folder));
3154         NS_ENSURE_SUCCESS(rv, rv);
3155       }
3156       for (uint32_t keyIndex = 0; keyIndex < mSpamKeysToMove.Length();
3157            keyIndex++) {
3158         // If an upstream filter moved this message, don't move it here.
3159         nsMsgKey msgKey = mSpamKeysToMove.ElementAt(keyIndex);
3160         nsMsgProcessingFlagType processingFlags;
3161         GetProcessingFlags(msgKey, &processingFlags);
3162         if (folder && !(processingFlags & nsMsgProcessingFlags::FilterToMove)) {
3163           nsCOMPtr<nsIMsgDBHdr> mailHdr;
3164           rv = GetMessageHeader(msgKey, getter_AddRefs(mailHdr));
3165           if (NS_SUCCEEDED(rv) && mailHdr) messages.AppendElement(mailHdr);
3166         } else {
3167           // We don't need the processing flag any more.
3168           AndProcessingFlags(msgKey, ~nsMsgProcessingFlags::FilterToMove);
3169         }
3170       }
3171 
3172       if (folder) {
3173         nsCOMPtr<nsIMsgCopyService> copySvc =
3174             do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
3175         NS_ENSURE_SUCCESS(rv, rv);
3176 
3177         rv = copySvc->CopyMessages(
3178             this, messages, folder, true,
3179             /*nsIMsgCopyServiceListener* listener*/ nullptr, nullptr,
3180             false /*allowUndo*/);
3181         NS_ASSERTION(NS_SUCCEEDED(rv), "CopyMessages failed");
3182         if (NS_FAILED(rv)) {
3183           nsAutoCString logMsg(
3184               "failed to copy junk messages to junk folder rv = ");
3185           logMsg.AppendInt(static_cast<uint32_t>(rv), 16);
3186           spamSettings->LogJunkString(logMsg.get());
3187         }
3188       }
3189     }
3190     int32_t numNewMessages;
3191     GetNumNewMessages(false, &numNewMessages);
3192     SetNumNewMessages(numNewMessages - messages.Length());
3193     mSpamKeysToMove.Clear();
3194     // check if this is the inbox first...
3195     if (mFlags & nsMsgFolderFlags::Inbox) PerformBiffNotifications();
3196   }
3197   return NS_OK;
3198 }
3199 
3200 NS_IMETHODIMP
3201 nsMsgLocalMailFolder::GetFolderScanState(nsLocalFolderScanState* aState) {
3202   NS_ENSURE_ARG_POINTER(aState);
3203 
3204   nsresult rv = GetMsgStore(getter_AddRefs(aState->m_msgStore));
3205   NS_ENSURE_SUCCESS(rv, rv);
3206   aState->m_uidl = nullptr;
3207   return rv;
3208 }
3209 
3210 NS_IMETHODIMP
3211 nsMsgLocalMailFolder::GetUidlFromFolder(nsLocalFolderScanState* aState,
3212                                         nsIMsgDBHdr* aMsgDBHdr) {
3213   bool more = false;
3214   uint32_t size = 0, len = 0;
3215   const char* accountKey = nullptr;
3216   nsresult rv = GetMsgInputStream(aMsgDBHdr, &aState->m_streamReusable,
3217                                   getter_AddRefs(aState->m_inputStream));
3218   aState->m_seekableStream = do_QueryInterface(aState->m_inputStream);
3219   NS_ENSURE_SUCCESS(rv, rv);
3220 
3221   mozilla::UniquePtr<nsLineBuffer<char>> lineBuffer(new nsLineBuffer<char>);
3222 
3223   aState->m_uidl = nullptr;
3224 
3225   aMsgDBHdr->GetMessageSize(&len);
3226   while (len > 0) {
3227     rv = NS_ReadLine(aState->m_inputStream.get(), lineBuffer.get(),
3228                      aState->m_header, &more);
3229     if (NS_SUCCEEDED(rv)) {
3230       size = aState->m_header.Length();
3231       if (!size) break;
3232       // this isn't quite right - need to account for line endings
3233       len -= size;
3234       // account key header will always be before X_UIDL header
3235       if (!accountKey) {
3236         accountKey =
3237             strstr(aState->m_header.get(), HEADER_X_MOZILLA_ACCOUNT_KEY);
3238         if (accountKey) {
3239           accountKey += strlen(HEADER_X_MOZILLA_ACCOUNT_KEY) + 2;
3240           aState->m_accountKey = accountKey;
3241         }
3242       } else {
3243         aState->m_uidl = strstr(aState->m_header.get(), X_UIDL);
3244         if (aState->m_uidl) {
3245           aState->m_uidl += X_UIDL_LEN + 2;  // skip UIDL: header
3246           break;
3247         }
3248       }
3249     }
3250   }
3251   if (!aState->m_streamReusable) {
3252     aState->m_inputStream->Close();
3253     aState->m_inputStream = nullptr;
3254   }
3255   return rv;
3256 }
3257 
3258 /**
3259  * Adds a message to the end of the folder, parsing it as it goes, and
3260  * applying filters, if applicable.
3261  */
3262 NS_IMETHODIMP
3263 nsMsgLocalMailFolder::AddMessage(const char* aMessage, nsIMsgDBHdr** aHdr) {
3264   NS_ENSURE_ARG_POINTER(aHdr);
3265   AutoTArray<nsCString, 1> aMessages = {nsDependentCString(aMessage)};
3266   nsTArray<RefPtr<nsIMsgDBHdr>> hdrs;
3267   nsresult rv = AddMessageBatch(aMessages, hdrs);
3268   NS_ENSURE_SUCCESS(rv, rv);
3269   NS_ADDREF(*aHdr = hdrs[0]);
3270   return rv;
3271 }
3272 
3273 NS_IMETHODIMP
3274 nsMsgLocalMailFolder::AddMessageBatch(
3275     const nsTArray<nsCString>& aMessages,
3276     nsTArray<RefPtr<nsIMsgDBHdr>>& aHdrArray) {
3277   aHdrArray.ClearAndRetainStorage();
3278   aHdrArray.SetCapacity(aMessages.Length());
3279 
3280   nsCOMPtr<nsIMsgIncomingServer> server;
3281   nsresult rv = GetServer(getter_AddRefs(server));
3282   NS_ENSURE_SUCCESS(rv, rv);
3283 
3284   nsCOMPtr<nsIMsgPluggableStore> msgStore;
3285   nsCOMPtr<nsIOutputStream> outFileStream;
3286   nsCOMPtr<nsIMsgDBHdr> newHdr;
3287 
3288   rv = server->GetMsgStore(getter_AddRefs(msgStore));
3289   NS_ENSURE_SUCCESS(rv, rv);
3290 
3291   nsCOMPtr<nsIMsgFolder> rootFolder;
3292   rv = GetRootFolder(getter_AddRefs(rootFolder));
3293   NS_ENSURE_SUCCESS(rv, rv);
3294 
3295   bool isLocked;
3296 
3297   GetLocked(&isLocked);
3298   if (isLocked) return NS_MSG_FOLDER_BUSY;
3299 
3300   AcquireSemaphore(static_cast<nsIMsgLocalMailFolder*>(this));
3301 
3302   if (NS_SUCCEEDED(rv)) {
3303     NS_ENSURE_SUCCESS(rv, rv);
3304     for (uint32_t i = 0; i < aMessages.Length(); i++) {
3305       RefPtr<nsParseNewMailState> newMailParser = new nsParseNewMailState;
3306       NS_ENSURE_TRUE(newMailParser, NS_ERROR_OUT_OF_MEMORY);
3307       if (!mGettingNewMessages) newMailParser->DisableFilters();
3308       bool reusable;
3309       rv = msgStore->GetNewMsgOutputStream(this, getter_AddRefs(newHdr),
3310                                            &reusable,
3311                                            getter_AddRefs(outFileStream));
3312       NS_ENSURE_SUCCESS(rv, rv);
3313 
3314       // Get a msgWindow. Proceed without one, but filter actions to imap
3315       // folders will silently fail if not signed in and no window for a prompt.
3316       nsCOMPtr<nsIMsgWindow> msgWindow;
3317       nsCOMPtr<nsIMsgMailSession> mailSession =
3318           do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
3319       if (NS_SUCCEEDED(rv))
3320         mailSession->GetTopmostMsgWindow(getter_AddRefs(msgWindow));
3321 
3322       rv = newMailParser->Init(rootFolder, this, msgWindow, newHdr,
3323                                outFileStream);
3324 
3325       uint32_t bytesWritten;
3326       uint32_t messageLen = aMessages[i].Length();
3327       outFileStream->Write(aMessages[i].get(), messageLen, &bytesWritten);
3328       newMailParser->BufferInput(aMessages[i].get(), messageLen);
3329 
3330       FinishNewLocalMessage(outFileStream, newHdr, msgStore, newMailParser);
3331       outFileStream->Close();
3332       outFileStream = nullptr;
3333       newMailParser->OnStopRequest(nullptr, NS_OK);
3334       newMailParser->EndMsgDownload();
3335       aHdrArray.AppendElement(newHdr);
3336     }
3337   }
3338   ReleaseSemaphore(static_cast<nsIMsgLocalMailFolder*>(this));
3339   return rv;
3340 }
3341 
3342 nsresult nsMsgLocalMailFolder::FinishNewLocalMessage(
3343     nsIOutputStream* aOutputStream, nsIMsgDBHdr* aNewHdr,
3344     nsIMsgPluggableStore* aMsgStore, nsParseMailMessageState* aParseMsgState) {
3345   uint32_t bytesWritten;
3346   aOutputStream->Write(MSG_LINEBREAK, MSG_LINEBREAK_LEN, &bytesWritten);
3347   if (aParseMsgState)
3348     aParseMsgState->ParseAFolderLine(MSG_LINEBREAK, MSG_LINEBREAK_LEN);
3349   return aMsgStore->FinishNewMessage(aOutputStream, aNewHdr);
3350 }
3351 
3352 NS_IMETHODIMP
3353 nsMsgLocalMailFolder::WarnIfLocalFileTooBig(nsIMsgWindow* aWindow,
3354                                             int64_t aSpaceRequested,
3355                                             bool* aTooBig) {
3356   NS_ENSURE_ARG_POINTER(aTooBig);
3357 
3358   *aTooBig = true;
3359   nsCOMPtr<nsIMsgPluggableStore> msgStore;
3360   nsresult rv = GetMsgStore(getter_AddRefs(msgStore));
3361   NS_ENSURE_SUCCESS(rv, rv);
3362   bool spaceAvailable = false;
3363   // check if we have a reasonable amount of space left
3364   rv = msgStore->HasSpaceAvailable(this, aSpaceRequested, &spaceAvailable);
3365   if (NS_SUCCEEDED(rv) && spaceAvailable) {
3366     *aTooBig = false;
3367   } else if (rv == NS_ERROR_FILE_TOO_BIG) {
3368     ThrowAlertMsg("mailboxTooLarge", aWindow);
3369   } else {
3370     ThrowAlertMsg("outOfDiskSpace", aWindow);
3371   }
3372   return NS_OK;
3373 }
3374 
3375 NS_IMETHODIMP nsMsgLocalMailFolder::FetchMsgPreviewText(
3376     nsTArray<nsMsgKey> const& aKeysToFetch, bool aLocalOnly,
3377     nsIUrlListener* aUrlListener, bool* aAsyncResults) {
3378   NS_ENSURE_ARG_POINTER(aAsyncResults);
3379 
3380   *aAsyncResults = false;
3381   nsCOMPtr<nsIInputStream> inputStream;
3382   nsCOMPtr<nsIMsgPluggableStore> msgStore;
3383   nsresult rv = GetMsgStore(getter_AddRefs(msgStore));
3384   NS_ENSURE_SUCCESS(rv, rv);
3385 
3386   for (uint32_t i = 0; i < aKeysToFetch.Length(); i++) {
3387     nsCOMPtr<nsIMsgDBHdr> msgHdr;
3388     nsCString prevBody;
3389     rv = GetMessageHeader(aKeysToFetch[i], getter_AddRefs(msgHdr));
3390     NS_ENSURE_SUCCESS(rv, rv);
3391     // ignore messages that already have a preview body.
3392     msgHdr->GetStringProperty("preview", getter_Copies(prevBody));
3393     if (!prevBody.IsEmpty()) continue;
3394 
3395     bool reusable;
3396     rv = GetMsgInputStream(msgHdr, &reusable, getter_AddRefs(inputStream));
3397     NS_ENSURE_SUCCESS(rv, rv);
3398     rv = GetMsgPreviewTextFromStream(msgHdr, inputStream);
3399   }
3400   return rv;
3401 }
3402 
3403 NS_IMETHODIMP nsMsgLocalMailFolder::AddKeywordsToMessages(
3404     const nsTArray<RefPtr<nsIMsgDBHdr>>& aMessages,
3405     const nsACString& aKeywords) {
3406   nsresult rv = nsMsgDBFolder::AddKeywordsToMessages(aMessages, aKeywords);
3407   NS_ENSURE_SUCCESS(rv, rv);
3408   nsCOMPtr<nsIMsgPluggableStore> msgStore;
3409   rv = GetMsgStore(getter_AddRefs(msgStore));
3410   NS_ENSURE_SUCCESS(rv, rv);
3411   return msgStore->ChangeKeywords(aMessages, aKeywords, true /* add */);
3412 }
3413 
3414 NS_IMETHODIMP nsMsgLocalMailFolder::RemoveKeywordsFromMessages(
3415     const nsTArray<RefPtr<nsIMsgDBHdr>>& aMessages,
3416     const nsACString& aKeywords) {
3417   nsresult rv = nsMsgDBFolder::RemoveKeywordsFromMessages(aMessages, aKeywords);
3418   NS_ENSURE_SUCCESS(rv, rv);
3419   nsCOMPtr<nsIMsgPluggableStore> msgStore;
3420   rv = GetMsgStore(getter_AddRefs(msgStore));
3421   NS_ENSURE_SUCCESS(rv, rv);
3422   return msgStore->ChangeKeywords(aMessages, aKeywords, false /* remove */);
3423 }
3424 
3425 NS_IMETHODIMP nsMsgLocalMailFolder::UpdateNewMsgHdr(nsIMsgDBHdr* aOldHdr,
3426                                                     nsIMsgDBHdr* aNewHdr) {
3427   NS_ENSURE_ARG_POINTER(aOldHdr);
3428   NS_ENSURE_ARG_POINTER(aNewHdr);
3429   // Preserve any properties set on the message.
3430   CopyPropertiesToMsgHdr(aNewHdr, aOldHdr, true);
3431 
3432   // Preserve keywords manually, since they are set as don't preserve.
3433   nsCString keywordString;
3434   aOldHdr->GetStringProperty("keywords", getter_Copies(keywordString));
3435   aNewHdr->SetStringProperty("keywords", keywordString.get());
3436 
3437   // If the junk score was set by the plugin, remove junkscore to force a new
3438   // junk analysis, this time using the body.
3439   nsCString junkScoreOrigin;
3440   aOldHdr->GetStringProperty("junkscoreorigin", getter_Copies(junkScoreOrigin));
3441   if (junkScoreOrigin.EqualsLiteral("plugin"))
3442     aNewHdr->SetStringProperty("junkscore", "");
3443 
3444   return NS_OK;
3445 }
3446