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