1 /* -*- Mode:
2 C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "msgCore.h"
8 #include "prmem.h"
9 #include "nsMsgImapCID.h"
10 #include "nsImapMailFolder.h"
11 #include "nsIImapService.h"
12 #include "nsIFile.h"
13 #include "nsIUrlListener.h"
14 #include "nsCOMPtr.h"
15 #include "nsMsgDBCID.h"
16 #include "nsMsgFolderFlags.h"
17 #include "nsISeekableStream.h"
18 #include "nsThreadUtils.h"
19 #include "nsIImapUrl.h"
20 #include "nsImapUtils.h"
21 #include "nsMsgUtils.h"
22 #include "nsIMsgMailSession.h"
23 #include "nsMsgBaseCID.h"
24 #include "nsMsgLocalCID.h"
25 #include "nsITransactionManager.h"
26 #include "nsImapUndoTxn.h"
27 #include "nsIImapHostSessionList.h"
28 #include "nsIMsgCopyService.h"
29 #include "nsICopyMessageStreamListener.h"
30 #include "nsImapStringBundle.h"
31 #include "nsIMsgFolderCacheElement.h"
32 #include "nsTextFormatter.h"
33 #include "nsIPrefBranch.h"
34 #include "nsIPrefService.h"
35 #include "nsMsgI18N.h"
36 #include "nsIMsgFilter.h"
37 #include "nsIMsgFilterService.h"
38 #include "nsIMsgSearchCustomTerm.h"
39 #include "nsIMsgSearchTerm.h"
40 #include "nsImapMoveCoalescer.h"
41 #include "nsIPrompt.h"
42 #include "nsIDocShell.h"
43 #include "nsUnicharUtils.h"
44 #include "nsIImapFlagAndUidState.h"
45 #include "nsIImapHeaderXferInfo.h"
46 #include "nsIMessenger.h"
47 #include "nsIMsgSearchAdapter.h"
48 #include "nsIImapMockChannel.h"
49 #include "nsIProgressEventSink.h"
50 #include "nsIMsgWindow.h"
51 #include "nsIMsgFolder.h" // TO include biffState enum. Change to bool later...
52 #include "nsIMsgLocalMailFolder.h"
53 #include "nsIMsgOfflineImapOperation.h"
54 #include "nsImapOfflineSync.h"
55 #include "nsIImapMailFolderSink.h"
56 #include "nsIImapServerSink.h"
57 #include "nsIMsgAccountManager.h"
58 #include "nsQuickSort.h"
59 #include "nsIImapMockChannel.h"
60 #include "nsNetUtil.h"
61 #include "nsImapNamespace.h"
62 #include "nsIMsgFolderCompactor.h"
63 #include "nsMsgMessageFlags.h"
64 #include "nsISpamSettings.h"
65 #include <time.h>
66 #include "nsIMsgMailNewsUrl.h"
67 #include "nsEmbedCID.h"
68 #include "nsIMsgComposeService.h"
69 #include "nsMsgCompCID.h"
70 #include "nsDirectoryServiceDefs.h"
71 #include "nsIDirectoryEnumerator.h"
72 #include "nsIMsgIdentity.h"
73 #include "nsIMsgFolderNotificationService.h"
74 #include "nsNativeCharsetUtils.h"
75 #include "nsIExternalProtocolService.h"
76 #include "nsCExternalHandlerService.h"
77 #include "prprf.h"
78 #include "nsAutoSyncManager.h"
79 #include "nsIMsgFilterCustomAction.h"
80 #include "nsMsgReadStateTxn.h"
81 #include "nsStringEnumerator.h"
82 #include "nsIMsgStatusFeedback.h"
83 #include "nsMsgLineBuffer.h"
84 #include "mozilla/Logging.h"
85 #include "mozilla/Attributes.h"
86 #include "nsStringStream.h"
87 #include "nsIStreamListener.h"
88
89 static NS_DEFINE_CID(kParseMailMsgStateCID, NS_PARSEMAILMSGSTATE_CID);
90 static NS_DEFINE_CID(kCImapHostSessionList, NS_IIMAPHOSTSESSIONLIST_CID);
91
92 #define MAILNEWS_CUSTOM_HEADERS "mailnews.customHeaders"
93
94 using namespace mozilla;
95
96 extern LazyLogModule gAutoSyncLog; // defined in nsAutoSyncManager.cpp
97 extern LazyLogModule IMAP; // defined in nsImapProtocol.cpp
98 extern LazyLogModule IMAP_CS; // For CONDSTORE, defined in nsImapProtocol.cpp
99 extern LazyLogModule FILTERLOGMODULE; // defined in nsMsgFilterService.cpp
100 LazyLogModule IMAP_KW("IMAP_KW"); // for logging keyword (tag) processing
101
102 /*
103 Copies the contents of srcDir into destDir.
104 destDir will be created if it doesn't exist.
105 */
106
RecursiveCopy(nsIFile * srcDir,nsIFile * destDir)107 static nsresult RecursiveCopy(nsIFile* srcDir, nsIFile* destDir) {
108 nsresult rv;
109 bool isDir;
110
111 rv = srcDir->IsDirectory(&isDir);
112 if (NS_FAILED(rv)) return rv;
113 if (!isDir) return NS_ERROR_INVALID_ARG;
114
115 bool exists;
116 rv = destDir->Exists(&exists);
117 if (NS_SUCCEEDED(rv) && !exists)
118 rv = destDir->Create(nsIFile::DIRECTORY_TYPE, 0775);
119 if (NS_FAILED(rv)) return rv;
120
121 nsCOMPtr<nsIDirectoryEnumerator> dirIterator;
122 rv = srcDir->GetDirectoryEntries(getter_AddRefs(dirIterator));
123 if (NS_FAILED(rv)) return rv;
124
125 bool hasMore = false;
126 while (NS_SUCCEEDED(dirIterator->HasMoreElements(&hasMore)) && hasMore) {
127 nsCOMPtr<nsIFile> dirEntry;
128 rv = dirIterator->GetNextFile(getter_AddRefs(dirEntry));
129 if (NS_SUCCEEDED(rv) && dirEntry) {
130 rv = dirEntry->IsDirectory(&isDir);
131 if (NS_SUCCEEDED(rv)) {
132 if (isDir) {
133 nsCOMPtr<nsIFile> newChild;
134 rv = destDir->Clone(getter_AddRefs(newChild));
135 if (NS_SUCCEEDED(rv)) {
136 nsAutoString leafName;
137 dirEntry->GetLeafName(leafName);
138 newChild->AppendRelativePath(leafName);
139 rv = newChild->Exists(&exists);
140 if (NS_SUCCEEDED(rv) && !exists)
141 rv = newChild->Create(nsIFile::DIRECTORY_TYPE, 0775);
142 rv = RecursiveCopy(dirEntry, newChild);
143 }
144 } else
145 rv = dirEntry->CopyTo(destDir, EmptyString());
146 }
147 }
148 }
149
150 return rv;
151 }
152
153 //
154 // nsMsgQuota
155 //
NS_IMPL_ISUPPORTS(nsMsgQuota,nsIMsgQuota)156 NS_IMPL_ISUPPORTS(nsMsgQuota, nsIMsgQuota)
157
158 nsMsgQuota::nsMsgQuota(const nsACString& aName, const uint64_t& aUsage,
159 const uint64_t& aLimit)
160 : mName(aName), mUsage(aUsage), mLimit(aLimit) {}
161
~nsMsgQuota()162 nsMsgQuota::~nsMsgQuota() {}
163
164 /**
165 * Note: These quota access function are not called but still must be defined
166 * for the linker.
167 */
GetName(nsACString & aName)168 NS_IMETHODIMP nsMsgQuota::GetName(nsACString& aName) {
169 aName = mName;
170 return NS_OK;
171 }
172
SetName(const nsACString & aName)173 NS_IMETHODIMP nsMsgQuota::SetName(const nsACString& aName) {
174 mName = aName;
175 return NS_OK;
176 }
177
GetUsage(uint64_t * aUsage)178 NS_IMETHODIMP nsMsgQuota::GetUsage(uint64_t* aUsage) {
179 *aUsage = mUsage;
180 return NS_OK;
181 }
182
SetUsage(uint64_t aUsage)183 NS_IMETHODIMP nsMsgQuota::SetUsage(uint64_t aUsage) {
184 mUsage = aUsage;
185 return NS_OK;
186 }
187
GetLimit(uint64_t * aLimit)188 NS_IMETHODIMP nsMsgQuota::GetLimit(uint64_t* aLimit) {
189 *aLimit = mLimit;
190 return NS_OK;
191 }
192
SetLimit(uint64_t aLimit)193 NS_IMETHODIMP nsMsgQuota::SetLimit(uint64_t aLimit) {
194 mLimit = aLimit;
195 return NS_OK;
196 }
197
198 //
199 // nsImapMailFolder
200 //
nsImapMailFolder()201 nsImapMailFolder::nsImapMailFolder()
202 : m_initialized(false),
203 m_haveDiscoveredAllFolders(false),
204 m_curMsgUid(0),
205 m_nextMessageByteLength(0),
206 m_urlRunning(false),
207 m_verifiedAsOnlineFolder(false),
208 m_explicitlyVerify(false),
209 m_folderIsNamespace(false),
210 m_folderNeedsSubscribing(false),
211 m_folderNeedsAdded(false),
212 m_folderNeedsACLListed(true),
213 m_performingBiff(false),
214 m_updatingFolder(false),
215 m_compactingOfflineStore(false),
216 m_expunging(false),
217 m_applyIncomingFilters(false),
218 m_downloadingFolderForOfflineUse(false),
219 m_filterListRequiresBody(false),
220 m_folderQuotaCommandIssued(false),
221 m_folderQuotaDataIsValid(false) {
222 m_boxFlags = 0;
223 m_uidValidity = kUidUnknown;
224 m_numServerRecentMessages = 0;
225 m_numServerUnseenMessages = 0;
226 m_numServerTotalMessages = 0;
227 m_nextUID = nsMsgKey_None;
228 m_hierarchyDelimiter = kOnlineHierarchySeparatorUnknown;
229 m_folderACL = nullptr;
230 m_aclFlags = 0;
231 m_supportedUserFlags = 0;
232 m_namespace = nullptr;
233 m_pendingPlaybackReq = nullptr;
234 }
235
~nsImapMailFolder()236 nsImapMailFolder::~nsImapMailFolder() {
237 delete m_folderACL;
238
239 // cleanup any pending request
240 delete m_pendingPlaybackReq;
241 }
242
NS_IMPL_ADDREF_INHERITED(nsImapMailFolder,nsMsgDBFolder)243 NS_IMPL_ADDREF_INHERITED(nsImapMailFolder, nsMsgDBFolder)
244 NS_IMPL_RELEASE_INHERITED(nsImapMailFolder, nsMsgDBFolder)
245 NS_IMPL_QUERY_HEAD(nsImapMailFolder)
246 NS_IMPL_QUERY_BODY(nsIMsgImapMailFolder)
247 NS_IMPL_QUERY_BODY(nsICopyMessageListener)
248 NS_IMPL_QUERY_BODY(nsIImapMailFolderSink)
249 NS_IMPL_QUERY_BODY(nsIImapMessageSink)
250 NS_IMPL_QUERY_BODY(nsIUrlListener)
251 NS_IMPL_QUERY_BODY(nsIMsgFilterHitNotify)
252 NS_IMPL_QUERY_TAIL_INHERITING(nsMsgDBFolder)
253
254 nsresult nsImapMailFolder::AddDirectorySeparator(nsIFile* path) {
255 if (mURI.Equals(kImapRootURI)) {
256 // don't concat the full separator with .sbd
257 } else {
258 // see if there's a dir with the same name ending with .sbd
259 nsAutoString leafName;
260 path->GetLeafName(leafName);
261 leafName.AppendLiteral(FOLDER_SUFFIX);
262 path->SetLeafName(leafName);
263 }
264
265 return NS_OK;
266 }
267
nsShouldIgnoreFile(nsString & name)268 static bool nsShouldIgnoreFile(nsString& name) {
269 if (StringEndsWith(name, NS_LITERAL_STRING_FROM_CSTRING(SUMMARY_SUFFIX),
270 nsCaseInsensitiveStringComparator)) {
271 name.SetLength(name.Length() -
272 SUMMARY_SUFFIX_LENGTH); // truncate the string
273 return false;
274 }
275 return true;
276 }
277
CreateChildFromURI(const nsACString & uri,nsIMsgFolder ** folder)278 nsresult nsImapMailFolder::CreateChildFromURI(const nsACString& uri,
279 nsIMsgFolder** folder) {
280 nsImapMailFolder* newFolder = new nsImapMailFolder;
281 newFolder->Init(uri);
282 NS_ADDREF(*folder = newFolder);
283 return NS_OK;
284 }
285
AddSubfolder(const nsAString & aName,nsIMsgFolder ** aChild)286 NS_IMETHODIMP nsImapMailFolder::AddSubfolder(const nsAString& aName,
287 nsIMsgFolder** aChild) {
288 NS_ENSURE_ARG_POINTER(aChild);
289
290 int32_t flags = 0;
291 nsresult rv;
292
293 nsAutoCString uri(mURI);
294 uri.Append('/');
295
296 nsAutoCString escapedName;
297 rv = NS_MsgEscapeEncodeURLPath(aName, escapedName);
298 NS_ENSURE_SUCCESS(rv, rv);
299
300 uri += escapedName.get();
301
302 nsCOMPtr<nsIMsgFolder> msgFolder;
303 rv = GetChildWithURI(uri, false /*deep*/, true /*case Insensitive*/,
304 getter_AddRefs(msgFolder));
305 if (NS_SUCCEEDED(rv) && msgFolder) return NS_MSG_FOLDER_EXISTS;
306
307 nsCOMPtr<nsIMsgFolder> folder;
308 rv = GetOrCreateFolder(uri, getter_AddRefs(folder));
309 NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
310
311 nsCOMPtr<nsIFile> path;
312 rv = CreateDirectoryForFolder(getter_AddRefs(path));
313 NS_ENSURE_SUCCESS(rv, rv);
314
315 folder->GetFlags((uint32_t*)&flags);
316
317 flags |= nsMsgFolderFlags::Mail;
318
319 nsCOMPtr<nsIImapIncomingServer> imapServer;
320 GetImapIncomingServer(getter_AddRefs(imapServer));
321 if (imapServer) {
322 bool setNewFoldersForOffline = false;
323 rv = imapServer->GetOfflineDownload(&setNewFoldersForOffline);
324 if (NS_SUCCEEDED(rv) && setNewFoldersForOffline)
325 flags |= nsMsgFolderFlags::Offline;
326 }
327
328 folder->SetParent(this);
329
330 folder->SetFlags(flags);
331
332 mSubFolders.AppendObject(folder);
333 folder.forget(aChild);
334
335 nsCOMPtr<nsIMsgImapMailFolder> imapChild = do_QueryInterface(*aChild);
336 if (imapChild) {
337 imapChild->SetOnlineName(NS_ConvertUTF16toUTF8(aName));
338 imapChild->SetHierarchyDelimiter(m_hierarchyDelimiter);
339 }
340 NotifyItemAdded(*aChild);
341 return rv;
342 }
343
344 // Creates a new child nsIMsgFolder locally, with no IMAP traffic.
AddSubfolderWithPath(nsAString & name,nsIFile * dbPath,nsIMsgFolder ** child,bool brandNew)345 nsresult nsImapMailFolder::AddSubfolderWithPath(nsAString& name,
346 nsIFile* dbPath,
347 nsIMsgFolder** child,
348 bool brandNew) {
349 NS_ENSURE_ARG_POINTER(child);
350 nsresult rv;
351
352 nsAutoCString uri(mURI);
353 uri.Append('/');
354 AppendUTF16toUTF8(name, uri);
355
356 bool isServer;
357 rv = GetIsServer(&isServer);
358 NS_ENSURE_SUCCESS(rv, rv);
359
360 bool isInbox = isServer && name.LowerCaseEqualsLiteral("inbox");
361
362 // will make sure mSubFolders does not have duplicates because of bogus msf
363 // files.
364 nsCOMPtr<nsIMsgFolder> msgFolder;
365 rv = GetChildWithURI(uri, false /*deep*/, isInbox /*case Insensitive*/,
366 getter_AddRefs(msgFolder));
367 if (NS_SUCCEEDED(rv) && msgFolder) return NS_MSG_FOLDER_EXISTS;
368
369 nsCOMPtr<nsIMsgFolder> folder;
370 rv = GetOrCreateFolder(uri, getter_AddRefs(folder));
371 NS_ENSURE_SUCCESS(rv, rv);
372
373 folder->SetFilePath(dbPath);
374 nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(folder, &rv);
375 mozilla::Unused << imapFolder;
376 NS_ENSURE_SUCCESS(rv, rv);
377
378 uint32_t flags = 0;
379 folder->GetFlags(&flags);
380
381 folder->SetParent(this);
382 flags |= nsMsgFolderFlags::Mail;
383
384 uint32_t pFlags;
385 GetFlags(&pFlags);
386 bool isParentInbox = pFlags & nsMsgFolderFlags::Inbox;
387
388 nsCOMPtr<nsIImapIncomingServer> imapServer;
389 rv = GetImapIncomingServer(getter_AddRefs(imapServer));
390 NS_ENSURE_SUCCESS(rv, rv);
391
392 // Only set these if these are top level children or parent is inbox
393 if (isInbox)
394 flags |= nsMsgFolderFlags::Inbox;
395 else if (isServer || isParentInbox) {
396 nsMsgImapDeleteModel deleteModel;
397 imapServer->GetDeleteModel(&deleteModel);
398 if (deleteModel == nsMsgImapDeleteModels::MoveToTrash) {
399 nsAutoString trashName;
400 GetTrashFolderName(trashName);
401 if (name.Equals(trashName)) flags |= nsMsgFolderFlags::Trash;
402 }
403 }
404
405 // Make the folder offline if it is newly created and the offline_download
406 // pref is true, unless it's the Trash or Junk folder.
407 if (brandNew &&
408 !(flags & (nsMsgFolderFlags::Trash | nsMsgFolderFlags::Junk))) {
409 bool setNewFoldersForOffline = false;
410 rv = imapServer->GetOfflineDownload(&setNewFoldersForOffline);
411 if (NS_SUCCEEDED(rv) && setNewFoldersForOffline)
412 flags |= nsMsgFolderFlags::Offline;
413 }
414
415 folder->SetFlags(flags);
416
417 if (folder) mSubFolders.AppendObject(folder);
418 folder.forget(child);
419 return NS_OK;
420 }
421
422 // Create child nsIMsgFolders by scanning the filesystem to find .msf files.
423 // No IMAP traffic.
CreateSubFolders(nsIFile * path)424 nsresult nsImapMailFolder::CreateSubFolders(nsIFile* path) {
425 nsresult rv = NS_OK;
426 nsCOMPtr<nsIMsgIncomingServer> server;
427 rv = GetServer(getter_AddRefs(server));
428 NS_ENSURE_SUCCESS(rv, rv);
429
430 nsCOMPtr<nsIDirectoryEnumerator> directoryEnumerator;
431 rv = path->GetDirectoryEntries(getter_AddRefs(directoryEnumerator));
432 NS_ENSURE_SUCCESS(rv, rv);
433
434 // For each .msf file in the directory...
435 bool hasMore = false;
436 while (NS_SUCCEEDED(directoryEnumerator->HasMoreElements(&hasMore)) &&
437 hasMore) {
438 nsCOMPtr<nsIFile> currentFolderPath;
439 rv = directoryEnumerator->GetNextFile(getter_AddRefs(currentFolderPath));
440 if (NS_FAILED(rv) || !currentFolderPath) continue;
441
442 nsAutoString currentFolderNameStr; // online name
443 nsAutoString currentFolderDBNameStr; // possibly munged name
444 currentFolderPath->GetLeafName(currentFolderNameStr);
445 // Skip if not an .msf file.
446 // (NOTE: nsShouldIgnoreFile() strips the trailing ".msf" here)
447 if (nsShouldIgnoreFile(currentFolderNameStr)) continue;
448
449 // OK, here we need to get the online name from the folder cache if we can.
450 // If we can, use that to create the sub-folder
451 nsCOMPtr<nsIFile> curFolder =
452 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
453 NS_ENSURE_SUCCESS(rv, rv);
454 nsCOMPtr<nsIFile> dbFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
455 NS_ENSURE_SUCCESS(rv, rv);
456 dbFile->InitWithFile(currentFolderPath);
457 curFolder->InitWithFile(currentFolderPath);
458 // don't strip off the .msf in currentFolderPath.
459 currentFolderPath->SetLeafName(currentFolderNameStr);
460 currentFolderDBNameStr = currentFolderNameStr;
461 nsAutoString utfLeafName = currentFolderNameStr;
462
463 if (curFolder) {
464 nsCOMPtr<nsIMsgFolderCacheElement> cacheElement;
465 rv = GetFolderCacheElemFromFile(dbFile, getter_AddRefs(cacheElement));
466 if (NS_SUCCEEDED(rv) && cacheElement) {
467 nsCString onlineFullUtfName;
468
469 uint32_t folderFlags;
470 rv = cacheElement->GetInt32Property("flags", (int32_t*)&folderFlags);
471 if (NS_SUCCEEDED(rv) &&
472 folderFlags & nsMsgFolderFlags::Virtual) // ignore virtual folders
473 continue;
474 int32_t hierarchyDelimiter;
475 rv = cacheElement->GetInt32Property("hierDelim", &hierarchyDelimiter);
476 if (NS_SUCCEEDED(rv) &&
477 hierarchyDelimiter == kOnlineHierarchySeparatorUnknown) {
478 currentFolderPath->Remove(false);
479 continue; // blow away .msf files for folders with unknown delimiter.
480 }
481 rv = cacheElement->GetStringProperty("onlineName", onlineFullUtfName);
482 if (NS_SUCCEEDED(rv) && !onlineFullUtfName.IsEmpty()) {
483 CopyFolderNameToUTF16(onlineFullUtfName, currentFolderNameStr);
484 char delimiter = 0;
485 GetHierarchyDelimiter(&delimiter);
486 int32_t leafPos = currentFolderNameStr.RFindChar(delimiter);
487 if (leafPos > 0) currentFolderNameStr.Cut(0, leafPos + 1);
488
489 // Take the full online name, and determine the leaf name.
490 CopyUTF8toUTF16(onlineFullUtfName, utfLeafName);
491 leafPos = utfLeafName.RFindChar(delimiter);
492 if (leafPos > 0) utfLeafName.Cut(0, leafPos + 1);
493 }
494 }
495 }
496 // make the imap folder remember the file spec it was created with.
497 nsCOMPtr<nsIFile> msfFilePath =
498 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
499 NS_ENSURE_SUCCESS(rv, rv);
500 msfFilePath->InitWithFile(currentFolderPath);
501 if (NS_SUCCEEDED(rv) && msfFilePath) {
502 // leaf name is the db name w/o .msf (nsShouldIgnoreFile strips it off)
503 // so this trims the .msf off the file spec.
504 msfFilePath->SetLeafName(currentFolderDBNameStr);
505 }
506 // Use the name as the uri for the folder.
507 nsCOMPtr<nsIMsgFolder> child;
508 AddSubfolderWithPath(utfLeafName, msfFilePath, getter_AddRefs(child));
509 if (child) {
510 // use the unicode name as the "pretty" name. Set it so it won't be
511 // automatically computed from the URI.
512 if (!currentFolderNameStr.IsEmpty())
513 child->SetPrettyName(currentFolderNameStr);
514 child->SetMsgDatabase(nullptr);
515 }
516 }
517 return rv;
518 }
519
GetSubFolders(nsTArray<RefPtr<nsIMsgFolder>> & folders)520 NS_IMETHODIMP nsImapMailFolder::GetSubFolders(
521 nsTArray<RefPtr<nsIMsgFolder>>& folders) {
522 bool isServer;
523 nsresult rv = GetIsServer(&isServer);
524 NS_ENSURE_SUCCESS(rv, rv);
525
526 if (!m_initialized) {
527 nsCOMPtr<nsIFile> pathFile;
528 rv = GetFilePath(getter_AddRefs(pathFile));
529 if (NS_FAILED(rv)) return rv;
530
531 // host directory does not need .sbd tacked on
532 if (!isServer) {
533 rv = AddDirectorySeparator(pathFile);
534 if (NS_FAILED(rv)) return rv;
535 }
536
537 m_initialized = true; // need to set this here to avoid infinite recursion
538 // from CreateSubfolders.
539 // we have to treat the root folder specially, because it's name
540 // doesn't end with .sbd
541
542 int32_t newFlags = nsMsgFolderFlags::Mail;
543 bool isDirectory = false;
544 pathFile->IsDirectory(&isDirectory);
545 if (isDirectory) {
546 newFlags |= (nsMsgFolderFlags::Directory | nsMsgFolderFlags::Elided);
547 if (!mIsServer) SetFlag(newFlags);
548 rv = CreateSubFolders(pathFile);
549 }
550 if (isServer) {
551 nsCOMPtr<nsIMsgFolder> inboxFolder;
552
553 GetFolderWithFlags(nsMsgFolderFlags::Inbox, getter_AddRefs(inboxFolder));
554 if (!inboxFolder) {
555 // create an inbox if we don't have one.
556 CreateClientSubfolderInfo("INBOX"_ns, kOnlineHierarchySeparatorUnknown,
557 0, true);
558 }
559 }
560
561 // Force initialisation recursively.
562 for (nsIMsgFolder* f : mSubFolders) {
563 nsTArray<RefPtr<nsIMsgFolder>> dummy;
564 rv = f->GetSubFolders(dummy);
565 if (NS_FAILED(rv)) {
566 break;
567 }
568 }
569
570 UpdateSummaryTotals(false);
571 if (NS_FAILED(rv)) return rv;
572 }
573
574 return nsMsgDBFolder::GetSubFolders(folders);
575 }
576
577 // Makes sure the database is open and exists. If the database is valid then
578 // returns NS_OK. Otherwise returns a failure error value.
GetDatabase()579 nsresult nsImapMailFolder::GetDatabase() {
580 nsresult rv = NS_OK;
581 if (!mDatabase) {
582 nsCOMPtr<nsIMsgDBService> msgDBService =
583 do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
584 NS_ENSURE_SUCCESS(rv, rv);
585
586 // Create the database, blowing it away if it needs to be rebuilt
587 rv = msgDBService->OpenFolderDB(this, false, getter_AddRefs(mDatabase));
588 if (NS_FAILED(rv))
589 rv = msgDBService->CreateNewDB(this, getter_AddRefs(mDatabase));
590
591 NS_ENSURE_SUCCESS(rv, rv);
592
593 // UpdateNewMessages/UpdateSummaryTotals can null mDatabase, so we save a
594 // local copy
595 nsCOMPtr<nsIMsgDatabase> database(mDatabase);
596 UpdateNewMessages();
597 if (mAddListener) database->AddListener(this);
598 UpdateSummaryTotals(true);
599 mDatabase = database;
600 }
601 return rv;
602 }
603
UpdateFolder(nsIMsgWindow * inMsgWindow)604 NS_IMETHODIMP nsImapMailFolder::UpdateFolder(nsIMsgWindow* inMsgWindow) {
605 return UpdateFolderWithListener(inMsgWindow, nullptr);
606 }
607
UpdateFolderWithListener(nsIMsgWindow * aMsgWindow,nsIUrlListener * aUrlListener)608 NS_IMETHODIMP nsImapMailFolder::UpdateFolderWithListener(
609 nsIMsgWindow* aMsgWindow, nsIUrlListener* aUrlListener) {
610 nsresult rv;
611 bool selectFolder = false;
612
613 // If this is the inbox, filters will be applied. Otherwise, we test the
614 // inherited folder property "applyIncomingFilters" (which defaults to empty).
615 // If this inherited property has the string value "true", we will apply
616 // filters even if this is not the inbox folder.
617 nsCString applyIncomingFilters;
618 GetInheritedStringProperty("applyIncomingFilters", applyIncomingFilters);
619 m_applyIncomingFilters = applyIncomingFilters.EqualsLiteral("true");
620
621 nsString folderName;
622 GetPrettyName(folderName);
623 MOZ_LOG(FILTERLOGMODULE, LogLevel::Debug,
624 ("(Imap) nsImapMailFolder::UpdateFolderWithListener() on folder '%s'",
625 NS_ConvertUTF16toUTF8(folderName).get()));
626 if (mFlags & nsMsgFolderFlags::Inbox || m_applyIncomingFilters) {
627 MOZ_LOG(FILTERLOGMODULE, LogLevel::Info,
628 ("(Imap) Preparing filter run on folder '%s'",
629 NS_ConvertUTF16toUTF8(folderName).get()));
630
631 if (!m_filterList) {
632 rv = GetFilterList(aMsgWindow, getter_AddRefs(m_filterList));
633 if (NS_FAILED(rv)) {
634 MOZ_LOG(FILTERLOGMODULE, LogLevel::Error,
635 ("(Imap) Loading of filter list failed"));
636 }
637 }
638
639 // if there's no msg window, but someone is updating the inbox, we're
640 // doing something biff-like, and may download headers, so make biff notify.
641 if (!aMsgWindow && mFlags & nsMsgFolderFlags::Inbox)
642 SetPerformingBiff(true);
643 }
644
645 if (m_filterList) {
646 nsCString listId;
647 m_filterList->GetListId(listId);
648 MOZ_LOG(FILTERLOGMODULE, LogLevel::Info,
649 ("(Imap) Preparing filter list %s", listId.get()));
650 nsCOMPtr<nsIMsgIncomingServer> server;
651 rv = GetServer(getter_AddRefs(server));
652 NS_ENSURE_SUCCESS(rv, rv);
653
654 bool canFileMessagesOnServer = true;
655 rv = server->GetCanFileMessagesOnServer(&canFileMessagesOnServer);
656 // the mdn filter is for filing return receipts into the sent folder
657 // some servers (like AOL mail servers)
658 // can't file to the sent folder, so we don't add the filter for those
659 // servers
660 if (canFileMessagesOnServer) {
661 rv = server->ConfigureTemporaryFilters(m_filterList);
662 NS_ENSURE_SUCCESS(rv, rv);
663 }
664
665 // If a body filter is enabled for an offline folder, delay the filter
666 // application until after message has been downloaded.
667 m_filterListRequiresBody = false;
668
669 if (mFlags & nsMsgFolderFlags::Offline) {
670 nsCOMPtr<nsIMsgFilterService> filterService =
671 do_GetService(NS_MSGFILTERSERVICE_CONTRACTID, &rv);
672 uint32_t filterCount = 0;
673 m_filterList->GetFilterCount(&filterCount);
674 for (uint32_t index = 0; index < filterCount && !m_filterListRequiresBody;
675 ++index) {
676 nsCOMPtr<nsIMsgFilter> filter;
677 m_filterList->GetFilterAt(index, getter_AddRefs(filter));
678 if (!filter) continue;
679 nsMsgFilterTypeType filterType;
680 filter->GetFilterType(&filterType);
681 if (!(filterType & nsMsgFilterType::Incoming)) continue;
682 bool enabled = false;
683 filter->GetEnabled(&enabled);
684 if (!enabled) continue;
685 nsTArray<RefPtr<nsIMsgSearchTerm>> searchTerms;
686 filter->GetSearchTerms(searchTerms);
687 for (nsIMsgSearchTerm* term : searchTerms) {
688 nsMsgSearchAttribValue attrib;
689 rv = term->GetAttrib(&attrib);
690 NS_ENSURE_SUCCESS(rv, rv);
691 if (attrib == nsMsgSearchAttrib::Body)
692 m_filterListRequiresBody = true;
693 else if (attrib == nsMsgSearchAttrib::Custom) {
694 nsAutoCString customId;
695 rv = term->GetCustomId(customId);
696 nsCOMPtr<nsIMsgSearchCustomTerm> customTerm;
697 if (NS_SUCCEEDED(rv) && filterService)
698 rv = filterService->GetCustomTerm(customId,
699 getter_AddRefs(customTerm));
700 bool needsBody = false;
701 if (NS_SUCCEEDED(rv) && customTerm)
702 rv = customTerm->GetNeedsBody(&needsBody);
703 if (NS_SUCCEEDED(rv) && needsBody) m_filterListRequiresBody = true;
704 }
705 if (m_filterListRequiresBody) {
706 break;
707 }
708 }
709
710 // Also check if filter actions need the body, as this
711 // is supported in custom actions.
712 uint32_t numActions = 0;
713 filter->GetActionCount(&numActions);
714 for (uint32_t actionIndex = 0;
715 actionIndex < numActions && !m_filterListRequiresBody;
716 actionIndex++) {
717 nsCOMPtr<nsIMsgRuleAction> action;
718 rv = filter->GetActionAt(actionIndex, getter_AddRefs(action));
719 if (NS_FAILED(rv) || !action) continue;
720
721 nsCOMPtr<nsIMsgFilterCustomAction> customAction;
722 rv = action->GetCustomAction(getter_AddRefs(customAction));
723 if (NS_FAILED(rv) || !customAction) continue;
724
725 bool needsBody = false;
726 customAction->GetNeedsBody(&needsBody);
727 if (needsBody) m_filterListRequiresBody = true;
728 }
729 }
730 }
731 MOZ_LOG(FILTERLOGMODULE, LogLevel::Info,
732 ("(Imap) Filters require the message body: %s",
733 (m_filterListRequiresBody ? "true" : "false")));
734 }
735
736 selectFolder = true;
737
738 bool isServer;
739 rv = GetIsServer(&isServer);
740 if (NS_SUCCEEDED(rv) && isServer) {
741 if (!m_haveDiscoveredAllFolders) {
742 bool hasSubFolders = false;
743 GetHasSubFolders(&hasSubFolders);
744 if (!hasSubFolders) {
745 rv = CreateClientSubfolderInfo(
746 "Inbox"_ns, kOnlineHierarchySeparatorUnknown, 0, false);
747 NS_ENSURE_SUCCESS(rv, rv);
748 }
749 m_haveDiscoveredAllFolders = true;
750 }
751 selectFolder = false;
752 }
753 rv = GetDatabase();
754 if (NS_FAILED(rv)) {
755 ThrowAlertMsg("errorGettingDB", aMsgWindow);
756 return rv;
757 }
758 bool canOpenThisFolder = true;
759 GetCanOpenFolder(&canOpenThisFolder);
760
761 bool hasOfflineEvents = false;
762 GetFlag(nsMsgFolderFlags::OfflineEvents, &hasOfflineEvents);
763
764 if (!WeAreOffline()) {
765 if (hasOfflineEvents) {
766 // hold a reference to the offline sync object. If ProcessNextOperation
767 // runs a url, a reference will be added to it. Otherwise, it will get
768 // destroyed when the refptr goes out of scope.
769 RefPtr<nsImapOfflineSync> goOnline =
770 new nsImapOfflineSync(aMsgWindow, this, this);
771 if (goOnline) {
772 m_urlListener = aUrlListener;
773 return goOnline->ProcessNextOperation();
774 }
775 }
776 }
777
778 // Check it we're password protecting the local store.
779 if (!PromptForMasterPasswordIfNecessary()) return NS_ERROR_FAILURE;
780
781 if (!canOpenThisFolder) selectFolder = false;
782 // don't run select if we can't select the folder...
783 if (NS_SUCCEEDED(rv) && !m_urlRunning && selectFolder) {
784 nsCOMPtr<nsIImapService> imapService =
785 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
786 NS_ENSURE_SUCCESS(rv, rv);
787
788 /* Do a discovery in its own url if needed. Do before SELECT url. */
789 nsCOMPtr<nsIImapHostSessionList> hostSession =
790 do_GetService(kCImapHostSessionList, &rv);
791 if (NS_SUCCEEDED(rv) && hostSession) {
792 bool foundMailboxesAlready = false;
793 nsCString serverKey;
794 GetServerKey(serverKey);
795 hostSession->GetHaveWeEverDiscoveredFoldersForHost(serverKey.get(),
796 foundMailboxesAlready);
797 if (!foundMailboxesAlready) {
798 bool discoveryInProgress = false;
799 // See if discovery in progress and not yet finished.
800 hostSession->GetDiscoveryForHostInProgress(serverKey.get(),
801 discoveryInProgress);
802 if (!discoveryInProgress) {
803 nsCOMPtr<nsIMsgFolder> rootFolder;
804 rv = GetRootFolder(getter_AddRefs(rootFolder));
805 if (NS_SUCCEEDED(rv) && rootFolder) {
806 rv = imapService->DiscoverAllFolders(rootFolder, this, aMsgWindow,
807 nullptr);
808 if (NS_SUCCEEDED(rv))
809 hostSession->SetDiscoveryForHostInProgress(serverKey.get(), true);
810 }
811 }
812 }
813 }
814
815 nsCOMPtr<nsIURI> url;
816 rv = imapService->SelectFolder(this, m_urlListener, aMsgWindow,
817 getter_AddRefs(url));
818 if (NS_SUCCEEDED(rv)) {
819 m_urlRunning = true;
820 m_updatingFolder = true;
821 }
822 if (url) {
823 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(url, &rv);
824 NS_ENSURE_SUCCESS(rv, rv);
825 mailnewsUrl->RegisterListener(this);
826 m_urlListener = aUrlListener;
827 }
828
829 // Allow IMAP folder auto-compact to occur when online or offline.
830 if (aMsgWindow) AutoCompact(aMsgWindow);
831
832 if (rv == NS_MSG_ERROR_OFFLINE || rv == NS_BINDING_ABORTED) {
833 rv = NS_OK;
834 NotifyFolderEvent(kFolderLoaded);
835 }
836 } else if (NS_SUCCEEDED(rv)) // tell the front end that the folder is loaded
837 // if we're not going to
838 { // actually run a url.
839 if (!m_updatingFolder) // if we're already running an update url, we'll let
840 // that one send the folder loaded
841 NotifyFolderEvent(kFolderLoaded);
842 }
843 return rv;
844 }
845
CreateSubfolder(const nsAString & folderName,nsIMsgWindow * msgWindow)846 NS_IMETHODIMP nsImapMailFolder::CreateSubfolder(const nsAString& folderName,
847 nsIMsgWindow* msgWindow) {
848 if (folderName.IsEmpty()) return NS_MSG_ERROR_INVALID_FOLDER_NAME;
849
850 nsresult rv;
851 nsAutoString trashName;
852 GetTrashFolderName(trashName);
853 if (folderName.Equals(trashName)) // Trash , a special folder
854 {
855 ThrowAlertMsg("folderExists", msgWindow);
856 return NS_MSG_FOLDER_EXISTS;
857 }
858 if (mIsServer &&
859 folderName.LowerCaseEqualsLiteral("inbox")) // Inbox, a special folder
860 {
861 ThrowAlertMsg("folderExists", msgWindow);
862 return NS_MSG_FOLDER_EXISTS;
863 }
864
865 nsCOMPtr<nsIImapService> imapService =
866 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
867 NS_ENSURE_SUCCESS(rv, rv);
868
869 return imapService->CreateFolder(this, folderName, this, nullptr);
870 }
871
CreateClientSubfolderInfo(const nsACString & folderName,char hierarchyDelimiter,int32_t flags,bool suppressNotification)872 NS_IMETHODIMP nsImapMailFolder::CreateClientSubfolderInfo(
873 const nsACString& folderName, char hierarchyDelimiter, int32_t flags,
874 bool suppressNotification) {
875 nsresult rv = NS_OK;
876
877 // Get a directory based on our current path.
878 nsCOMPtr<nsIFile> path;
879 rv = CreateDirectoryForFolder(getter_AddRefs(path));
880 if (NS_FAILED(rv)) return rv;
881
882 NS_ConvertUTF8toUTF16 leafName(folderName);
883 nsAutoString folderNameStr;
884 nsAutoString parentName = leafName;
885 // use RFind, because folder can start with a delimiter and
886 // not be a leaf folder.
887 int32_t folderStart = leafName.RFindChar('/');
888 if (folderStart > 0) {
889 nsCOMPtr<nsIMsgImapMailFolder> imapFolder;
890 nsAutoCString uri(mURI);
891 leafName.Assign(Substring(parentName, folderStart + 1));
892 parentName.SetLength(folderStart);
893
894 rv = CreateDirectoryForFolder(getter_AddRefs(path));
895 NS_ENSURE_SUCCESS(rv, rv);
896 uri.Append('/');
897 uri.Append(NS_ConvertUTF16toUTF8(parentName));
898 nsCOMPtr<nsIMsgFolder> folder;
899 rv = GetOrCreateFolder(uri, getter_AddRefs(folder));
900 NS_ENSURE_SUCCESS(rv, rv);
901 imapFolder = do_QueryInterface(folder, &rv);
902 NS_ENSURE_SUCCESS(rv, rv);
903 nsAutoCString leafnameC;
904 CopyUTF16toUTF8(leafName, leafnameC);
905 return imapFolder->CreateClientSubfolderInfo(leafnameC, hierarchyDelimiter,
906 flags, suppressNotification);
907 }
908
909 // if we get here, it's really a leaf, and "this" is the parent.
910 folderNameStr = leafName;
911
912 // Create an empty database for this mail folder, set its name from the user
913 nsCOMPtr<nsIMsgDatabase> mailDBFactory;
914 nsCOMPtr<nsIMsgFolder> child;
915
916 nsCOMPtr<nsIMsgDBService> msgDBService =
917 do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
918 NS_ENSURE_SUCCESS(rv, rv);
919 nsCOMPtr<nsIMsgDatabase> unusedDB;
920 nsCOMPtr<nsIFile> dbFile;
921
922 // warning, path will be changed
923 rv = CreateFileForDB(folderNameStr, path, getter_AddRefs(dbFile));
924 NS_ENSURE_SUCCESS(rv, rv);
925
926 // Now let's create the actual new folder
927 rv = AddSubfolderWithPath(folderNameStr, dbFile, getter_AddRefs(child), true);
928 NS_ENSURE_SUCCESS(rv, rv);
929 rv = msgDBService->OpenMailDBFromFile(dbFile, child, true, true,
930 getter_AddRefs(unusedDB));
931 if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING) rv = NS_OK;
932
933 if (NS_SUCCEEDED(rv) && unusedDB) {
934 // need to set the folder name
935 nsCOMPtr<nsIDBFolderInfo> folderInfo;
936 rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
937 nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(child, &rv);
938 if (NS_SUCCEEDED(rv)) {
939 nsAutoCString onlineName(m_onlineFolderName);
940 if (!onlineName.IsEmpty()) onlineName.Append(hierarchyDelimiter);
941 onlineName.Append(NS_ConvertUTF16toUTF8(folderNameStr));
942 imapFolder->SetVerifiedAsOnlineFolder(true);
943 imapFolder->SetOnlineName(onlineName);
944 imapFolder->SetHierarchyDelimiter(hierarchyDelimiter);
945 imapFolder->SetBoxFlags(flags);
946
947 // Now that the child is created and the boxflags are set we can be sure
948 // all special folder flags are known. The child may get its flags already
949 // in AddSubfolderWithPath if they were in FolderCache, but that's
950 // not always the case.
951 uint32_t flags = 0;
952 child->GetFlags(&flags);
953
954 // Set the offline use flag for the newly created folder if the
955 // offline_download preference is true, unless it's the Trash or Junk
956 // folder.
957 if (!(flags & (nsMsgFolderFlags::Trash | nsMsgFolderFlags::Junk))) {
958 nsCOMPtr<nsIImapIncomingServer> imapServer;
959 rv = GetImapIncomingServer(getter_AddRefs(imapServer));
960 NS_ENSURE_SUCCESS(rv, rv);
961 bool setNewFoldersForOffline = false;
962 rv = imapServer->GetOfflineDownload(&setNewFoldersForOffline);
963 if (NS_SUCCEEDED(rv) && setNewFoldersForOffline)
964 flags |= nsMsgFolderFlags::Offline;
965 } else {
966 flags &= ~nsMsgFolderFlags::Offline; // clear offline flag if set
967 }
968
969 flags |= nsMsgFolderFlags::Elided;
970 child->SetFlags(flags);
971
972 nsString unicodeName;
973 rv = CopyFolderNameToUTF16(nsCString(folderName), unicodeName);
974 if (NS_SUCCEEDED(rv)) child->SetPrettyName(unicodeName);
975
976 // store the online name as the mailbox name in the db folder info
977 // I don't think anyone uses the mailbox name, so we'll use it
978 // to restore the online name when blowing away an imap db.
979 if (folderInfo)
980 folderInfo->SetMailboxName(NS_ConvertUTF8toUTF16(onlineName));
981 }
982
983 unusedDB->SetSummaryValid(true);
984 unusedDB->Commit(nsMsgDBCommitType::kLargeCommit);
985 unusedDB->Close(true);
986 // don't want to hold onto this newly created db.
987 child->SetMsgDatabase(nullptr);
988 }
989
990 if (!suppressNotification) {
991 if (NS_SUCCEEDED(rv) && child) {
992 NotifyItemAdded(child);
993 child->NotifyFolderEvent(kFolderCreateCompleted);
994 nsCOMPtr<nsIMsgFolderNotificationService> notifier(
995 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
996 if (notifier) notifier->NotifyFolderAdded(child);
997 } else {
998 NotifyFolderEvent(kFolderCreateFailed);
999 }
1000 }
1001 return rv;
1002 }
1003
List()1004 NS_IMETHODIMP nsImapMailFolder::List() {
1005 nsresult rv;
1006 nsCOMPtr<nsIImapService> imapService =
1007 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
1008 NS_ENSURE_SUCCESS(rv, rv);
1009 return imapService->ListFolder(this, this, nullptr);
1010 }
1011
RemoveLocalSelf()1012 NS_IMETHODIMP nsImapMailFolder::RemoveLocalSelf() {
1013 // Kill the local folder and its storage.
1014 return nsMsgDBFolder::DeleteSelf(nullptr);
1015 }
1016
CreateStorageIfMissing(nsIUrlListener * urlListener)1017 NS_IMETHODIMP nsImapMailFolder::CreateStorageIfMissing(
1018 nsIUrlListener* urlListener) {
1019 nsresult rv = NS_OK;
1020 nsCOMPtr<nsIMsgFolder> msgParent;
1021 GetParent(getter_AddRefs(msgParent));
1022
1023 // parent is probably not set because *this* was probably created by rdf
1024 // and not by folder discovery. So, we have to compute the parent.
1025 if (!msgParent) {
1026 nsAutoCString folderName(mURI);
1027
1028 int32_t leafPos = folderName.RFindChar('/');
1029 nsAutoCString parentName(folderName);
1030
1031 if (leafPos > 0) {
1032 // If there is a hierarchy, there is a parent.
1033 // Don't strip off slash if it's the first character
1034 parentName.SetLength(leafPos);
1035 rv = GetOrCreateFolder(parentName, getter_AddRefs(msgParent));
1036 NS_ENSURE_SUCCESS(rv, rv);
1037 }
1038 }
1039 if (msgParent) {
1040 nsString folderName;
1041 GetName(folderName);
1042 nsresult rv;
1043 nsCOMPtr<nsIImapService> imapService =
1044 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
1045 NS_ENSURE_SUCCESS(rv, rv);
1046 nsCOMPtr<nsIURI> uri;
1047 imapService->EnsureFolderExists(msgParent, folderName, nullptr, urlListener,
1048 getter_AddRefs(uri));
1049 }
1050 return rv;
1051 }
1052
GetVerifiedAsOnlineFolder(bool * aVerifiedAsOnlineFolder)1053 NS_IMETHODIMP nsImapMailFolder::GetVerifiedAsOnlineFolder(
1054 bool* aVerifiedAsOnlineFolder) {
1055 NS_ENSURE_ARG_POINTER(aVerifiedAsOnlineFolder);
1056 *aVerifiedAsOnlineFolder = m_verifiedAsOnlineFolder;
1057 return NS_OK;
1058 }
1059
SetVerifiedAsOnlineFolder(bool aVerifiedAsOnlineFolder)1060 NS_IMETHODIMP nsImapMailFolder::SetVerifiedAsOnlineFolder(
1061 bool aVerifiedAsOnlineFolder) {
1062 m_verifiedAsOnlineFolder = aVerifiedAsOnlineFolder;
1063 // mark ancestors as verified as well
1064 if (aVerifiedAsOnlineFolder) {
1065 nsCOMPtr<nsIMsgFolder> parent;
1066 do {
1067 GetParent(getter_AddRefs(parent));
1068 if (parent) {
1069 nsCOMPtr<nsIMsgImapMailFolder> imapParent = do_QueryInterface(parent);
1070 if (imapParent) {
1071 bool verifiedOnline;
1072 imapParent->GetVerifiedAsOnlineFolder(&verifiedOnline);
1073 if (verifiedOnline) break;
1074 imapParent->SetVerifiedAsOnlineFolder(true);
1075 }
1076 }
1077 } while (parent);
1078 }
1079 return NS_OK;
1080 }
1081
GetOnlineDelimiter(char * onlineDelimiter)1082 NS_IMETHODIMP nsImapMailFolder::GetOnlineDelimiter(char* onlineDelimiter) {
1083 return GetHierarchyDelimiter(onlineDelimiter);
1084 }
1085
SetHierarchyDelimiter(char aHierarchyDelimiter)1086 NS_IMETHODIMP nsImapMailFolder::SetHierarchyDelimiter(
1087 char aHierarchyDelimiter) {
1088 m_hierarchyDelimiter = aHierarchyDelimiter;
1089 return NS_OK;
1090 }
1091
GetHierarchyDelimiter(char * aHierarchyDelimiter)1092 NS_IMETHODIMP nsImapMailFolder::GetHierarchyDelimiter(
1093 char* aHierarchyDelimiter) {
1094 NS_ENSURE_ARG_POINTER(aHierarchyDelimiter);
1095 if (mIsServer) {
1096 // if it's the root folder, we don't know the delimiter. So look at the
1097 // first child.
1098 int32_t count = mSubFolders.Count();
1099 if (count > 0) {
1100 nsCOMPtr<nsIMsgImapMailFolder> childFolder(
1101 do_QueryInterface(mSubFolders[0]));
1102 if (childFolder) {
1103 nsresult rv = childFolder->GetHierarchyDelimiter(aHierarchyDelimiter);
1104 // some code uses m_hierarchyDelimiter directly, so we should set it.
1105 m_hierarchyDelimiter = *aHierarchyDelimiter;
1106 return rv;
1107 }
1108 }
1109 }
1110 ReadDBFolderInfo(false); // update cache first.
1111 *aHierarchyDelimiter = m_hierarchyDelimiter;
1112 return NS_OK;
1113 }
1114
SetBoxFlags(int32_t aBoxFlags)1115 NS_IMETHODIMP nsImapMailFolder::SetBoxFlags(int32_t aBoxFlags) {
1116 ReadDBFolderInfo(false);
1117
1118 m_boxFlags = aBoxFlags;
1119 uint32_t newFlags = mFlags;
1120
1121 newFlags |= nsMsgFolderFlags::ImapBox;
1122
1123 if (m_boxFlags & kNoinferiors)
1124 newFlags |= nsMsgFolderFlags::ImapNoinferiors;
1125 else
1126 newFlags &= ~nsMsgFolderFlags::ImapNoinferiors;
1127 if (m_boxFlags & kNoselect)
1128 newFlags |= nsMsgFolderFlags::ImapNoselect;
1129 else
1130 newFlags &= ~nsMsgFolderFlags::ImapNoselect;
1131 if (m_boxFlags & kPublicMailbox)
1132 newFlags |= nsMsgFolderFlags::ImapPublic;
1133 else
1134 newFlags &= ~nsMsgFolderFlags::ImapPublic;
1135 if (m_boxFlags & kOtherUsersMailbox)
1136 newFlags |= nsMsgFolderFlags::ImapOtherUser;
1137 else
1138 newFlags &= ~nsMsgFolderFlags::ImapOtherUser;
1139 if (m_boxFlags & kPersonalMailbox)
1140 newFlags |= nsMsgFolderFlags::ImapPersonal;
1141 else
1142 newFlags &= ~nsMsgFolderFlags::ImapPersonal;
1143
1144 // The following are all flags returned by XLIST.
1145 // nsImapIncomingServer::DiscoveryDone checks for these folders.
1146 if (m_boxFlags & kImapDrafts) newFlags |= nsMsgFolderFlags::Drafts;
1147
1148 if (m_boxFlags & kImapSpam) newFlags |= nsMsgFolderFlags::Junk;
1149
1150 if (m_boxFlags & kImapSent) newFlags |= nsMsgFolderFlags::SentMail;
1151
1152 if (m_boxFlags & kImapInbox) newFlags |= nsMsgFolderFlags::Inbox;
1153
1154 if (m_boxFlags & kImapXListTrash) {
1155 nsCOMPtr<nsIImapIncomingServer> imapServer;
1156 nsMsgImapDeleteModel deleteModel = nsMsgImapDeleteModels::MoveToTrash;
1157 (void)GetImapIncomingServer(getter_AddRefs(imapServer));
1158 if (imapServer) imapServer->GetDeleteModel(&deleteModel);
1159 if (deleteModel == nsMsgImapDeleteModels::MoveToTrash)
1160 newFlags |= nsMsgFolderFlags::Trash;
1161 }
1162 // Treat the GMail all mail folder as the archive folder.
1163 if (m_boxFlags & (kImapAllMail | kImapArchive))
1164 newFlags |= nsMsgFolderFlags::Archive;
1165
1166 SetFlags(newFlags);
1167 return NS_OK;
1168 }
1169
GetBoxFlags(int32_t * aBoxFlags)1170 NS_IMETHODIMP nsImapMailFolder::GetBoxFlags(int32_t* aBoxFlags) {
1171 NS_ENSURE_ARG_POINTER(aBoxFlags);
1172 *aBoxFlags = m_boxFlags;
1173 return NS_OK;
1174 }
1175
GetExplicitlyVerify(bool * aExplicitlyVerify)1176 NS_IMETHODIMP nsImapMailFolder::GetExplicitlyVerify(bool* aExplicitlyVerify) {
1177 NS_ENSURE_ARG_POINTER(aExplicitlyVerify);
1178 *aExplicitlyVerify = m_explicitlyVerify;
1179 return NS_OK;
1180 }
1181
SetExplicitlyVerify(bool aExplicitlyVerify)1182 NS_IMETHODIMP nsImapMailFolder::SetExplicitlyVerify(bool aExplicitlyVerify) {
1183 m_explicitlyVerify = aExplicitlyVerify;
1184 return NS_OK;
1185 }
1186
GetNoSelect(bool * aResult)1187 NS_IMETHODIMP nsImapMailFolder::GetNoSelect(bool* aResult) {
1188 NS_ENSURE_ARG_POINTER(aResult);
1189 return GetFlag(nsMsgFolderFlags::ImapNoselect, aResult);
1190 }
1191
ApplyRetentionSettings()1192 NS_IMETHODIMP nsImapMailFolder::ApplyRetentionSettings() {
1193 int32_t numDaysToKeepOfflineMsgs = -1;
1194
1195 // Check if we've limited the offline storage by age.
1196 nsCOMPtr<nsIImapIncomingServer> imapServer;
1197 nsresult rv = GetImapIncomingServer(getter_AddRefs(imapServer));
1198 NS_ENSURE_SUCCESS(rv, rv);
1199 imapServer->GetAutoSyncMaxAgeDays(&numDaysToKeepOfflineMsgs);
1200
1201 nsCOMPtr<nsIMsgDatabase> holdDBOpen;
1202 if (numDaysToKeepOfflineMsgs > 0) {
1203 bool dbWasCached = mDatabase != nullptr;
1204 rv = GetDatabase();
1205 NS_ENSURE_SUCCESS(rv, rv);
1206 nsCOMPtr<nsIMsgEnumerator> hdrs;
1207 rv = mDatabase->EnumerateMessages(getter_AddRefs(hdrs));
1208 NS_ENSURE_SUCCESS(rv, rv);
1209 bool hasMore = false;
1210
1211 PRTime cutOffDay =
1212 MsgConvertAgeInDaysToCutoffDate(numDaysToKeepOfflineMsgs);
1213
1214 // so now cutOffDay is the PRTime cut-off point. Any offline msg with
1215 // a date less than that will get marked for pending removal.
1216 while (NS_SUCCEEDED(rv = hdrs->HasMoreElements(&hasMore)) && hasMore) {
1217 nsCOMPtr<nsIMsgDBHdr> header;
1218 rv = hdrs->GetNext(getter_AddRefs(header));
1219 NS_ENSURE_SUCCESS(rv, rv);
1220
1221 uint32_t msgFlags;
1222 PRTime msgDate;
1223 header->GetFlags(&msgFlags);
1224 if (msgFlags & nsMsgMessageFlags::Offline) {
1225 header->GetDate(&msgDate);
1226 MarkPendingRemoval(header, msgDate < cutOffDay);
1227 // I'm horribly tempted to break out of the loop if we've found
1228 // a message after the cut-off date, because messages will most likely
1229 // be in date order in the db, but there are always edge cases.
1230 }
1231 }
1232 if (!dbWasCached) {
1233 holdDBOpen = mDatabase;
1234 mDatabase = nullptr;
1235 }
1236 }
1237 return nsMsgDBFolder::ApplyRetentionSettings();
1238 }
1239
1240 /**
1241 * The listener will get called when both the online expunge and the offline
1242 * store compaction are finished (if the latter is needed).
1243 */
Compact(nsIUrlListener * aListener,nsIMsgWindow * aMsgWindow)1244 NS_IMETHODIMP nsImapMailFolder::Compact(nsIUrlListener* aListener,
1245 nsIMsgWindow* aMsgWindow) {
1246 GetDatabase();
1247 // now's a good time to apply the retention settings. If we do delete any
1248 // messages, the expunge is going to have to wait until the delete to
1249 // finish before it can run, but the multiple-connection protection code
1250 // should handle that.
1251 if (mDatabase) ApplyRetentionSettings();
1252
1253 m_urlListener = aListener;
1254 // We should be able to compact the offline store now that this should
1255 // just be called by the UI.
1256 if (aMsgWindow && (mFlags & nsMsgFolderFlags::Offline)) {
1257 m_compactingOfflineStore = true;
1258 CompactOfflineStore(aMsgWindow, this);
1259 }
1260 if (WeAreOffline()) return NS_OK;
1261 m_expunging = true;
1262 return Expunge(this, aMsgWindow);
1263 }
1264
1265 NS_IMETHODIMP
NotifyCompactCompleted()1266 nsImapMailFolder::NotifyCompactCompleted() {
1267 if (!m_expunging && m_urlListener) {
1268 m_urlListener->OnStopRunningUrl(nullptr, NS_OK);
1269 m_urlListener = nullptr;
1270 }
1271 m_compactingOfflineStore = false;
1272 return NS_OK;
1273 }
1274
MarkPendingRemoval(nsIMsgDBHdr * aHdr,bool aMark)1275 NS_IMETHODIMP nsImapMailFolder::MarkPendingRemoval(nsIMsgDBHdr* aHdr,
1276 bool aMark) {
1277 NS_ENSURE_ARG_POINTER(aHdr);
1278 uint32_t offlineMessageSize;
1279 aHdr->GetOfflineMessageSize(&offlineMessageSize);
1280 aHdr->SetStringProperty("pendingRemoval", aMark ? "1" : "");
1281 if (!aMark) return NS_OK;
1282 nsresult rv = GetDatabase();
1283 NS_ENSURE_SUCCESS(rv, rv);
1284 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
1285 rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
1286 NS_ENSURE_SUCCESS(rv, rv);
1287 return dbFolderInfo->ChangeExpungedBytes(offlineMessageSize);
1288 }
1289
Expunge(nsIUrlListener * aListener,nsIMsgWindow * aMsgWindow)1290 NS_IMETHODIMP nsImapMailFolder::Expunge(nsIUrlListener* aListener,
1291 nsIMsgWindow* aMsgWindow) {
1292 nsresult rv;
1293 nsCOMPtr<nsIImapService> imapService =
1294 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
1295 NS_ENSURE_SUCCESS(rv, rv);
1296
1297 return imapService->Expunge(this, aListener, aMsgWindow, nullptr);
1298 }
1299
CompactAll(nsIUrlListener * aListener,nsIMsgWindow * aMsgWindow,bool aCompactOfflineAlso)1300 NS_IMETHODIMP nsImapMailFolder::CompactAll(nsIUrlListener* aListener,
1301 nsIMsgWindow* aMsgWindow,
1302 bool aCompactOfflineAlso) {
1303 nsresult rv;
1304 nsTArray<RefPtr<nsIMsgFolder>> folderArray;
1305 nsTArray<RefPtr<nsIMsgFolder>> offlineFolderArray;
1306
1307 nsCOMPtr<nsIMsgFolder> rootFolder;
1308 rv = GetRootFolder(getter_AddRefs(rootFolder));
1309 if (NS_SUCCEEDED(rv) && rootFolder) {
1310 nsTArray<RefPtr<nsIMsgFolder>> allDescendants;
1311 rootFolder->GetDescendants(allDescendants);
1312 for (auto folder : allDescendants) {
1313 uint32_t folderFlags;
1314 folder->GetFlags(&folderFlags);
1315 if (!(folderFlags &
1316 (nsMsgFolderFlags::Virtual | nsMsgFolderFlags::ImapNoselect))) {
1317 folderArray.AppendElement(folder);
1318 if (aCompactOfflineAlso) offlineFolderArray.AppendElement(folder);
1319 }
1320 }
1321 if (folderArray.IsEmpty()) return NotifyCompactCompleted();
1322 }
1323 nsCOMPtr<nsIMsgFolderCompactor> folderCompactor =
1324 do_CreateInstance(NS_MSGLOCALFOLDERCOMPACTOR_CONTRACTID, &rv);
1325 NS_ENSURE_SUCCESS(rv, rv);
1326 return folderCompactor->CompactFolders(folderArray, offlineFolderArray,
1327 aListener, aMsgWindow);
1328 }
1329
UpdateStatus(nsIUrlListener * aListener,nsIMsgWindow * aMsgWindow)1330 NS_IMETHODIMP nsImapMailFolder::UpdateStatus(nsIUrlListener* aListener,
1331 nsIMsgWindow* aMsgWindow) {
1332 nsresult rv;
1333 nsCOMPtr<nsIImapService> imapService =
1334 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
1335 NS_ENSURE_SUCCESS(rv, rv);
1336
1337 nsCOMPtr<nsIURI> uri;
1338 rv = imapService->UpdateFolderStatus(this, aListener, getter_AddRefs(uri));
1339 if (uri && !aMsgWindow) {
1340 nsCOMPtr<nsIMsgMailNewsUrl> mailNewsUrl = do_QueryInterface(uri, &rv);
1341 NS_ENSURE_SUCCESS(rv, rv);
1342 // if no msg window, we won't put up error messages (this is almost
1343 // certainly a biff-inspired status)
1344 mailNewsUrl->SetSuppressErrorMsgs(true);
1345 }
1346 return rv;
1347 }
1348
EmptyTrash(nsIMsgWindow * aMsgWindow,nsIUrlListener * aListener)1349 NS_IMETHODIMP nsImapMailFolder::EmptyTrash(nsIMsgWindow* aMsgWindow,
1350 nsIUrlListener* aListener) {
1351 nsCOMPtr<nsIMsgFolder> trashFolder;
1352 nsresult rv = GetTrashFolder(getter_AddRefs(trashFolder));
1353 if (NS_SUCCEEDED(rv)) {
1354 nsCOMPtr<nsIMsgAccountManager> accountManager =
1355 do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
1356 NS_ENSURE_SUCCESS(rv, rv);
1357 // if we are emptying trash on exit and we are an aol server then don't
1358 // perform this operation because it's causing a hang that we haven't been
1359 // able to figure out yet this is an rtm fix and we'll look for the right
1360 // solution post rtm.
1361 bool empytingOnExit = false;
1362 accountManager->GetEmptyTrashInProgress(&empytingOnExit);
1363 if (empytingOnExit) {
1364 nsCOMPtr<nsIImapIncomingServer> imapServer;
1365 rv = GetImapIncomingServer(getter_AddRefs(imapServer));
1366 if (imapServer) {
1367 bool isAOLServer = false;
1368 imapServer->GetIsAOLServer(&isAOLServer);
1369 if (isAOLServer)
1370 return NS_ERROR_FAILURE; // we will not be performing an empty
1371 // trash....
1372 } // if we fetched an imap server
1373 } // if emptying trash on exit which is done through the account manager.
1374
1375 if (WeAreOffline()) {
1376 nsCOMPtr<nsIMsgDatabase> trashDB;
1377 rv = trashFolder->GetMsgDatabase(getter_AddRefs(trashDB));
1378 if (trashDB) {
1379 nsMsgKey fakeKey;
1380 trashDB->GetNextFakeOfflineMsgKey(&fakeKey);
1381
1382 nsCOMPtr<nsIMsgOfflineImapOperation> op;
1383 rv = trashDB->GetOfflineOpForKey(fakeKey, true, getter_AddRefs(op));
1384 trashFolder->SetFlag(nsMsgFolderFlags::OfflineEvents);
1385 op->SetOperation(nsIMsgOfflineImapOperation::kDeleteAllMsgs);
1386 }
1387 return rv;
1388 }
1389
1390 nsCOMPtr<nsIImapService> imapService =
1391 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
1392 NS_ENSURE_SUCCESS(rv, rv);
1393
1394 if (aListener)
1395 rv = imapService->DeleteAllMessages(trashFolder, aListener, nullptr);
1396 else {
1397 nsCOMPtr<nsIUrlListener> urlListener = do_QueryInterface(trashFolder);
1398 rv = imapService->DeleteAllMessages(trashFolder, urlListener, nullptr);
1399 }
1400 // Return an error if this failed. We want the empty trash on exit code
1401 // to know if this fails so that it doesn't block waiting for empty trash to
1402 // finish.
1403 NS_ENSURE_SUCCESS(rv, rv);
1404
1405 // Delete any subfolders under Trash.
1406 nsTArray<RefPtr<nsIMsgFolder>> subFolders;
1407 rv = trashFolder->GetSubFolders(subFolders);
1408 NS_ENSURE_SUCCESS(rv, rv);
1409 while (!subFolders.IsEmpty()) {
1410 RefPtr<nsIMsgFolder> f = subFolders.PopLastElement();
1411 rv = trashFolder->PropagateDelete(f, true, aMsgWindow);
1412 NS_ENSURE_SUCCESS(rv, rv);
1413 }
1414
1415 nsCOMPtr<nsIDBFolderInfo> transferInfo;
1416 rv = trashFolder->GetDBTransferInfo(getter_AddRefs(transferInfo));
1417 NS_ENSURE_SUCCESS(rv, rv);
1418 // Bulk-delete all the messages by deleting the msf file and storage.
1419 // This is a little kludgy.
1420 rv = trashFolder->DeleteStorage();
1421 NS_ENSURE_SUCCESS(rv, rv);
1422 trashFolder->SetDBTransferInfo(transferInfo);
1423 trashFolder->SetSizeOnDisk(0);
1424
1425 // The trash folder has effectively been deleted.
1426 nsCOMPtr<nsIMsgFolderNotificationService> notifier(
1427 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
1428 if (notifier) notifier->NotifyFolderDeleted(trashFolder);
1429
1430 return NS_OK;
1431 }
1432 return rv;
1433 }
1434
DeleteStorage()1435 NS_IMETHODIMP nsImapMailFolder::DeleteStorage() {
1436 nsresult rv = nsMsgDBFolder::DeleteStorage();
1437
1438 // Should notify nsIMsgFolderListeners about the folder getting deleted?
1439 return rv;
1440 }
1441
Rename(const nsAString & newName,nsIMsgWindow * msgWindow)1442 NS_IMETHODIMP nsImapMailFolder::Rename(const nsAString& newName,
1443 nsIMsgWindow* msgWindow) {
1444 if (mFlags & nsMsgFolderFlags::Virtual)
1445 return nsMsgDBFolder::Rename(newName, msgWindow);
1446 nsresult rv;
1447 nsAutoString newNameStr(newName);
1448 if (newNameStr.FindChar(m_hierarchyDelimiter, 0) != kNotFound) {
1449 nsCOMPtr<nsIDocShell> docShell;
1450 if (msgWindow) msgWindow->GetRootDocShell(getter_AddRefs(docShell));
1451 if (docShell) {
1452 nsCOMPtr<nsIStringBundle> bundle;
1453 rv = IMAPGetStringBundle(getter_AddRefs(bundle));
1454 if (NS_SUCCEEDED(rv) && bundle) {
1455 AutoTArray<nsString, 1> formatStrings;
1456 formatStrings.AppendElement()->Append(m_hierarchyDelimiter);
1457 nsString alertString;
1458 rv = bundle->FormatStringFromName("imapSpecialChar2", formatStrings,
1459 alertString);
1460 nsCOMPtr<nsIPrompt> dialog(do_GetInterface(docShell));
1461 // setting up the dialog title
1462 nsCOMPtr<nsIMsgIncomingServer> server;
1463 rv = GetServer(getter_AddRefs(server));
1464 NS_ENSURE_SUCCESS(rv, rv);
1465 nsString dialogTitle;
1466 nsString accountName;
1467 rv = server->GetPrettyName(accountName);
1468 NS_ENSURE_SUCCESS(rv, rv);
1469 AutoTArray<nsString, 1> titleParams = {accountName};
1470 rv = bundle->FormatStringFromName("imapAlertDialogTitle", titleParams,
1471 dialogTitle);
1472
1473 if (dialog && !alertString.IsEmpty())
1474 dialog->Alert(dialogTitle.get(), alertString.get());
1475 }
1476 }
1477 return NS_ERROR_FAILURE;
1478 }
1479 nsCOMPtr<nsIImapIncomingServer> incomingImapServer;
1480 GetImapIncomingServer(getter_AddRefs(incomingImapServer));
1481 if (incomingImapServer) RecursiveCloseActiveConnections(incomingImapServer);
1482
1483 nsCOMPtr<nsIImapService> imapService =
1484 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
1485 NS_ENSURE_SUCCESS(rv, rv);
1486 return imapService->RenameLeaf(this, newName, this, msgWindow, nullptr);
1487 }
1488
RecursiveCloseActiveConnections(nsIImapIncomingServer * incomingImapServer)1489 NS_IMETHODIMP nsImapMailFolder::RecursiveCloseActiveConnections(
1490 nsIImapIncomingServer* incomingImapServer) {
1491 NS_ENSURE_ARG(incomingImapServer);
1492
1493 nsCOMPtr<nsIMsgImapMailFolder> folder;
1494 int32_t count = mSubFolders.Count();
1495 for (int32_t i = 0; i < count; i++) {
1496 folder = do_QueryInterface(mSubFolders[i]);
1497 if (folder) folder->RecursiveCloseActiveConnections(incomingImapServer);
1498
1499 incomingImapServer->CloseConnectionForFolder(mSubFolders[i]);
1500 }
1501 return NS_OK;
1502 }
1503
1504 // this is called *after* we've done the rename on the server.
PrepareToRename()1505 NS_IMETHODIMP nsImapMailFolder::PrepareToRename() {
1506 nsCOMPtr<nsIMsgImapMailFolder> folder;
1507 int32_t count = mSubFolders.Count();
1508 for (int32_t i = 0; i < count; i++) {
1509 folder = do_QueryInterface(mSubFolders[i]);
1510 if (folder) folder->PrepareToRename();
1511 }
1512
1513 SetOnlineName(EmptyCString());
1514 return NS_OK;
1515 }
1516
RenameLocal(const nsACString & newName,nsIMsgFolder * parent)1517 NS_IMETHODIMP nsImapMailFolder::RenameLocal(const nsACString& newName,
1518 nsIMsgFolder* parent) {
1519 nsAutoCString leafname(newName);
1520 nsAutoCString parentName;
1521 // newName always in the canonical form "greatparent/parentname/leafname"
1522 int32_t leafpos = leafname.RFindChar('/');
1523 if (leafpos > 0) leafname.Cut(0, leafpos + 1);
1524 m_msgParser = nullptr;
1525 PrepareToRename();
1526 CloseAndBackupFolderDB(leafname);
1527
1528 nsresult rv = NS_OK;
1529 nsCOMPtr<nsIFile> oldPathFile;
1530 rv = GetFilePath(getter_AddRefs(oldPathFile));
1531 if (NS_FAILED(rv)) return rv;
1532
1533 nsCOMPtr<nsIFile> parentPathFile;
1534 rv = parent->GetFilePath(getter_AddRefs(parentPathFile));
1535 NS_ENSURE_SUCCESS(rv, rv);
1536
1537 bool isDirectory = false;
1538 parentPathFile->IsDirectory(&isDirectory);
1539 if (!isDirectory) AddDirectorySeparator(parentPathFile);
1540
1541 nsCOMPtr<nsIFile> dirFile;
1542
1543 int32_t count = mSubFolders.Count();
1544 if (count > 0) rv = CreateDirectoryForFolder(getter_AddRefs(dirFile));
1545
1546 nsCOMPtr<nsIFile> oldSummaryFile;
1547 rv = GetSummaryFileLocation(oldPathFile, getter_AddRefs(oldSummaryFile));
1548 NS_ENSURE_SUCCESS(rv, rv);
1549
1550 nsAutoCString newNameStr;
1551 oldSummaryFile->Remove(false);
1552 if (count > 0) {
1553 newNameStr = leafname;
1554 NS_MsgHashIfNecessary(newNameStr);
1555 newNameStr.AppendLiteral(FOLDER_SUFFIX8);
1556 nsAutoCString leafName;
1557 dirFile->GetNativeLeafName(leafName);
1558 if (!leafName.Equals(newNameStr))
1559 return dirFile->MoveToNative(
1560 nullptr,
1561 newNameStr); // in case of rename operation leaf names will differ
1562
1563 parentPathFile->AppendNative(
1564 newNameStr); // only for move we need to progress further in case the
1565 // parent differs
1566 bool isDirectory = false;
1567 parentPathFile->IsDirectory(&isDirectory);
1568 if (!isDirectory) {
1569 rv = parentPathFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
1570 NS_ENSURE_SUCCESS(rv, rv);
1571 } else {
1572 NS_ERROR("Directory already exists.");
1573 }
1574 rv = RecursiveCopy(dirFile, parentPathFile);
1575 NS_ENSURE_SUCCESS(rv, rv);
1576 dirFile->Remove(true); // moving folders
1577 }
1578 return rv;
1579 }
1580
GetPrettyName(nsAString & prettyName)1581 NS_IMETHODIMP nsImapMailFolder::GetPrettyName(nsAString& prettyName) {
1582 return GetName(prettyName);
1583 }
1584
UpdateSummaryTotals(bool force)1585 NS_IMETHODIMP nsImapMailFolder::UpdateSummaryTotals(bool force) {
1586 // bug 72871 inserted the mIsServer check for IMAP
1587 return mIsServer ? NS_OK : nsMsgDBFolder::UpdateSummaryTotals(force);
1588 }
1589
GetDeletable(bool * deletable)1590 NS_IMETHODIMP nsImapMailFolder::GetDeletable(bool* deletable) {
1591 NS_ENSURE_ARG_POINTER(deletable);
1592
1593 bool isServer;
1594 GetIsServer(&isServer);
1595
1596 *deletable = !(isServer || (mFlags & nsMsgFolderFlags::SpecialUse));
1597 return NS_OK;
1598 }
1599
GetSizeOnDisk(int64_t * size)1600 NS_IMETHODIMP nsImapMailFolder::GetSizeOnDisk(int64_t* size) {
1601 NS_ENSURE_ARG_POINTER(size);
1602
1603 bool isServer = false;
1604 nsresult rv = GetIsServer(&isServer);
1605 // If this is the rootFolder, return 0 as a safe value.
1606 if (NS_FAILED(rv) || isServer) mFolderSize = 0;
1607
1608 *size = mFolderSize;
1609 return NS_OK;
1610 }
1611
1612 NS_IMETHODIMP
GetCanCreateSubfolders(bool * aResult)1613 nsImapMailFolder::GetCanCreateSubfolders(bool* aResult) {
1614 NS_ENSURE_ARG_POINTER(aResult);
1615 *aResult = !(mFlags &
1616 (nsMsgFolderFlags::ImapNoinferiors | nsMsgFolderFlags::Virtual));
1617
1618 bool isServer = false;
1619 GetIsServer(&isServer);
1620 if (!isServer) {
1621 nsCOMPtr<nsIImapIncomingServer> imapServer;
1622 nsresult rv = GetImapIncomingServer(getter_AddRefs(imapServer));
1623 bool dualUseFolders = true;
1624 if (NS_SUCCEEDED(rv) && imapServer)
1625 imapServer->GetDualUseFolders(&dualUseFolders);
1626 if (!dualUseFolders && *aResult)
1627 *aResult = (mFlags & nsMsgFolderFlags::ImapNoselect);
1628 }
1629 return NS_OK;
1630 }
1631
1632 NS_IMETHODIMP
GetCanSubscribe(bool * aResult)1633 nsImapMailFolder::GetCanSubscribe(bool* aResult) {
1634 NS_ENSURE_ARG_POINTER(aResult);
1635 *aResult = false;
1636
1637 bool isImapServer = false;
1638 nsresult rv = GetIsServer(&isImapServer);
1639 if (NS_FAILED(rv)) return rv;
1640 // you can only subscribe to imap servers, not imap folders
1641 *aResult = isImapServer;
1642 return NS_OK;
1643 }
1644
GetServerKey(nsACString & serverKey)1645 nsresult nsImapMailFolder::GetServerKey(nsACString& serverKey) {
1646 // look for matching imap folders, then pop folders
1647 nsCOMPtr<nsIMsgIncomingServer> server;
1648 nsresult rv = GetServer(getter_AddRefs(server));
1649 if (NS_SUCCEEDED(rv)) rv = server->GetKey(serverKey);
1650 return rv;
1651 }
1652
1653 NS_IMETHODIMP
GetImapIncomingServer(nsIImapIncomingServer ** aImapIncomingServer)1654 nsImapMailFolder::GetImapIncomingServer(
1655 nsIImapIncomingServer** aImapIncomingServer) {
1656 NS_ENSURE_ARG(aImapIncomingServer);
1657 nsCOMPtr<nsIMsgIncomingServer> server;
1658 if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server) {
1659 nsCOMPtr<nsIImapIncomingServer> incomingServer = do_QueryInterface(server);
1660 NS_ENSURE_TRUE(incomingServer, NS_ERROR_NO_INTERFACE);
1661 incomingServer.forget(aImapIncomingServer);
1662 return NS_OK;
1663 }
1664 return NS_ERROR_NULL_POINTER;
1665 }
1666
1667 NS_IMETHODIMP
AddMessageDispositionState(nsIMsgDBHdr * aMessage,nsMsgDispositionState aDispositionFlag)1668 nsImapMailFolder::AddMessageDispositionState(
1669 nsIMsgDBHdr* aMessage, nsMsgDispositionState aDispositionFlag) {
1670 nsMsgDBFolder::AddMessageDispositionState(aMessage, aDispositionFlag);
1671
1672 // set the mark message answered flag on the server for this message...
1673 if (aMessage) {
1674 nsMsgKey msgKey;
1675 aMessage->GetMessageKey(&msgKey);
1676
1677 if (aDispositionFlag == nsIMsgFolder::nsMsgDispositionState_Replied)
1678 StoreImapFlags(kImapMsgAnsweredFlag, true, {msgKey}, nullptr);
1679 else if (aDispositionFlag == nsIMsgFolder::nsMsgDispositionState_Forwarded)
1680 StoreImapFlags(kImapMsgForwardedFlag, true, {msgKey}, nullptr);
1681 }
1682 return NS_OK;
1683 }
1684
1685 NS_IMETHODIMP
MarkMessagesRead(const nsTArray<RefPtr<nsIMsgDBHdr>> & messages,bool markRead)1686 nsImapMailFolder::MarkMessagesRead(
1687 const nsTArray<RefPtr<nsIMsgDBHdr>>& messages, bool markRead) {
1688 // tell the folder to do it, which will mark them read in the db.
1689 nsresult rv = nsMsgDBFolder::MarkMessagesRead(messages, markRead);
1690 if (NS_SUCCEEDED(rv)) {
1691 nsAutoCString messageIds;
1692 nsTArray<nsMsgKey> keysToMarkRead;
1693 rv = BuildIdsAndKeyArray(messages, messageIds, keysToMarkRead);
1694 NS_ENSURE_SUCCESS(rv, rv);
1695
1696 StoreImapFlags(kImapMsgSeenFlag, markRead, keysToMarkRead, nullptr);
1697 rv = GetDatabase();
1698 if (NS_SUCCEEDED(rv)) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
1699 }
1700 return rv;
1701 }
1702
1703 NS_IMETHODIMP
SetLabelForMessages(const nsTArray<RefPtr<nsIMsgDBHdr>> & aMessages,nsMsgLabelValue aLabel)1704 nsImapMailFolder::SetLabelForMessages(
1705 const nsTArray<RefPtr<nsIMsgDBHdr>>& aMessages, nsMsgLabelValue aLabel) {
1706 nsresult rv = nsMsgDBFolder::SetLabelForMessages(aMessages, aLabel);
1707 if (NS_SUCCEEDED(rv)) {
1708 nsAutoCString messageIds;
1709 nsTArray<nsMsgKey> keysToLabel;
1710 nsresult rv = BuildIdsAndKeyArray(aMessages, messageIds, keysToLabel);
1711 NS_ENSURE_SUCCESS(rv, rv);
1712 StoreImapFlags((aLabel << 9), true, keysToLabel, nullptr);
1713 rv = GetDatabase();
1714 if (NS_SUCCEEDED(rv)) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
1715 }
1716 return rv;
1717 }
1718
1719 NS_IMETHODIMP
MarkAllMessagesRead(nsIMsgWindow * aMsgWindow)1720 nsImapMailFolder::MarkAllMessagesRead(nsIMsgWindow* aMsgWindow) {
1721 nsresult rv = GetDatabase();
1722 if (NS_SUCCEEDED(rv)) {
1723 nsTArray<nsMsgKey> thoseMarked;
1724 EnableNotifications(allMessageCountNotifications, false);
1725 rv = mDatabase->MarkAllRead(thoseMarked);
1726 EnableNotifications(allMessageCountNotifications, true);
1727 if (NS_SUCCEEDED(rv) && thoseMarked.Length() > 0) {
1728 rv = StoreImapFlags(kImapMsgSeenFlag, true, thoseMarked, nullptr);
1729 mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
1730
1731 // Setup a undo-state
1732 if (aMsgWindow)
1733 rv = AddMarkAllReadUndoAction(aMsgWindow, thoseMarked.Elements(),
1734 thoseMarked.Length());
1735 }
1736 }
1737 return rv;
1738 }
1739
MarkThreadRead(nsIMsgThread * thread)1740 NS_IMETHODIMP nsImapMailFolder::MarkThreadRead(nsIMsgThread* thread) {
1741 nsresult rv = GetDatabase();
1742 if (NS_SUCCEEDED(rv)) {
1743 nsTArray<nsMsgKey> keys;
1744 rv = mDatabase->MarkThreadRead(thread, nullptr, keys);
1745 if (NS_SUCCEEDED(rv) && keys.Length() > 0) {
1746 rv = StoreImapFlags(kImapMsgSeenFlag, true, keys, nullptr);
1747 mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
1748 }
1749 }
1750 return rv;
1751 }
1752
ReadFromFolderCacheElem(nsIMsgFolderCacheElement * element)1753 NS_IMETHODIMP nsImapMailFolder::ReadFromFolderCacheElem(
1754 nsIMsgFolderCacheElement* element) {
1755 nsresult rv = nsMsgDBFolder::ReadFromFolderCacheElem(element);
1756 int32_t hierarchyDelimiter = kOnlineHierarchySeparatorUnknown;
1757 nsCString onlineName;
1758
1759 element->GetInt32Property("boxFlags", &m_boxFlags);
1760 if (NS_SUCCEEDED(
1761 element->GetInt32Property("hierDelim", &hierarchyDelimiter)) &&
1762 hierarchyDelimiter != kOnlineHierarchySeparatorUnknown)
1763 m_hierarchyDelimiter = (char)hierarchyDelimiter;
1764 rv = element->GetStringProperty("onlineName", onlineName);
1765 if (NS_SUCCEEDED(rv) && !onlineName.IsEmpty())
1766 m_onlineFolderName.Assign(onlineName);
1767
1768 m_aclFlags = kAclInvalid; // init to invalid value.
1769 element->GetInt32Property("aclFlags", (int32_t*)&m_aclFlags);
1770 element->GetInt32Property("serverTotal", &m_numServerTotalMessages);
1771 element->GetInt32Property("serverUnseen", &m_numServerUnseenMessages);
1772 element->GetInt32Property("serverRecent", &m_numServerRecentMessages);
1773 element->GetInt32Property("nextUID", &m_nextUID);
1774 int32_t lastSyncTimeInSec;
1775 if (NS_FAILED(element->GetInt32Property("lastSyncTimeInSec",
1776 (int32_t*)&lastSyncTimeInSec)))
1777 lastSyncTimeInSec = 0U;
1778
1779 // make sure that auto-sync state object is created
1780 InitAutoSyncState();
1781 m_autoSyncStateObj->SetLastSyncTimeInSec(lastSyncTimeInSec);
1782
1783 return rv;
1784 }
1785
WriteToFolderCacheElem(nsIMsgFolderCacheElement * element)1786 NS_IMETHODIMP nsImapMailFolder::WriteToFolderCacheElem(
1787 nsIMsgFolderCacheElement* element) {
1788 nsresult rv = nsMsgDBFolder::WriteToFolderCacheElem(element);
1789 element->SetInt32Property("boxFlags", m_boxFlags);
1790 element->SetInt32Property("hierDelim", (int32_t)m_hierarchyDelimiter);
1791 element->SetStringProperty("onlineName", m_onlineFolderName);
1792 element->SetInt32Property("aclFlags", (int32_t)m_aclFlags);
1793 element->SetInt32Property("serverTotal", m_numServerTotalMessages);
1794 element->SetInt32Property("serverUnseen", m_numServerUnseenMessages);
1795 element->SetInt32Property("serverRecent", m_numServerRecentMessages);
1796 if (m_nextUID != (int32_t)nsMsgKey_None)
1797 element->SetInt32Property("nextUID", m_nextUID);
1798
1799 // store folder's last sync time
1800 if (m_autoSyncStateObj) {
1801 PRTime lastSyncTime;
1802 m_autoSyncStateObj->GetLastSyncTime(&lastSyncTime);
1803 // store in sec
1804 element->SetInt32Property("lastSyncTimeInSec",
1805 (int32_t)(lastSyncTime / PR_USEC_PER_SEC));
1806 }
1807
1808 return rv;
1809 }
1810
1811 NS_IMETHODIMP
MarkMessagesFlagged(const nsTArray<RefPtr<nsIMsgDBHdr>> & messages,bool markFlagged)1812 nsImapMailFolder::MarkMessagesFlagged(
1813 const nsTArray<RefPtr<nsIMsgDBHdr>>& messages, bool markFlagged) {
1814 nsresult rv;
1815 // tell the folder to do it, which will mark them read in the db.
1816 rv = nsMsgDBFolder::MarkMessagesFlagged(messages, markFlagged);
1817 if (NS_SUCCEEDED(rv)) {
1818 nsAutoCString messageIds;
1819 nsTArray<nsMsgKey> keysToMarkFlagged;
1820 rv = BuildIdsAndKeyArray(messages, messageIds, keysToMarkFlagged);
1821 if (NS_FAILED(rv)) return rv;
1822 rv = StoreImapFlags(kImapMsgFlaggedFlag, markFlagged, keysToMarkFlagged,
1823 nullptr);
1824 NS_ENSURE_SUCCESS(rv, rv);
1825 rv = GetDatabase();
1826 NS_ENSURE_SUCCESS(rv, rv);
1827 mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
1828 }
1829 return rv;
1830 }
1831
SetOnlineName(const nsACString & aOnlineFolderName)1832 NS_IMETHODIMP nsImapMailFolder::SetOnlineName(
1833 const nsACString& aOnlineFolderName) {
1834 nsresult rv;
1835 nsCOMPtr<nsIMsgDatabase> db;
1836 nsCOMPtr<nsIDBFolderInfo> folderInfo;
1837 rv = GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(db));
1838 // do this after GetDBFolderInfoAndDB, because it crunches m_onlineFolderName
1839 // (not sure why)
1840 m_onlineFolderName = aOnlineFolderName;
1841 if (NS_SUCCEEDED(rv) && folderInfo) {
1842 nsAutoString onlineName;
1843 CopyUTF8toUTF16(aOnlineFolderName, onlineName);
1844 rv = folderInfo->SetProperty("onlineName", onlineName);
1845 rv = folderInfo->SetMailboxName(onlineName);
1846 // so, when are we going to commit this? Definitely not every time!
1847 // We could check if the online name has changed.
1848 db->Commit(nsMsgDBCommitType::kLargeCommit);
1849 }
1850 folderInfo = nullptr;
1851 return rv;
1852 }
1853
GetOnlineName(nsACString & aOnlineFolderName)1854 NS_IMETHODIMP nsImapMailFolder::GetOnlineName(nsACString& aOnlineFolderName) {
1855 ReadDBFolderInfo(false); // update cache first.
1856 aOnlineFolderName = m_onlineFolderName;
1857 return NS_OK;
1858 }
1859
1860 NS_IMETHODIMP
GetDBFolderInfoAndDB(nsIDBFolderInfo ** folderInfo,nsIMsgDatabase ** db)1861 nsImapMailFolder::GetDBFolderInfoAndDB(nsIDBFolderInfo** folderInfo,
1862 nsIMsgDatabase** db) {
1863 NS_ENSURE_ARG_POINTER(folderInfo);
1864 NS_ENSURE_ARG_POINTER(db);
1865
1866 nsresult rv = GetDatabase();
1867 if (NS_FAILED(rv)) return rv;
1868
1869 NS_ADDREF(*db = mDatabase);
1870
1871 rv = (*db)->GetDBFolderInfo(folderInfo);
1872 if (NS_FAILED(rv))
1873 return rv; // GetDBFolderInfo can't return NS_OK if !folderInfo
1874
1875 nsCString onlineName;
1876 rv = (*folderInfo)->GetCharProperty("onlineName", onlineName);
1877 if (NS_FAILED(rv)) return rv;
1878
1879 if (!onlineName.IsEmpty())
1880 m_onlineFolderName.Assign(onlineName);
1881 else {
1882 nsAutoString autoOnlineName;
1883 (*folderInfo)->GetMailboxName(autoOnlineName);
1884 if (autoOnlineName.IsEmpty()) {
1885 nsCString uri;
1886 rv = GetURI(uri);
1887 NS_ENSURE_SUCCESS(rv, rv);
1888
1889 nsCString hostname;
1890 rv = GetHostname(hostname);
1891 NS_ENSURE_SUCCESS(rv, rv);
1892
1893 nsCString onlineCName;
1894 rv = nsImapURI2FullName(kImapRootURI, hostname.get(), uri.get(),
1895 getter_Copies(onlineCName));
1896 // Note: check for unknown separator '^' only became needed
1897 // with UTF8=ACCEPT modification and haven't found why. Online name
1898 // contained the '^' delimiter and gmail said "NO" when folder under
1899 // [Gmail] is created and selected.
1900 if ((m_hierarchyDelimiter != '/') &&
1901 (m_hierarchyDelimiter != kOnlineHierarchySeparatorUnknown))
1902 onlineCName.ReplaceChar('/', m_hierarchyDelimiter);
1903 // XXX: What if online name contains slashes? Breaks?
1904 m_onlineFolderName.Assign(onlineCName);
1905 CopyUTF8toUTF16(onlineCName, autoOnlineName);
1906 }
1907 (*folderInfo)->SetProperty("onlineName", autoOnlineName);
1908 }
1909 return rv;
1910 }
1911
1912 /* static */
BuildIdsAndKeyArray(const nsTArray<RefPtr<nsIMsgDBHdr>> & messages,nsCString & msgIds,nsTArray<nsMsgKey> & keyArray)1913 nsresult nsImapMailFolder::BuildIdsAndKeyArray(
1914 const nsTArray<RefPtr<nsIMsgDBHdr>>& messages, nsCString& msgIds,
1915 nsTArray<nsMsgKey>& keyArray) {
1916 keyArray.Clear();
1917 keyArray.SetCapacity(messages.Length());
1918 // build up message keys.
1919 for (auto msgDBHdr : messages) {
1920 nsMsgKey key;
1921 nsresult rv = msgDBHdr->GetMessageKey(&key);
1922 if (NS_SUCCEEDED(rv)) keyArray.AppendElement(key);
1923 }
1924 return AllocateUidStringFromKeys(keyArray, msgIds);
1925 }
1926
1927 /* static */
AllocateUidStringFromKeys(const nsTArray<nsMsgKey> & keys,nsCString & msgIds)1928 nsresult nsImapMailFolder::AllocateUidStringFromKeys(
1929 const nsTArray<nsMsgKey>& keys, nsCString& msgIds) {
1930 if (keys.IsEmpty()) return NS_ERROR_INVALID_ARG;
1931 nsresult rv = NS_OK;
1932 uint32_t startSequence;
1933 startSequence = keys[0];
1934 uint32_t curSequenceEnd = startSequence;
1935 uint32_t total = keys.Length();
1936 // sort keys and then generate ranges instead of singletons!
1937 nsTArray<nsMsgKey> sorted(keys.Clone());
1938 sorted.Sort();
1939 for (uint32_t keyIndex = 0; keyIndex < total; keyIndex++) {
1940 uint32_t curKey = sorted[keyIndex];
1941 uint32_t nextKey =
1942 (keyIndex + 1 < total) ? sorted[keyIndex + 1] : 0xFFFFFFFF;
1943 bool lastKey = (nextKey == 0xFFFFFFFF);
1944
1945 if (lastKey) curSequenceEnd = curKey;
1946 if (nextKey == (uint32_t)curSequenceEnd + 1 && !lastKey) {
1947 curSequenceEnd = nextKey;
1948 continue;
1949 }
1950 if (curSequenceEnd > startSequence) {
1951 AppendUid(msgIds, startSequence);
1952 msgIds += ':';
1953 AppendUid(msgIds, curSequenceEnd);
1954 if (!lastKey) msgIds += ',';
1955 startSequence = nextKey;
1956 curSequenceEnd = startSequence;
1957 } else {
1958 startSequence = nextKey;
1959 curSequenceEnd = startSequence;
1960 AppendUid(msgIds, sorted[keyIndex]);
1961 if (!lastKey) msgIds += ',';
1962 }
1963 }
1964 return rv;
1965 }
1966
MarkMessagesImapDeleted(nsTArray<nsMsgKey> * keyArray,bool deleted,nsIMsgDatabase * db)1967 nsresult nsImapMailFolder::MarkMessagesImapDeleted(nsTArray<nsMsgKey>* keyArray,
1968 bool deleted,
1969 nsIMsgDatabase* db) {
1970 for (uint32_t kindex = 0; kindex < keyArray->Length(); kindex++) {
1971 nsMsgKey key = keyArray->ElementAt(kindex);
1972 db->MarkImapDeleted(key, deleted, nullptr);
1973 }
1974 return NS_OK;
1975 }
1976
DeleteMessages(nsTArray<RefPtr<nsIMsgDBHdr>> const & msgHeaders,nsIMsgWindow * msgWindow,bool deleteStorage,bool isMove,nsIMsgCopyServiceListener * listener,bool allowUndo)1977 NS_IMETHODIMP nsImapMailFolder::DeleteMessages(
1978 nsTArray<RefPtr<nsIMsgDBHdr>> const& msgHeaders, nsIMsgWindow* msgWindow,
1979 bool deleteStorage, bool isMove, nsIMsgCopyServiceListener* listener,
1980 bool allowUndo) {
1981 // *** jt - assuming delete is move to the trash folder for now
1982 nsAutoCString uri;
1983 bool deleteImmediatelyNoTrash = false;
1984 nsAutoCString messageIds;
1985 nsTArray<nsMsgKey> srcKeyArray;
1986 bool deleteMsgs = true; // used for toggling delete status - default is true
1987 nsMsgImapDeleteModel deleteModel = nsMsgImapDeleteModels::MoveToTrash;
1988 imapMessageFlagsType messageFlags = kImapMsgDeletedFlag;
1989
1990 nsCOMPtr<nsIImapIncomingServer> imapServer;
1991 nsresult rv = GetFlag(nsMsgFolderFlags::Trash, &deleteImmediatelyNoTrash);
1992 rv = GetImapIncomingServer(getter_AddRefs(imapServer));
1993
1994 if (NS_SUCCEEDED(rv) && imapServer) {
1995 imapServer->GetDeleteModel(&deleteModel);
1996 if (deleteModel != nsMsgImapDeleteModels::MoveToTrash || deleteStorage)
1997 deleteImmediatelyNoTrash = true;
1998 // if we're deleting a message, we should pseudo-interrupt the msg
1999 // load of the current message.
2000 bool interrupted = false;
2001 imapServer->PseudoInterruptMsgLoad(this, msgWindow, &interrupted);
2002 }
2003
2004 rv = BuildIdsAndKeyArray(msgHeaders, messageIds, srcKeyArray);
2005 if (NS_FAILED(rv)) return rv;
2006
2007 nsCOMPtr<nsIMsgFolder> rootFolder;
2008 nsCOMPtr<nsIMsgFolder> trashFolder;
2009
2010 if (!deleteImmediatelyNoTrash) {
2011 rv = GetRootFolder(getter_AddRefs(rootFolder));
2012 if (NS_SUCCEEDED(rv) && rootFolder) {
2013 rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Trash,
2014 getter_AddRefs(trashFolder));
2015 NS_ASSERTION(trashFolder, "couldn't find trash");
2016 // if we can't find the trash, we'll just have to do an imap delete and
2017 // pretend this is the trash
2018 if (!trashFolder) deleteImmediatelyNoTrash = true;
2019 }
2020 }
2021
2022 if ((NS_SUCCEEDED(rv) && deleteImmediatelyNoTrash) ||
2023 deleteModel == nsMsgImapDeleteModels::IMAPDelete) {
2024 if (allowUndo) {
2025 // need to take care of these two delete models
2026 RefPtr<nsImapMoveCopyMsgTxn> undoMsgTxn = new nsImapMoveCopyMsgTxn;
2027 if (!undoMsgTxn ||
2028 NS_FAILED(undoMsgTxn->Init(this, &srcKeyArray, messageIds.get(),
2029 nullptr, true, isMove)))
2030 return NS_ERROR_OUT_OF_MEMORY;
2031
2032 undoMsgTxn->SetTransactionType(nsIMessenger::eDeleteMsg);
2033 // we're adding this undo action before the delete is successful. This is
2034 // evil, but 4.5 did it as well.
2035 nsCOMPtr<nsITransactionManager> txnMgr;
2036 if (msgWindow) msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
2037 if (txnMgr) txnMgr->DoTransaction(undoMsgTxn);
2038 }
2039
2040 if (deleteModel == nsMsgImapDeleteModels::IMAPDelete && !deleteStorage) {
2041 deleteMsgs = false;
2042 for (nsIMsgDBHdr* msgHdr : msgHeaders) {
2043 if (!msgHdr) {
2044 continue;
2045 }
2046 uint32_t flags;
2047 msgHdr->GetFlags(&flags);
2048 if (!(flags & nsMsgMessageFlags::IMAPDeleted)) {
2049 deleteMsgs = true;
2050 break;
2051 }
2052 }
2053 }
2054 // if copy service listener is also a url listener, pass that
2055 // url listener into StoreImapFlags.
2056 nsCOMPtr<nsIUrlListener> urlListener = do_QueryInterface(listener);
2057 if (deleteMsgs) messageFlags |= kImapMsgSeenFlag;
2058 rv = StoreImapFlags(messageFlags, deleteMsgs, srcKeyArray, urlListener);
2059
2060 if (NS_SUCCEEDED(rv)) {
2061 if (mDatabase) {
2062 nsCOMPtr<nsIMsgDatabase> database(mDatabase);
2063 if (deleteModel == nsMsgImapDeleteModels::IMAPDelete)
2064 MarkMessagesImapDeleted(&srcKeyArray, deleteMsgs, database);
2065 else {
2066 EnableNotifications(allMessageCountNotifications,
2067 false); //"remove it immediately" model
2068 // Notify if this is an actual delete.
2069 if (!isMove) {
2070 nsCOMPtr<nsIMsgFolderNotificationService> notifier(
2071 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
2072 if (notifier) notifier->NotifyMsgsDeleted(msgHeaders);
2073 }
2074 DeleteStoreMessages(msgHeaders);
2075 database->DeleteMessages(srcKeyArray, nullptr);
2076 EnableNotifications(allMessageCountNotifications, true);
2077 }
2078 if (listener) {
2079 listener->OnStartCopy();
2080 listener->OnStopCopy(NS_OK);
2081 }
2082 NotifyFolderEvent(kDeleteOrMoveMsgCompleted);
2083 }
2084 }
2085 return rv;
2086 }
2087
2088 // have to move the messages to the trash
2089 if (trashFolder) {
2090 nsCOMPtr<nsIMsgFolder> srcFolder;
2091 nsCOMPtr<nsISupports> srcSupport;
2092
2093 rv = QueryInterface(NS_GET_IID(nsIMsgFolder), getter_AddRefs(srcFolder));
2094 nsCOMPtr<nsIMsgCopyService> copyService =
2095 do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
2096 NS_ENSURE_SUCCESS(rv, rv);
2097 rv = copyService->CopyMessages(srcFolder, msgHeaders, trashFolder, true,
2098 listener, msgWindow, allowUndo);
2099 }
2100
2101 return rv;
2102 }
2103
2104 // check if folder is the trash, or a descendent of the trash
2105 // so we can tell if the folders we're deleting from it should
2106 // be *really* deleted.
TrashOrDescendentOfTrash(nsIMsgFolder * folder)2107 bool nsImapMailFolder::TrashOrDescendentOfTrash(nsIMsgFolder* folder) {
2108 NS_ENSURE_TRUE(folder, false);
2109 nsCOMPtr<nsIMsgFolder> parent;
2110 nsCOMPtr<nsIMsgFolder> curFolder = folder;
2111 nsresult rv;
2112 uint32_t flags = 0;
2113 do {
2114 rv = curFolder->GetFlags(&flags);
2115 if (NS_FAILED(rv)) return false;
2116 if (flags & nsMsgFolderFlags::Trash) return true;
2117 curFolder->GetParent(getter_AddRefs(parent));
2118 if (!parent) return false;
2119 curFolder = parent;
2120 } while (NS_SUCCEEDED(rv) && curFolder);
2121 return false;
2122 }
2123 NS_IMETHODIMP
DeleteSelf(nsIMsgWindow * msgWindow)2124 nsImapMailFolder::DeleteSelf(nsIMsgWindow* msgWindow) {
2125 nsCOMPtr<nsIMsgFolder> trashFolder;
2126 nsresult rv;
2127 uint32_t folderFlags;
2128
2129 // No IMAP shenanigans required for virtual folders.
2130 GetFlags(&folderFlags);
2131 if (folderFlags & nsMsgFolderFlags::Virtual) {
2132 return nsMsgDBFolder::DeleteSelf(nullptr);
2133 }
2134
2135 // "this" is the folder we're deleting from
2136 bool deleteNoTrash = TrashOrDescendentOfTrash(this) || !DeleteIsMoveToTrash();
2137 bool confirmDeletion = true;
2138
2139 nsCOMPtr<nsIImapService> imapService =
2140 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
2141 NS_ENSURE_SUCCESS(rv, rv);
2142 if (!deleteNoTrash) {
2143 rv = GetTrashFolder(getter_AddRefs(trashFolder));
2144 // If we can't find the trash folder and we are supposed to move it to the
2145 // trash return failure.
2146 if (NS_FAILED(rv) || !trashFolder) return NS_ERROR_FAILURE;
2147 bool canHaveSubFoldersOfTrash = true;
2148 trashFolder->GetCanCreateSubfolders(&canHaveSubFoldersOfTrash);
2149 if (canHaveSubFoldersOfTrash) // UW server doesn't set NOINFERIORS - check
2150 // dual use pref
2151 {
2152 nsCOMPtr<nsIImapIncomingServer> imapServer;
2153 rv = GetImapIncomingServer(getter_AddRefs(imapServer));
2154 NS_ENSURE_SUCCESS(rv, rv);
2155 bool serverSupportsDualUseFolders;
2156 imapServer->GetDualUseFolders(&serverSupportsDualUseFolders);
2157 if (!serverSupportsDualUseFolders) canHaveSubFoldersOfTrash = false;
2158 }
2159 if (!canHaveSubFoldersOfTrash) deleteNoTrash = true;
2160 nsCOMPtr<nsIPrefBranch> prefBranch(
2161 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
2162 NS_ENSURE_SUCCESS(rv, rv);
2163 prefBranch->GetBoolPref("mailnews.confirm.moveFoldersToTrash",
2164 &confirmDeletion);
2165 }
2166
2167 // If we are deleting folder immediately, ask user for confirmation.
2168 bool confirmed = false;
2169 if (confirmDeletion || deleteNoTrash) {
2170 nsCOMPtr<nsIStringBundle> bundle;
2171 rv = IMAPGetStringBundle(getter_AddRefs(bundle));
2172 NS_ENSURE_SUCCESS(rv, rv);
2173
2174 nsAutoString folderName;
2175 rv = GetName(folderName);
2176 NS_ENSURE_SUCCESS(rv, rv);
2177 AutoTArray<nsString, 1> formatStrings = {folderName};
2178
2179 nsAutoString deleteFolderDialogTitle;
2180 rv = bundle->GetStringFromName("imapDeleteFolderDialogTitle",
2181 deleteFolderDialogTitle);
2182 NS_ENSURE_SUCCESS(rv, rv);
2183
2184 nsAutoString deleteFolderButtonLabel;
2185 rv = bundle->GetStringFromName("imapDeleteFolderButtonLabel",
2186 deleteFolderButtonLabel);
2187 NS_ENSURE_SUCCESS(rv, rv);
2188
2189 nsAutoString confirmationStr;
2190 rv = bundle->FormatStringFromName(
2191 (deleteNoTrash) ? "imapDeleteNoTrash" : "imapMoveFolderToTrash",
2192 formatStrings, confirmationStr);
2193 NS_ENSURE_SUCCESS(rv, rv);
2194 if (!msgWindow) return NS_ERROR_NULL_POINTER;
2195 nsCOMPtr<nsIDocShell> docShell;
2196 msgWindow->GetRootDocShell(getter_AddRefs(docShell));
2197 nsCOMPtr<nsIPrompt> dialog;
2198 if (docShell) dialog = do_GetInterface(docShell);
2199 if (dialog) {
2200 int32_t buttonPressed = 0;
2201 // Default the dialog to "cancel".
2202 const uint32_t buttonFlags =
2203 (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_0) +
2204 (nsIPrompt::BUTTON_TITLE_CANCEL * nsIPrompt::BUTTON_POS_1);
2205
2206 bool dummyValue = false;
2207 rv = dialog->ConfirmEx(deleteFolderDialogTitle.get(),
2208 confirmationStr.get(), buttonFlags,
2209 deleteFolderButtonLabel.get(), nullptr, nullptr,
2210 nullptr, &dummyValue, &buttonPressed);
2211 NS_ENSURE_SUCCESS(rv, rv);
2212 confirmed = !buttonPressed; // "ok" is in position 0
2213 }
2214 } else {
2215 confirmed = true;
2216 }
2217
2218 if (confirmed) {
2219 if (deleteNoTrash) {
2220 rv = imapService->DeleteFolder(this, this, msgWindow, nullptr);
2221 nsMsgDBFolder::DeleteSelf(msgWindow);
2222 } else {
2223 bool match = false;
2224 rv = MatchOrChangeFilterDestination(nullptr, false, &match);
2225 if (match) {
2226 bool confirm = false;
2227 ConfirmFolderDeletionForFilter(msgWindow, &confirm);
2228 if (!confirm) return NS_OK;
2229 }
2230 rv = imapService->MoveFolder(this, trashFolder, this, msgWindow, nullptr);
2231 }
2232 }
2233 return rv;
2234 }
2235
2236 // FIXME: helper function to know whether we should check all IMAP folders
2237 // for new mail; this is necessary because of a legacy hidden preference
2238 // mail.check_all_imap_folders_for_new (now replaced by per-server preference
2239 // mail.server.%serverkey%.check_all_folders_for_new), still present in some
2240 // profiles.
2241 /*static*/
ShouldCheckAllFolders(nsIImapIncomingServer * imapServer)2242 bool nsImapMailFolder::ShouldCheckAllFolders(
2243 nsIImapIncomingServer* imapServer) {
2244 // Check legacy global preference to see if we should check all folders for
2245 // new messages, or just the inbox and marked ones.
2246 bool checkAllFolders = false;
2247 nsresult rv;
2248 nsCOMPtr<nsIPrefBranch> prefBranch =
2249 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
2250 NS_ENSURE_SUCCESS(rv, false);
2251 // This pref might not exist, which is OK.
2252 (void)prefBranch->GetBoolPref("mail.check_all_imap_folders_for_new",
2253 &checkAllFolders);
2254
2255 if (checkAllFolders) return true;
2256
2257 // If the legacy preference doesn't exist or has its default value (False),
2258 // the true preference is read.
2259 imapServer->GetCheckAllFoldersForNew(&checkAllFolders);
2260 return checkAllFolders;
2261 }
2262
2263 // Called by Biff, or when user presses GetMsg button.
GetNewMessages(nsIMsgWindow * aWindow,nsIUrlListener * aListener)2264 NS_IMETHODIMP nsImapMailFolder::GetNewMessages(nsIMsgWindow* aWindow,
2265 nsIUrlListener* aListener) {
2266 nsCOMPtr<nsIMsgFolder> rootFolder;
2267 nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
2268 if (NS_SUCCEEDED(rv) && rootFolder) {
2269 nsCOMPtr<nsIImapIncomingServer> imapServer;
2270 rv = GetImapIncomingServer(getter_AddRefs(imapServer));
2271 NS_ENSURE_SUCCESS(rv, rv);
2272 bool performingBiff = false;
2273 nsCOMPtr<nsIMsgIncomingServer> incomingServer =
2274 do_QueryInterface(imapServer, &rv);
2275 NS_ENSURE_SUCCESS(rv, rv);
2276 incomingServer->GetPerformingBiff(&performingBiff);
2277 m_urlListener = aListener;
2278
2279 // See if we should check all folders for new messages, or just the inbox
2280 // and marked ones
2281 bool checkAllFolders = ShouldCheckAllFolders(imapServer);
2282
2283 // Get new messages for inbox
2284 nsCOMPtr<nsIMsgFolder> inbox;
2285 rv = rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox,
2286 getter_AddRefs(inbox));
2287 if (inbox) {
2288 nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(inbox, &rv);
2289 NS_ENSURE_SUCCESS(rv, rv);
2290 imapFolder->SetPerformingBiff(performingBiff);
2291 inbox->SetGettingNewMessages(true);
2292 rv = inbox->UpdateFolder(aWindow);
2293 }
2294 // Get new messages for other folders if marked, or all of them if the pref
2295 // is set
2296 rv = imapServer->GetNewMessagesForNonInboxFolders(
2297 rootFolder, aWindow, checkAllFolders, performingBiff);
2298 }
2299 return rv;
2300 }
2301
Shutdown(bool shutdownChildren)2302 NS_IMETHODIMP nsImapMailFolder::Shutdown(bool shutdownChildren) {
2303 m_filterList = nullptr;
2304 m_initialized = false;
2305 // mPath is used to decide if folder pathname needs to be reconstructed in
2306 // GetPath().
2307 mPath = nullptr;
2308 m_moveCoalescer = nullptr;
2309 m_msgParser = nullptr;
2310 if (m_playbackTimer) {
2311 m_playbackTimer->Cancel();
2312 m_playbackTimer = nullptr;
2313 }
2314 m_pendingOfflineMoves.Clear();
2315 return nsMsgDBFolder::Shutdown(shutdownChildren);
2316 }
2317
GetBodysToDownload(nsTArray<nsMsgKey> * keysOfMessagesToDownload)2318 nsresult nsImapMailFolder::GetBodysToDownload(
2319 nsTArray<nsMsgKey>* keysOfMessagesToDownload) {
2320 NS_ENSURE_ARG(keysOfMessagesToDownload);
2321 NS_ENSURE_TRUE(mDatabase, NS_ERROR_FAILURE);
2322
2323 nsCOMPtr<nsIMsgEnumerator> enumerator;
2324 nsresult rv = mDatabase->EnumerateMessages(getter_AddRefs(enumerator));
2325 if (NS_SUCCEEDED(rv) && enumerator) {
2326 bool hasMore;
2327 while (NS_SUCCEEDED(rv = enumerator->HasMoreElements(&hasMore)) &&
2328 hasMore) {
2329 nsCOMPtr<nsIMsgDBHdr> header;
2330 rv = enumerator->GetNext(getter_AddRefs(header));
2331 NS_ENSURE_SUCCESS(rv, rv);
2332 bool shouldStoreMsgOffline = false;
2333 nsMsgKey msgKey;
2334 header->GetMessageKey(&msgKey);
2335 // MsgFitsDownloadCriteria ignores nsMsgFolderFlags::Offline, which we
2336 // want
2337 if (m_downloadingFolderForOfflineUse)
2338 MsgFitsDownloadCriteria(msgKey, &shouldStoreMsgOffline);
2339 else
2340 ShouldStoreMsgOffline(msgKey, &shouldStoreMsgOffline);
2341 if (shouldStoreMsgOffline)
2342 keysOfMessagesToDownload->AppendElement(msgKey);
2343 }
2344 }
2345 return rv;
2346 }
2347
OnNewIdleMessages()2348 NS_IMETHODIMP nsImapMailFolder::OnNewIdleMessages() {
2349 nsresult rv;
2350 nsCOMPtr<nsIImapIncomingServer> imapServer;
2351 rv = GetImapIncomingServer(getter_AddRefs(imapServer));
2352 NS_ENSURE_SUCCESS(rv, rv);
2353
2354 bool checkAllFolders = ShouldCheckAllFolders(imapServer);
2355
2356 // only trigger biff if we're checking all new folders for new messages, or
2357 // this particular folder, but excluding trash,junk, sent, and no select
2358 // folders, by default.
2359 if ((checkAllFolders &&
2360 !(mFlags &
2361 (nsMsgFolderFlags::Trash | nsMsgFolderFlags::Junk |
2362 nsMsgFolderFlags::SentMail | nsMsgFolderFlags::ImapNoselect))) ||
2363 (mFlags & (nsMsgFolderFlags::CheckNew | nsMsgFolderFlags::Inbox)))
2364 SetPerformingBiff(true);
2365 return UpdateFolder(nullptr);
2366 }
2367
UpdateImapMailboxInfo(nsIImapProtocol * aProtocol,nsIMailboxSpec * aSpec)2368 NS_IMETHODIMP nsImapMailFolder::UpdateImapMailboxInfo(
2369 nsIImapProtocol* aProtocol, nsIMailboxSpec* aSpec) {
2370 nsresult rv;
2371 ChangeNumPendingTotalMessages(-mNumPendingTotalMessages);
2372 ChangeNumPendingUnread(-mNumPendingUnreadMessages);
2373 m_numServerRecentMessages = 0; // clear this since we selected the folder.
2374 m_numServerUnseenMessages = 0; // clear this since we selected the folder.
2375
2376 if (!mDatabase) GetDatabase();
2377
2378 bool folderSelected;
2379 rv = aSpec->GetFolderSelected(&folderSelected);
2380 NS_ENSURE_SUCCESS(rv, rv);
2381 nsTArray<nsMsgKey> existingKeys;
2382 nsTArray<nsMsgKey> keysToDelete;
2383 uint32_t numNewUnread;
2384 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
2385 int32_t imapUIDValidity = 0;
2386 if (mDatabase) {
2387 rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
2388 if (NS_SUCCEEDED(rv) && dbFolderInfo) {
2389 dbFolderInfo->GetImapUidValidity(&imapUIDValidity);
2390 uint64_t mailboxHighestModSeq;
2391 aSpec->GetHighestModSeq(&mailboxHighestModSeq);
2392 MOZ_LOG(IMAP_CS, mozilla::LogLevel::Debug,
2393 ("UpdateImapMailboxInfo(): Store highest MODSEQ=%" PRIu64
2394 " for folder=%s",
2395 mailboxHighestModSeq, m_onlineFolderName.get()));
2396 char intStrBuf[40];
2397 PR_snprintf(intStrBuf, sizeof(intStrBuf), "%llu", mailboxHighestModSeq);
2398 dbFolderInfo->SetCharProperty(kModSeqPropertyName,
2399 nsDependentCString(intStrBuf));
2400 }
2401 nsTArray<nsMsgKey> keys;
2402 rv = mDatabase->ListAllKeys(keys);
2403 NS_ENSURE_SUCCESS(rv, rv);
2404 existingKeys.AppendElements(keys);
2405 mDatabase->ListAllOfflineDeletes(&existingKeys);
2406 }
2407 int32_t folderValidity;
2408 aSpec->GetFolder_UIDVALIDITY(&folderValidity);
2409 nsCOMPtr<nsIImapFlagAndUidState> flagState;
2410 aSpec->GetFlagState(getter_AddRefs(flagState));
2411
2412 // remember what the supported user flags are.
2413 uint32_t supportedUserFlags;
2414 aSpec->GetSupportedUserFlags(&supportedUserFlags);
2415 SetSupportedUserFlags(supportedUserFlags);
2416
2417 m_uidValidity = folderValidity;
2418
2419 if (imapUIDValidity != folderValidity) {
2420 NS_ASSERTION(imapUIDValidity == kUidUnknown,
2421 "uid validity seems to have changed, blowing away db");
2422 nsCOMPtr<nsIFile> pathFile;
2423 rv = GetFilePath(getter_AddRefs(pathFile));
2424 if (NS_FAILED(rv)) return rv;
2425
2426 nsCOMPtr<nsIMsgDBService> msgDBService =
2427 do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
2428 NS_ENSURE_SUCCESS(rv, rv);
2429
2430 nsCOMPtr<nsIDBFolderInfo> transferInfo;
2431 if (dbFolderInfo)
2432 dbFolderInfo->GetTransferInfo(getter_AddRefs(transferInfo));
2433
2434 // A backup message database might have been created earlier, for example
2435 // if the user requested a reindex. We'll use the earlier one if we can,
2436 // otherwise we'll try to backup at this point.
2437 nsresult rvbackup = OpenBackupMsgDatabase();
2438 if (mDatabase) {
2439 dbFolderInfo = nullptr;
2440 if (NS_FAILED(rvbackup)) {
2441 CloseAndBackupFolderDB(EmptyCString());
2442 if (NS_FAILED(OpenBackupMsgDatabase()) && mBackupDatabase) {
2443 mBackupDatabase->RemoveListener(this);
2444 mBackupDatabase = nullptr;
2445 }
2446 } else
2447 mDatabase->ForceClosed();
2448 }
2449 mDatabase = nullptr;
2450
2451 nsCOMPtr<nsIFile> summaryFile;
2452 rv = GetSummaryFileLocation(pathFile, getter_AddRefs(summaryFile));
2453 // Remove summary file.
2454 if (NS_SUCCEEDED(rv) && summaryFile) summaryFile->Remove(false);
2455
2456 // Create a new summary file, update the folder message counts, and
2457 // Close the summary file db.
2458 rv = msgDBService->CreateNewDB(this, getter_AddRefs(mDatabase));
2459
2460 if (NS_FAILED(rv) && mDatabase) {
2461 mDatabase->ForceClosed();
2462 mDatabase = nullptr;
2463 } else if (NS_SUCCEEDED(rv) && mDatabase) {
2464 if (transferInfo) SetDBTransferInfo(transferInfo);
2465
2466 SummaryChanged();
2467 if (mDatabase) {
2468 if (mAddListener) mDatabase->AddListener(this);
2469 rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
2470 }
2471 }
2472 // store the new UIDVALIDITY value
2473
2474 if (NS_SUCCEEDED(rv) && dbFolderInfo) {
2475 dbFolderInfo->SetImapUidValidity(folderValidity);
2476 // need to forget highest mod seq when uid validity rolls.
2477 MOZ_LOG(IMAP_CS, mozilla::LogLevel::Debug,
2478 ("UpdateImapMailboxInfo(): UIDVALIDITY changed, reset highest "
2479 "MODSEQ and UID for folder=%s",
2480 m_onlineFolderName.get()));
2481 dbFolderInfo->SetCharProperty(kModSeqPropertyName, EmptyCString());
2482 dbFolderInfo->SetUint32Property(kHighestRecordedUIDPropertyName, 0);
2483 }
2484 // delete all my msgs, the keys are bogus now
2485 // add every message in this folder
2486 existingKeys.Clear();
2487 // keysToDelete.CopyArray(&existingKeys);
2488
2489 if (flagState) {
2490 nsTArray<nsMsgKey> no_existingKeys;
2491 FindKeysToAdd(no_existingKeys, m_keysToFetch, numNewUnread, flagState);
2492 }
2493 if (NS_FAILED(rv)) pathFile->Remove(false);
2494
2495 } else if (!flagState /*&& !NET_IsOffline() */) // if there are no messages
2496 // on the server
2497 keysToDelete = existingKeys.Clone();
2498 else /* if ( !NET_IsOffline()) */
2499 {
2500 uint32_t boxFlags;
2501 aSpec->GetBox_flags(&boxFlags);
2502 // FindKeysToDelete and FindKeysToAdd require sorted lists
2503 existingKeys.Sort();
2504 FindKeysToDelete(existingKeys, keysToDelete, flagState, boxFlags);
2505 // if this is the result of an expunge then don't grab headers
2506 if (!(boxFlags & kJustExpunged))
2507 FindKeysToAdd(existingKeys, m_keysToFetch, numNewUnread, flagState);
2508 }
2509 m_totalKeysToFetch = m_keysToFetch.Length();
2510 if (!keysToDelete.IsEmpty() && mDatabase) {
2511 nsTArray<RefPtr<nsIMsgDBHdr>> hdrsToDelete;
2512 MsgGetHeadersFromKeys(mDatabase, keysToDelete, hdrsToDelete);
2513 // Notify nsIMsgFolderListeners of a mass delete, but only if we actually
2514 // have headers
2515 if (!hdrsToDelete.IsEmpty()) {
2516 nsCOMPtr<nsIMsgFolderNotificationService> notifier(
2517 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
2518 if (notifier) notifier->NotifyMsgsDeleted(hdrsToDelete);
2519 }
2520 DeleteStoreMessages(hdrsToDelete);
2521 EnableNotifications(nsIMsgFolder::allMessageCountNotifications, false);
2522 mDatabase->DeleteMessages(keysToDelete, nullptr);
2523 EnableNotifications(nsIMsgFolder::allMessageCountNotifications, true);
2524 }
2525 int32_t numUnreadFromServer;
2526 aSpec->GetNumUnseenMessages(&numUnreadFromServer);
2527
2528 bool partialUIDFetch;
2529 flagState->GetPartialUIDFetch(&partialUIDFetch);
2530
2531 // For partial UID fetches, we can only trust the numUnread from the server.
2532 if (partialUIDFetch) numNewUnread = numUnreadFromServer;
2533
2534 // If we are performing biff for this folder, tell the
2535 // stand-alone biff about the new high water mark
2536 if (m_performingBiff && numNewUnread) {
2537 // We must ensure that the server knows that we are performing biff.
2538 // Otherwise the stand-alone biff won't fire.
2539 nsCOMPtr<nsIMsgIncomingServer> server;
2540 if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server)
2541 server->SetPerformingBiff(true);
2542 SetNumNewMessages(numNewUnread);
2543 }
2544 SyncFlags(flagState);
2545 if (mDatabase && (int32_t)(mNumUnreadMessages + m_keysToFetch.Length()) >
2546 numUnreadFromServer)
2547 mDatabase->SyncCounts();
2548
2549 if (!m_keysToFetch.IsEmpty() && aProtocol)
2550 PrepareToAddHeadersToMailDB(aProtocol);
2551 else {
2552 bool gettingNewMessages;
2553 GetGettingNewMessages(&gettingNewMessages);
2554 if (gettingNewMessages)
2555 ProgressStatusString(aProtocol, "imapNoNewMessages", nullptr);
2556 SetPerformingBiff(false);
2557 }
2558 aSpec->GetNumMessages(&m_numServerTotalMessages);
2559 aSpec->GetNumUnseenMessages(&m_numServerUnseenMessages);
2560 aSpec->GetNumRecentMessages(&m_numServerRecentMessages);
2561
2562 // some servers don't return UIDNEXT on SELECT - don't crunch
2563 // existing values in that case.
2564 int32_t nextUID;
2565 aSpec->GetNextUID(&nextUID);
2566 if (nextUID != (int32_t)nsMsgKey_None) m_nextUID = nextUID;
2567
2568 return rv;
2569 }
2570
UpdateImapMailboxStatus(nsIImapProtocol * aProtocol,nsIMailboxSpec * aSpec)2571 NS_IMETHODIMP nsImapMailFolder::UpdateImapMailboxStatus(
2572 nsIImapProtocol* aProtocol, nsIMailboxSpec* aSpec) {
2573 NS_ENSURE_ARG_POINTER(aSpec);
2574 int32_t numUnread, numTotal;
2575 aSpec->GetNumUnseenMessages(&numUnread);
2576 aSpec->GetNumMessages(&numTotal);
2577 aSpec->GetNumRecentMessages(&m_numServerRecentMessages);
2578 int32_t prevNextUID = m_nextUID;
2579 aSpec->GetNextUID(&m_nextUID);
2580 bool summaryChanged = false;
2581
2582 // If m_numServerUnseenMessages is 0, it means
2583 // this is the first time we've done a Status.
2584 // In that case, we count all the previous pending unread messages we know
2585 // about as unread messages. We may want to do similar things with total
2586 // messages, but the total messages include deleted messages if the folder
2587 // hasn't been expunged.
2588 int32_t previousUnreadMessages =
2589 (m_numServerUnseenMessages)
2590 ? m_numServerUnseenMessages
2591 : mNumPendingUnreadMessages + mNumUnreadMessages;
2592 if (numUnread != previousUnreadMessages || m_nextUID != prevNextUID) {
2593 int32_t unreadDelta =
2594 numUnread - (mNumPendingUnreadMessages + mNumUnreadMessages);
2595 if (numUnread - previousUnreadMessages != unreadDelta)
2596 NS_WARNING("unread count should match server count");
2597 ChangeNumPendingUnread(unreadDelta);
2598 if (unreadDelta > 0 &&
2599 !(mFlags & (nsMsgFolderFlags::Trash | nsMsgFolderFlags::Junk))) {
2600 SetHasNewMessages(true);
2601 SetNumNewMessages(unreadDelta);
2602 SetBiffState(nsMsgBiffState_NewMail);
2603 }
2604 summaryChanged = true;
2605 }
2606 SetPerformingBiff(false);
2607 if (m_numServerUnseenMessages != numUnread ||
2608 m_numServerTotalMessages != numTotal) {
2609 if (numUnread > m_numServerUnseenMessages ||
2610 m_numServerTotalMessages > numTotal)
2611 NotifyHasPendingMsgs();
2612 summaryChanged = true;
2613 m_numServerUnseenMessages = numUnread;
2614 m_numServerTotalMessages = numTotal;
2615 }
2616 if (summaryChanged) SummaryChanged();
2617
2618 return NS_OK;
2619 }
2620
ParseMsgHdrs(nsIImapProtocol * aProtocol,nsIImapHeaderXferInfo * aHdrXferInfo)2621 NS_IMETHODIMP nsImapMailFolder::ParseMsgHdrs(
2622 nsIImapProtocol* aProtocol, nsIImapHeaderXferInfo* aHdrXferInfo) {
2623 NS_ENSURE_ARG_POINTER(aHdrXferInfo);
2624 int32_t numHdrs;
2625 nsCOMPtr<nsIImapHeaderInfo> headerInfo;
2626 nsCOMPtr<nsIImapUrl> aImapUrl;
2627 nsImapAction imapAction = nsIImapUrl::nsImapTest; // unused value.
2628 if (!mDatabase) GetDatabase();
2629
2630 nsresult rv = aHdrXferInfo->GetNumHeaders(&numHdrs);
2631 if (aProtocol) {
2632 (void)aProtocol->GetRunningImapURL(getter_AddRefs(aImapUrl));
2633 if (aImapUrl) aImapUrl->GetImapAction(&imapAction);
2634 }
2635 for (uint32_t i = 0; NS_SUCCEEDED(rv) && (int32_t)i < numHdrs; i++) {
2636 rv = aHdrXferInfo->GetHeader(i, getter_AddRefs(headerInfo));
2637 NS_ENSURE_SUCCESS(rv, rv);
2638 if (!headerInfo) break;
2639 int32_t msgSize;
2640 nsMsgKey msgKey;
2641 bool containsKey;
2642 const char* msgHdrs;
2643 headerInfo->GetMsgSize(&msgSize);
2644 headerInfo->GetMsgUid(&msgKey);
2645 if (msgKey == nsMsgKey_None) // not a valid uid.
2646 continue;
2647 if (imapAction == nsIImapUrl::nsImapMsgPreview) {
2648 nsCOMPtr<nsIMsgDBHdr> msgHdr;
2649 headerInfo->GetMsgHdrs(&msgHdrs);
2650 // create an input stream based on the hdr string.
2651 nsCOMPtr<nsIStringInputStream> inputStream =
2652 do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv);
2653 NS_ENSURE_SUCCESS(rv, rv);
2654 inputStream->ShareData(msgHdrs, strlen(msgHdrs));
2655 GetMessageHeader(msgKey, getter_AddRefs(msgHdr));
2656 if (msgHdr) {
2657 GetMsgPreviewTextFromStream(msgHdr, inputStream);
2658 }
2659 continue;
2660 }
2661 if (mDatabase &&
2662 NS_SUCCEEDED(mDatabase->ContainsKey(msgKey, &containsKey)) &&
2663 containsKey) {
2664 NS_ERROR("downloading hdrs for hdr we already have");
2665 continue;
2666 }
2667 nsresult rv = SetupHeaderParseStream(msgSize, EmptyCString(), nullptr);
2668 NS_ENSURE_SUCCESS(rv, rv);
2669 headerInfo->GetMsgHdrs(&msgHdrs);
2670 rv = ParseAdoptedHeaderLine(msgHdrs, msgKey);
2671 NS_ENSURE_SUCCESS(rv, rv);
2672 rv = NormalEndHeaderParseStream(aProtocol, aImapUrl);
2673 }
2674 return rv;
2675 }
2676
SetupHeaderParseStream(uint32_t aSize,const nsACString & content_type,nsIMailboxSpec * boxSpec)2677 nsresult nsImapMailFolder::SetupHeaderParseStream(
2678 uint32_t aSize, const nsACString& content_type, nsIMailboxSpec* boxSpec) {
2679 if (!mDatabase) GetDatabase();
2680 m_nextMessageByteLength = aSize;
2681 if (!m_msgParser) {
2682 nsresult rv;
2683 m_msgParser = do_CreateInstance(kParseMailMsgStateCID, &rv);
2684 NS_ENSURE_SUCCESS(rv, rv);
2685 } else
2686 m_msgParser->Clear();
2687
2688 m_msgParser->SetMailDB(mDatabase);
2689 if (mBackupDatabase) m_msgParser->SetBackupMailDB(mBackupDatabase);
2690 return m_msgParser->SetState(nsIMsgParseMailMsgState::ParseHeadersState);
2691 }
2692
ParseAdoptedHeaderLine(const char * aMessageLine,nsMsgKey aMsgKey)2693 nsresult nsImapMailFolder::ParseAdoptedHeaderLine(const char* aMessageLine,
2694 nsMsgKey aMsgKey) {
2695 // we can get blocks that contain more than one line,
2696 // but they never contain partial lines
2697 const char* str = aMessageLine;
2698 m_curMsgUid = aMsgKey;
2699 m_msgParser->SetNewKey(m_curMsgUid);
2700 // m_envelope_pos, for local folders,
2701 // is the msg key. Setting this will set the msg key for the new header.
2702
2703 int32_t len = strlen(str);
2704 char* currentEOL = PL_strstr(str, MSG_LINEBREAK);
2705 const char* currentLine = str;
2706 while (currentLine < (str + len)) {
2707 if (currentEOL) {
2708 m_msgParser->ParseAFolderLine(
2709 currentLine, (currentEOL + MSG_LINEBREAK_LEN) - currentLine);
2710 currentLine = currentEOL + MSG_LINEBREAK_LEN;
2711 currentEOL = PL_strstr(currentLine, MSG_LINEBREAK);
2712 } else {
2713 m_msgParser->ParseAFolderLine(currentLine, PL_strlen(currentLine));
2714 currentLine = str + len + 1;
2715 }
2716 }
2717 return NS_OK;
2718 }
2719
NormalEndHeaderParseStream(nsIImapProtocol * aProtocol,nsIImapUrl * imapUrl)2720 nsresult nsImapMailFolder::NormalEndHeaderParseStream(
2721 nsIImapProtocol* aProtocol, nsIImapUrl* imapUrl) {
2722 nsCOMPtr<nsIMsgDBHdr> newMsgHdr;
2723 nsresult rv;
2724 NS_ENSURE_TRUE(m_msgParser, NS_ERROR_NULL_POINTER);
2725
2726 nsMailboxParseState parseState;
2727 m_msgParser->GetState(&parseState);
2728 if (parseState == nsIMsgParseMailMsgState::ParseHeadersState)
2729 m_msgParser->ParseAFolderLine(CRLF, 2);
2730 rv = m_msgParser->GetNewMsgHdr(getter_AddRefs(newMsgHdr));
2731 NS_ENSURE_SUCCESS(rv, rv);
2732
2733 char* headers;
2734 int32_t headersSize;
2735
2736 nsCOMPtr<nsIMsgWindow> msgWindow;
2737 nsCOMPtr<nsIMsgMailNewsUrl> msgUrl;
2738 if (imapUrl) {
2739 msgUrl = do_QueryInterface(imapUrl, &rv);
2740 NS_ENSURE_SUCCESS(rv, rv);
2741 msgUrl->GetMsgWindow(getter_AddRefs(msgWindow));
2742 }
2743
2744 nsCOMPtr<nsIMsgIncomingServer> server;
2745 rv = GetServer(getter_AddRefs(server));
2746 NS_ENSURE_SUCCESS(rv, rv);
2747
2748 nsCOMPtr<nsIImapIncomingServer> imapServer = do_QueryInterface(server);
2749 rv = imapServer->GetIsGMailServer(&m_isGmailServer);
2750 NS_ENSURE_SUCCESS(rv, rv);
2751
2752 newMsgHdr->SetMessageKey(m_curMsgUid);
2753 TweakHeaderFlags(aProtocol, newMsgHdr);
2754 uint32_t messageSize;
2755 if (NS_SUCCEEDED(newMsgHdr->GetMessageSize(&messageSize)))
2756 mFolderSize += messageSize;
2757 m_msgMovedByFilter = false;
2758
2759 nsMsgKey highestUID = 0;
2760 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
2761 if (mDatabase) mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
2762 if (dbFolderInfo)
2763 dbFolderInfo->GetUint32Property(kHighestRecordedUIDPropertyName, 0,
2764 &highestUID);
2765
2766 // If this is the inbox, try to apply filters. Otherwise, test the inherited
2767 // folder property "applyIncomingFilters" (which defaults to empty). If this
2768 // inherited property has the string value "true", then apply filters even
2769 // if this is not the Inbox folder.
2770 if (mFlags & nsMsgFolderFlags::Inbox || m_applyIncomingFilters) {
2771 // Use highwater to determine whether to filter?
2772 bool filterOnHighwater = false;
2773 nsCOMPtr<nsIPrefBranch> prefBranch(
2774 do_GetService(NS_PREFSERVICE_CONTRACTID));
2775 if (prefBranch)
2776 prefBranch->GetBoolPref("mail.imap.filter_on_new", &filterOnHighwater);
2777
2778 uint32_t msgFlags;
2779 newMsgHdr->GetFlags(&msgFlags);
2780
2781 // clang-format off
2782 bool doFilter = filterOnHighwater
2783 // Filter on largest UUID and not deleted.
2784 ? m_curMsgUid > highestUID && !(msgFlags & nsMsgMessageFlags::IMAPDeleted)
2785 // Filter on unread and not deleted.
2786 : !(msgFlags & (nsMsgMessageFlags::Read | nsMsgMessageFlags::IMAPDeleted));
2787 // clang-format on
2788
2789 if (doFilter)
2790 MOZ_LOG(FILTERLOGMODULE, LogLevel::Info,
2791 ("(Imap) New message parsed, and filters will be run on it"));
2792 else
2793 MOZ_LOG(FILTERLOGMODULE, LogLevel::Info,
2794 ("(Imap) New message parsed, but filters will not be run on it"));
2795
2796 if (doFilter) {
2797 int32_t duplicateAction = nsIMsgIncomingServer::keepDups;
2798 if (server) server->GetIncomingDuplicateAction(&duplicateAction);
2799 if ((duplicateAction != nsIMsgIncomingServer::keepDups) &&
2800 mFlags & nsMsgFolderFlags::Inbox) {
2801 bool isDup;
2802 server->IsNewHdrDuplicate(newMsgHdr, &isDup);
2803 if (isDup) {
2804 // we want to do something similar to applying filter hits.
2805 // if a dup is marked read, it shouldn't trigger biff.
2806 // Same for deleting it or moving it to trash.
2807 switch (duplicateAction) {
2808 case nsIMsgIncomingServer::deleteDups: {
2809 uint32_t newFlags;
2810 newMsgHdr->OrFlags(
2811 nsMsgMessageFlags::Read | nsMsgMessageFlags::IMAPDeleted,
2812 &newFlags);
2813 StoreImapFlags(kImapMsgSeenFlag | kImapMsgDeletedFlag, true,
2814 {m_curMsgUid}, nullptr);
2815 m_msgMovedByFilter = true;
2816 } break;
2817 case nsIMsgIncomingServer::moveDupsToTrash: {
2818 nsCOMPtr<nsIMsgFolder> trash;
2819 GetTrashFolder(getter_AddRefs(trash));
2820 if (trash) {
2821 nsCString trashUri;
2822 trash->GetURI(trashUri);
2823 nsresult err = MoveIncorporatedMessage(
2824 newMsgHdr, mDatabase, trashUri, nullptr, msgWindow);
2825 if (NS_SUCCEEDED(err)) m_msgMovedByFilter = true;
2826 }
2827 } break;
2828 case nsIMsgIncomingServer::markDupsRead: {
2829 uint32_t newFlags;
2830 newMsgHdr->OrFlags(nsMsgMessageFlags::Read, &newFlags);
2831 StoreImapFlags(kImapMsgSeenFlag, true, {m_curMsgUid}, nullptr);
2832 } break;
2833 }
2834 int32_t numNewMessages;
2835 GetNumNewMessages(false, &numNewMessages);
2836 SetNumNewMessages(numNewMessages - 1);
2837 }
2838 }
2839 rv = m_msgParser->GetAllHeaders(&headers, &headersSize);
2840
2841 if (NS_SUCCEEDED(rv) && headers && !m_msgMovedByFilter &&
2842 !m_filterListRequiresBody) {
2843 if (m_filterList) {
2844 GetMoveCoalescer(); // not sure why we're doing this here.
2845 MOZ_LOG(FILTERLOGMODULE, LogLevel::Debug,
2846 ("(Imap) ApplyFilterToHdr from "
2847 "nsImapMailFolder::NormalEndHeaderParseStream()"));
2848 m_filterList->ApplyFiltersToHdr(
2849 nsMsgFilterType::InboxRule, newMsgHdr, this, mDatabase,
2850 nsDependentCSubstring(headers, headersSize), this, msgWindow);
2851 NotifyFolderEvent(kFiltersApplied);
2852 }
2853 }
2854 }
2855 }
2856 // here we need to tweak flags from uid state..
2857 if (mDatabase && (!m_msgMovedByFilter || ShowDeletedMessages())) {
2858 nsCOMPtr<nsIMsgFolderNotificationService> notifier(
2859 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
2860 // Check if this header corresponds to a pseudo header
2861 // we have from doing a pseudo-offline move and then downloading
2862 // the real header from the server. In that case, we notify
2863 // db/folder listeners that the pseudo-header has become the new
2864 // header, i.e., the key has changed.
2865 nsCString newMessageId;
2866 newMsgHdr->GetMessageId(getter_Copies(newMessageId));
2867 nsMsgKey pseudoKey =
2868 m_pseudoHdrs.MaybeGet(newMessageId).valueOr(nsMsgKey_None);
2869 if (notifier && pseudoKey != nsMsgKey_None) {
2870 notifier->NotifyMsgKeyChanged(pseudoKey, newMsgHdr);
2871 m_pseudoHdrs.Remove(newMessageId);
2872 }
2873 mDatabase->AddNewHdrToDB(newMsgHdr, true);
2874 if (notifier) notifier->NotifyMsgAdded(newMsgHdr);
2875 // mark the header as not yet reported classified
2876 OrProcessingFlags(m_curMsgUid, nsMsgProcessingFlags::NotReportedClassified);
2877 }
2878 // adjust highestRecordedUID
2879 if (dbFolderInfo) {
2880 if (m_curMsgUid > highestUID) {
2881 MOZ_LOG(IMAP_CS, mozilla::LogLevel::Debug,
2882 ("NormalEndHeaderParseStream(): Store new highest UID=%" PRIu32
2883 " for folder=%s",
2884 m_curMsgUid, m_onlineFolderName.get()));
2885 dbFolderInfo->SetUint32Property(kHighestRecordedUIDPropertyName,
2886 m_curMsgUid);
2887 }
2888 }
2889
2890 if (m_isGmailServer) {
2891 nsCOMPtr<nsIImapFlagAndUidState> flagState;
2892 aProtocol->GetFlagAndUidState(getter_AddRefs(flagState));
2893 nsCString msgIDValue;
2894 nsCString threadIDValue;
2895 nsCString labelsValue;
2896 flagState->GetCustomAttribute(m_curMsgUid, "X-GM-MSGID"_ns, msgIDValue);
2897 flagState->GetCustomAttribute(m_curMsgUid, "X-GM-THRID"_ns, threadIDValue);
2898 flagState->GetCustomAttribute(m_curMsgUid, "X-GM-LABELS"_ns, labelsValue);
2899 newMsgHdr->SetStringProperty("X-GM-MSGID", msgIDValue.get());
2900 newMsgHdr->SetStringProperty("X-GM-THRID", threadIDValue.get());
2901 newMsgHdr->SetStringProperty("X-GM-LABELS", labelsValue.get());
2902 }
2903
2904 m_msgParser->Clear(); // clear out parser, because it holds onto a msg hdr.
2905 m_msgParser->SetMailDB(nullptr); // tell it to let go of the db too.
2906 // I don't think we want to do this - it does bad things like set the size
2907 // incorrectly.
2908 // m_msgParser->FinishHeader();
2909 return NS_OK;
2910 }
2911
AbortHeaderParseStream(nsIImapProtocol * aProtocol)2912 NS_IMETHODIMP nsImapMailFolder::AbortHeaderParseStream(
2913 nsIImapProtocol* aProtocol) {
2914 nsresult rv = NS_ERROR_FAILURE;
2915 return rv;
2916 }
2917
BeginCopy(nsIMsgDBHdr * message)2918 NS_IMETHODIMP nsImapMailFolder::BeginCopy(nsIMsgDBHdr* message) {
2919 NS_ENSURE_TRUE(m_copyState, NS_ERROR_NULL_POINTER);
2920 nsresult rv;
2921 if (m_copyState->m_tmpFile) // leftover file spec nuke it
2922 {
2923 rv = m_copyState->m_tmpFile->Remove(false);
2924 if (NS_FAILED(rv)) {
2925 nsCString nativePath = m_copyState->m_tmpFile->HumanReadablePath();
2926 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
2927 ("couldn't remove prev temp file %s: %" PRIx32, nativePath.get(),
2928 static_cast<uint32_t>(rv)));
2929 }
2930 m_copyState->m_tmpFile = nullptr;
2931 }
2932
2933 rv = GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR, "nscpmsg.txt",
2934 getter_AddRefs(m_copyState->m_tmpFile));
2935 if (NS_FAILED(rv))
2936 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
2937 ("couldn't find nscpmsg.txt:%" PRIx32, static_cast<uint32_t>(rv)));
2938 NS_ENSURE_SUCCESS(rv, rv);
2939
2940 // create a unique file, since multiple copies may be open on multiple folders
2941 rv = m_copyState->m_tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600);
2942 if (NS_FAILED(rv)) {
2943 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
2944 ("couldn't create temp nscpmsg.txt:%" PRIx32,
2945 static_cast<uint32_t>(rv)));
2946 // Last ditch attempt to create a temp file, because virus checker might
2947 // be locking the previous temp file, and CreateUnique fails if the file
2948 // is locked. Use the message key to make a unique name.
2949 if (message) {
2950 nsCString tmpFileName("nscpmsg-");
2951 nsMsgKey msgKey;
2952 message->GetMessageKey(&msgKey);
2953 tmpFileName.AppendInt(msgKey);
2954 tmpFileName.AppendLiteral(".txt");
2955 m_copyState->m_tmpFile->SetNativeLeafName(tmpFileName);
2956 rv = m_copyState->m_tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE,
2957 00600);
2958 if (NS_FAILED(rv)) {
2959 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
2960 ("couldn't create temp nscpmsg.txt: %" PRIx32,
2961 static_cast<uint32_t>(rv)));
2962 OnCopyCompleted(m_copyState->m_srcSupport, rv);
2963 return rv;
2964 }
2965 }
2966 }
2967
2968 nsCOMPtr<nsIOutputStream> fileOutputStream;
2969 rv = MsgNewBufferedFileOutputStream(
2970 getter_AddRefs(m_copyState->m_msgFileStream), m_copyState->m_tmpFile, -1,
2971 00600);
2972 if (NS_FAILED(rv))
2973 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
2974 ("couldn't create output file stream: %" PRIx32,
2975 static_cast<uint32_t>(rv)));
2976
2977 if (!m_copyState->m_dataBuffer)
2978 m_copyState->m_dataBuffer = (char*)PR_CALLOC(COPY_BUFFER_SIZE + 1);
2979 NS_ENSURE_TRUE(m_copyState->m_dataBuffer, NS_ERROR_OUT_OF_MEMORY);
2980 m_copyState->m_dataBufferSize = COPY_BUFFER_SIZE;
2981 return NS_OK;
2982 }
2983
CopyDataToOutputStreamForAppend(nsIInputStream * aIStream,int32_t aLength,nsIOutputStream * outputStream)2984 NS_IMETHODIMP nsImapMailFolder::CopyDataToOutputStreamForAppend(
2985 nsIInputStream* aIStream, int32_t aLength, nsIOutputStream* outputStream) {
2986 uint32_t readCount;
2987 uint32_t writeCount;
2988 if (!m_copyState) m_copyState = new nsImapMailCopyState();
2989
2990 if (aLength + m_copyState->m_leftOver > m_copyState->m_dataBufferSize) {
2991 char* newBuffer = (char*)PR_REALLOC(m_copyState->m_dataBuffer,
2992 aLength + m_copyState->m_leftOver + 1);
2993 NS_ENSURE_TRUE(newBuffer, NS_ERROR_OUT_OF_MEMORY);
2994 m_copyState->m_dataBuffer = newBuffer;
2995 m_copyState->m_dataBufferSize = aLength + m_copyState->m_leftOver;
2996 }
2997
2998 char *start, *end;
2999 uint32_t linebreak_len = 1;
3000
3001 nsresult rv = aIStream->Read(
3002 m_copyState->m_dataBuffer + m_copyState->m_leftOver, aLength, &readCount);
3003 if (NS_FAILED(rv)) return rv;
3004
3005 m_copyState->m_leftOver += readCount;
3006 m_copyState->m_dataBuffer[m_copyState->m_leftOver] = '\0';
3007
3008 start = m_copyState->m_dataBuffer;
3009 if (m_copyState->m_eatLF) {
3010 if (*start == '\n') start++;
3011 m_copyState->m_eatLF = false;
3012 }
3013 end = PL_strpbrk(start, "\r\n");
3014 if (end && *end == '\r' && *(end + 1) == '\n') linebreak_len = 2;
3015
3016 while (start && end) {
3017 if (PL_strncasecmp(start, "X-Mozilla-Status:", 17) &&
3018 PL_strncasecmp(start, "X-Mozilla-Status2:", 18) &&
3019 PL_strncmp(start, "From - ", 7)) {
3020 rv = outputStream->Write(start, end - start, &writeCount);
3021 rv = outputStream->Write(CRLF, 2, &writeCount);
3022 }
3023 start = end + linebreak_len;
3024 if (start >= m_copyState->m_dataBuffer + m_copyState->m_leftOver) {
3025 m_copyState->m_leftOver = 0;
3026 break;
3027 }
3028 linebreak_len = 1;
3029
3030 end = PL_strpbrk(start, "\r\n");
3031 if (end && *end == '\r') {
3032 if (*(end + 1) == '\n')
3033 linebreak_len = 2;
3034 else if (!*(end + 1)) // block might have split CRLF so remember if
3035 m_copyState->m_eatLF = true; // we should eat LF
3036 }
3037
3038 if (start && !end) {
3039 m_copyState->m_leftOver -= (start - m_copyState->m_dataBuffer);
3040 memcpy(m_copyState->m_dataBuffer, start,
3041 m_copyState->m_leftOver + 1); // including null
3042 }
3043 }
3044 return rv;
3045 }
3046
CopyDataDone()3047 NS_IMETHODIMP nsImapMailFolder::CopyDataDone() {
3048 m_copyState = nullptr;
3049 return NS_OK;
3050 }
3051
3052 // sICopyMessageListener methods, BeginCopy, CopyData, EndCopy, EndMove,
3053 // StartMessage, EndMessage
CopyData(nsIInputStream * aIStream,int32_t aLength)3054 NS_IMETHODIMP nsImapMailFolder::CopyData(nsIInputStream* aIStream,
3055 int32_t aLength) {
3056 NS_ENSURE_TRUE(
3057 m_copyState && m_copyState->m_msgFileStream && m_copyState->m_dataBuffer,
3058 NS_ERROR_NULL_POINTER);
3059 nsresult rv = CopyDataToOutputStreamForAppend(aIStream, aLength,
3060 m_copyState->m_msgFileStream);
3061 if (NS_FAILED(rv)) {
3062 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
3063 ("CopyData failed: %" PRIx32, static_cast<uint32_t>(rv)));
3064 OnCopyCompleted(m_copyState->m_srcSupport, rv);
3065 }
3066 return rv;
3067 }
3068
EndCopy(bool copySucceeded)3069 NS_IMETHODIMP nsImapMailFolder::EndCopy(bool copySucceeded) {
3070 nsresult rv = copySucceeded ? NS_OK : NS_ERROR_FAILURE;
3071 if (copySucceeded && m_copyState && m_copyState->m_msgFileStream) {
3072 nsCOMPtr<nsIUrlListener> urlListener;
3073 m_copyState->m_msgFileStream->Close();
3074 // m_tmpFile can be stale because we wrote to it
3075 nsCOMPtr<nsIFile> tmpFile;
3076 m_copyState->m_tmpFile->Clone(getter_AddRefs(tmpFile));
3077 m_copyState->m_tmpFile = tmpFile;
3078 nsCOMPtr<nsIImapService> imapService =
3079 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
3080 NS_ENSURE_SUCCESS(rv, rv);
3081
3082 rv =
3083 QueryInterface(NS_GET_IID(nsIUrlListener), getter_AddRefs(urlListener));
3084 rv = imapService->AppendMessageFromFile(
3085 m_copyState->m_tmpFile, this, EmptyCString(), true,
3086 m_copyState->m_selectedState, urlListener, nullptr, m_copyState,
3087 m_copyState->m_msgWindow);
3088 }
3089 if (NS_FAILED(rv) || !copySucceeded)
3090 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
3091 ("EndCopy failed: %" PRIx32, static_cast<uint32_t>(rv)));
3092 return rv;
3093 }
3094
EndMove(bool moveSucceeded)3095 NS_IMETHODIMP nsImapMailFolder::EndMove(bool moveSucceeded) { return NS_OK; }
3096 // this is the beginning of the next message copied
StartMessage()3097 NS_IMETHODIMP nsImapMailFolder::StartMessage() { return NS_OK; }
3098
3099 // just finished the current message.
EndMessage(nsMsgKey key)3100 NS_IMETHODIMP nsImapMailFolder::EndMessage(nsMsgKey key) { return NS_OK; }
3101
ApplyFilterHit(nsIMsgFilter * filter,nsIMsgWindow * msgWindow,bool * applyMore)3102 NS_IMETHODIMP nsImapMailFolder::ApplyFilterHit(nsIMsgFilter* filter,
3103 nsIMsgWindow* msgWindow,
3104 bool* applyMore) {
3105 //
3106 // This routine is called indirectly from ApplyFiltersToHdr in two
3107 // circumstances, controlled by m_filterListRequiresBody:
3108 //
3109 // If false, after headers are parsed in NormalEndHeaderParseStream.
3110 // If true, after the message body is downloaded in NormalEndMsgWriteStream.
3111 //
3112 // In NormalEndHeaderParseStream, the message has not been added to the
3113 // database, and it is important that database notifications and count
3114 // updates do not occur. In NormalEndMsgWriteStream, the message has been
3115 // added to the database, and database notifications and count updates
3116 // should be performed.
3117 //
3118
3119 NS_ENSURE_ARG_POINTER(filter);
3120 NS_ENSURE_ARG_POINTER(applyMore);
3121
3122 nsresult rv = NS_OK;
3123
3124 nsCOMPtr<nsIMsgDBHdr> msgHdr;
3125 if (m_filterListRequiresBody)
3126 GetMessageHeader(m_curMsgUid, getter_AddRefs(msgHdr));
3127 else if (m_msgParser)
3128 m_msgParser->GetNewMsgHdr(getter_AddRefs(msgHdr));
3129 NS_ENSURE_TRUE(msgHdr,
3130 NS_ERROR_NULL_POINTER); // fatal error, cannot apply filters
3131
3132 bool deleteToTrash = DeleteIsMoveToTrash();
3133
3134 nsTArray<RefPtr<nsIMsgRuleAction>> filterActionList;
3135 rv = filter->GetSortedActionList(filterActionList);
3136 NS_ENSURE_SUCCESS(rv, rv);
3137
3138 uint32_t numActions = filterActionList.Length();
3139
3140 nsCString msgId;
3141 msgHdr->GetMessageId(getter_Copies(msgId));
3142 nsMsgKey msgKey;
3143 msgHdr->GetMessageKey(&msgKey);
3144 MOZ_LOG(FILTERLOGMODULE, LogLevel::Info,
3145 ("(Imap) Applying %" PRIu32
3146 " filter actions on message with key %" PRIu32,
3147 numActions, msgKeyToInt(msgKey)));
3148 MOZ_LOG(FILTERLOGMODULE, LogLevel::Debug,
3149 ("(Imap) Message ID: %s", msgId.get()));
3150
3151 bool loggingEnabled = false;
3152 if (m_filterList && numActions)
3153 (void)m_filterList->GetLoggingEnabled(&loggingEnabled);
3154
3155 bool msgIsNew = true;
3156
3157 rv = GetDatabase();
3158 NS_ENSURE_SUCCESS(rv, rv);
3159
3160 nsresult finalResult = NS_OK; // result of all actions
3161 for (uint32_t actionIndex = 0; actionIndex < numActions; actionIndex++) {
3162 nsCOMPtr<nsIMsgRuleAction> filterAction(filterActionList[actionIndex]);
3163 if (!filterAction) {
3164 MOZ_LOG(FILTERLOGMODULE, LogLevel::Warning,
3165 ("(Imap) Filter action at index %" PRIu32 " invalid, skipping",
3166 actionIndex));
3167 continue;
3168 }
3169
3170 rv = NS_OK; // result of the current action
3171 nsMsgRuleActionType actionType;
3172 if (NS_SUCCEEDED(filterAction->GetType(&actionType))) {
3173 MOZ_LOG(FILTERLOGMODULE, LogLevel::Info,
3174 ("(Imap) Running filter action at index %" PRIu32
3175 ", action type = %i",
3176 actionIndex, actionType));
3177 if (loggingEnabled) (void)filter->LogRuleHit(filterAction, msgHdr);
3178
3179 nsCString actionTargetFolderUri;
3180 if (actionType == nsMsgFilterAction::MoveToFolder ||
3181 actionType == nsMsgFilterAction::CopyToFolder) {
3182 rv = filterAction->GetTargetFolderUri(actionTargetFolderUri);
3183 if (NS_FAILED(rv) || actionTargetFolderUri.IsEmpty()) {
3184 // clang-format off
3185 MOZ_LOG(FILTERLOGMODULE, LogLevel::Warning,
3186 ("(Imap) Target URI for Copy/Move action is empty, skipping"));
3187 // clang-format on
3188 NS_ASSERTION(false, "actionTargetFolderUri is empty");
3189 continue;
3190 }
3191 }
3192
3193 uint32_t msgFlags;
3194 msgHdr->GetFlags(&msgFlags);
3195 bool isRead = (msgFlags & nsMsgMessageFlags::Read);
3196
3197 switch (actionType) {
3198 case nsMsgFilterAction::Delete: {
3199 if (deleteToTrash) {
3200 // set value to trash folder
3201 nsCOMPtr<nsIMsgFolder> mailTrash;
3202 rv = GetTrashFolder(getter_AddRefs(mailTrash));
3203 if (NS_SUCCEEDED(rv) && mailTrash) {
3204 rv = mailTrash->GetURI(actionTargetFolderUri);
3205 if (NS_FAILED(rv)) break;
3206 }
3207 // msgHdr->OrFlags(nsMsgMessageFlags::Read, &newFlags); // mark
3208 // read in trash.
3209 } else {
3210 mDatabase->MarkHdrRead(msgHdr, true, nullptr);
3211 mDatabase->MarkImapDeleted(msgKey, true, nullptr);
3212 rv = StoreImapFlags(kImapMsgSeenFlag | kImapMsgDeletedFlag, true,
3213 {msgKey}, nullptr);
3214 if (NS_FAILED(rv)) break;
3215 // this will prevent us from adding the header to the db.
3216 m_msgMovedByFilter = true;
3217 }
3218 msgIsNew = false;
3219 }
3220 // note that delete falls through to move.
3221 [[fallthrough]];
3222 case nsMsgFilterAction::MoveToFolder: {
3223 // if moving to a different file, do it.
3224 nsCString uri;
3225 rv = GetURI(uri);
3226 if (NS_FAILED(rv)) break;
3227
3228 if (!actionTargetFolderUri.Equals(uri)) {
3229 msgHdr->GetFlags(&msgFlags);
3230 if (msgFlags & nsMsgMessageFlags::MDNReportNeeded && !isRead) {
3231 mDatabase->MarkMDNNeeded(msgKey, false, nullptr);
3232 mDatabase->MarkMDNSent(msgKey, true, nullptr);
3233 }
3234 nsresult rv = MoveIncorporatedMessage(
3235 msgHdr, mDatabase, actionTargetFolderUri, filter, msgWindow);
3236 if (NS_SUCCEEDED(rv)) {
3237 m_msgMovedByFilter = true;
3238 } else {
3239 if (loggingEnabled) {
3240 (void)filter->LogRuleHitFail(filterAction, msgHdr, rv,
3241 "filterFailureMoveFailed"_ns);
3242 }
3243 }
3244 }
3245 // don't apply any more filters, even if it was a move to the same
3246 // folder
3247 *applyMore = false;
3248 } break;
3249 case nsMsgFilterAction::CopyToFolder: {
3250 nsCString uri;
3251 rv = GetURI(uri);
3252 if (NS_FAILED(rv)) break;
3253
3254 if (!actionTargetFolderUri.Equals(uri)) {
3255 // XXXshaver I'm not actually 100% what the right semantics are for
3256 // MDNs and copied messages, but I suspect deep down inside that
3257 // we probably want to suppress them only on the copies.
3258 msgHdr->GetFlags(&msgFlags);
3259 if (msgFlags & nsMsgMessageFlags::MDNReportNeeded && !isRead) {
3260 mDatabase->MarkMDNNeeded(msgKey, false, nullptr);
3261 mDatabase->MarkMDNSent(msgKey, true, nullptr);
3262 }
3263
3264 nsCOMPtr<nsIMsgFolder> dstFolder;
3265 rv = GetExistingFolder(actionTargetFolderUri,
3266 getter_AddRefs(dstFolder));
3267 if (NS_FAILED(rv)) break;
3268
3269 nsCOMPtr<nsIMsgCopyService> copyService =
3270 do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
3271 if (NS_FAILED(rv)) break;
3272 rv = copyService->CopyMessages(this, {&*msgHdr}, dstFolder, false,
3273 nullptr, msgWindow, false);
3274 if (NS_FAILED(rv)) {
3275 if (loggingEnabled) {
3276 (void)filter->LogRuleHitFail(filterAction, msgHdr, rv,
3277 "filterFailureCopyFailed"_ns);
3278 }
3279 }
3280 }
3281 } break;
3282 case nsMsgFilterAction::MarkRead: {
3283 mDatabase->MarkHdrRead(msgHdr, true, nullptr);
3284 rv = StoreImapFlags(kImapMsgSeenFlag, true, {msgKey}, nullptr);
3285 msgIsNew = false;
3286 } break;
3287 case nsMsgFilterAction::MarkUnread: {
3288 mDatabase->MarkHdrRead(msgHdr, false, nullptr);
3289 rv = StoreImapFlags(kImapMsgSeenFlag, false, {msgKey}, nullptr);
3290 msgIsNew = true;
3291 } break;
3292 case nsMsgFilterAction::MarkFlagged: {
3293 mDatabase->MarkHdrMarked(msgHdr, true, nullptr);
3294 rv = StoreImapFlags(kImapMsgFlaggedFlag, true, {msgKey}, nullptr);
3295 } break;
3296 case nsMsgFilterAction::KillThread:
3297 case nsMsgFilterAction::WatchThread: {
3298 nsCOMPtr<nsIMsgThread> msgThread;
3299 nsMsgKey threadKey;
3300 mDatabase->GetThreadContainingMsgHdr(msgHdr,
3301 getter_AddRefs(msgThread));
3302 if (msgThread) {
3303 msgThread->GetThreadKey(&threadKey);
3304 if (actionType == nsMsgFilterAction::KillThread)
3305 rv = mDatabase->MarkThreadIgnored(msgThread, threadKey, true,
3306 nullptr);
3307 else
3308 rv = mDatabase->MarkThreadWatched(msgThread, threadKey, true,
3309 nullptr);
3310 } else {
3311 if (actionType == nsMsgFilterAction::KillThread)
3312 rv = msgHdr->SetUint32Property("ProtoThreadFlags",
3313 nsMsgMessageFlags::Ignored);
3314 else
3315 rv = msgHdr->SetUint32Property("ProtoThreadFlags",
3316 nsMsgMessageFlags::Watched);
3317 }
3318 if (actionType == nsMsgFilterAction::KillThread) {
3319 mDatabase->MarkHdrRead(msgHdr, true, nullptr);
3320 rv = StoreImapFlags(kImapMsgSeenFlag, true, {msgKey}, nullptr);
3321 msgIsNew = false;
3322 }
3323 } break;
3324 case nsMsgFilterAction::KillSubthread: {
3325 mDatabase->MarkHeaderKilled(msgHdr, true, nullptr);
3326 mDatabase->MarkHdrRead(msgHdr, true, nullptr);
3327 rv = StoreImapFlags(kImapMsgSeenFlag, true, {msgKey}, nullptr);
3328 msgIsNew = false;
3329 } break;
3330 case nsMsgFilterAction::ChangePriority: {
3331 nsMsgPriorityValue filterPriority; // a int32_t
3332 filterAction->GetPriority(&filterPriority);
3333 rv = mDatabase->SetUint32PropertyByHdr(
3334 msgHdr, "priority", static_cast<uint32_t>(filterPriority));
3335 } break;
3336 case nsMsgFilterAction::Label: {
3337 nsMsgLabelValue filterLabel;
3338 filterAction->GetLabel(&filterLabel);
3339 mDatabase->SetUint32PropertyByHdr(msgHdr, "label",
3340 static_cast<uint32_t>(filterLabel));
3341 rv = StoreImapFlags((filterLabel << 9), true, {msgKey}, nullptr);
3342 } break;
3343 case nsMsgFilterAction::AddTag: {
3344 nsCString keyword;
3345 filterAction->GetStrValue(keyword);
3346 rv = AddKeywordsToMessages({&*msgHdr}, keyword);
3347 } break;
3348 case nsMsgFilterAction::JunkScore: {
3349 nsAutoCString junkScoreStr;
3350 int32_t junkScore;
3351 filterAction->GetJunkScore(&junkScore);
3352 junkScoreStr.AppendInt(junkScore);
3353 rv = mDatabase->SetStringProperty(msgKey, "junkscore",
3354 junkScoreStr.get());
3355 mDatabase->SetStringProperty(msgKey, "junkscoreorigin", "filter");
3356
3357 // If score is available, set up to store junk status on server.
3358 if (junkScore == nsIJunkMailPlugin::IS_SPAM_SCORE ||
3359 junkScore == nsIJunkMailPlugin::IS_HAM_SCORE) {
3360 nsTArray<nsMsgKey>* keysToClassify = m_moveCoalescer->GetKeyBucket(
3361 (junkScore == nsIJunkMailPlugin::IS_SPAM_SCORE) ? 0 : 1);
3362 NS_ASSERTION(keysToClassify, "error getting key bucket");
3363 if (keysToClassify) keysToClassify->AppendElement(msgKey);
3364 if (msgIsNew && junkScore == nsIJunkMailPlugin::IS_SPAM_SCORE) {
3365 msgIsNew = false;
3366 mDatabase->MarkHdrNotNew(msgHdr, nullptr);
3367 // nsMsgDBFolder::SendFlagNotifications by the call to
3368 // SetBiffState(nsMsgBiffState_NoMail) will reset numNewMessages
3369 // only if the message is also read and database notifications
3370 // are active, but we are not going to mark it read in this
3371 // action, preferring to leave the choice to the user.
3372 // So correct numNewMessages.
3373 if (m_filterListRequiresBody) {
3374 msgHdr->GetFlags(&msgFlags);
3375 if (!(msgFlags & nsMsgMessageFlags::Read)) {
3376 int32_t numNewMessages;
3377 GetNumNewMessages(false, &numNewMessages);
3378 SetNumNewMessages(--numNewMessages);
3379 SetHasNewMessages(numNewMessages != 0);
3380 }
3381 }
3382 }
3383 }
3384 } break;
3385 case nsMsgFilterAction::Forward: {
3386 nsCString forwardTo;
3387 filterAction->GetStrValue(forwardTo);
3388 nsCOMPtr<nsIMsgIncomingServer> server;
3389 rv = GetServer(getter_AddRefs(server));
3390 if (NS_FAILED(rv)) break;
3391 if (!forwardTo.IsEmpty()) {
3392 nsCOMPtr<nsIMsgComposeService> compService =
3393 do_GetService(NS_MSGCOMPOSESERVICE_CONTRACTID, &rv);
3394 if (NS_FAILED(rv)) break;
3395 rv = compService->ForwardMessage(
3396 NS_ConvertUTF8toUTF16(forwardTo), msgHdr, msgWindow, server,
3397 nsIMsgComposeService::kForwardAsDefault);
3398 }
3399 } break;
3400
3401 case nsMsgFilterAction::Reply: {
3402 nsCString replyTemplateUri;
3403 filterAction->GetStrValue(replyTemplateUri);
3404 nsCOMPtr<nsIMsgIncomingServer> server;
3405 rv = GetServer(getter_AddRefs(server));
3406 if (NS_FAILED(rv)) break;
3407 if (!replyTemplateUri.IsEmpty()) {
3408 nsCOMPtr<nsIMsgComposeService> compService =
3409 do_GetService(NS_MSGCOMPOSESERVICE_CONTRACTID, &rv);
3410 if (NS_SUCCEEDED(rv) && compService) {
3411 rv = compService->ReplyWithTemplate(msgHdr, replyTemplateUri,
3412 msgWindow, server);
3413 if (NS_FAILED(rv)) {
3414 NS_WARNING("ReplyWithTemplate failed");
3415 if (rv == NS_ERROR_ABORT) {
3416 (void)filter->LogRuleHitFail(
3417 filterAction, msgHdr, rv,
3418 "filterFailureSendingReplyAborted"_ns);
3419 } else {
3420 (void)filter->LogRuleHitFail(
3421 filterAction, msgHdr, rv,
3422 "filterFailureSendingReplyError"_ns);
3423 }
3424 }
3425 }
3426 }
3427 } break;
3428
3429 case nsMsgFilterAction::StopExecution: {
3430 // don't apply any more filters
3431 *applyMore = false;
3432 rv = NS_OK;
3433 } break;
3434
3435 case nsMsgFilterAction::Custom: {
3436 nsCOMPtr<nsIMsgFilterCustomAction> customAction;
3437 rv = filterAction->GetCustomAction(getter_AddRefs(customAction));
3438 if (NS_FAILED(rv)) break;
3439
3440 nsAutoCString value;
3441 rv = filterAction->GetStrValue(value);
3442 if (NS_FAILED(rv)) break;
3443
3444 rv = customAction->ApplyAction({&*msgHdr}, value, nullptr,
3445 nsMsgFilterType::InboxRule, msgWindow);
3446 // allow custom action to affect new
3447 msgHdr->GetFlags(&msgFlags);
3448 if (!(msgFlags & nsMsgMessageFlags::New)) msgIsNew = false;
3449 } break;
3450
3451 default:
3452 NS_ERROR("unexpected filter action");
3453 rv = NS_ERROR_UNEXPECTED;
3454 break;
3455 }
3456 }
3457 if (NS_FAILED(rv)) {
3458 finalResult = rv;
3459 MOZ_LOG(FILTERLOGMODULE, LogLevel::Error,
3460 ("(Imap) Action execution failed with error: %" PRIx32,
3461 static_cast<uint32_t>(rv)));
3462 if (loggingEnabled) {
3463 (void)filter->LogRuleHitFail(filterAction, msgHdr, rv,
3464 "filterFailureAction"_ns);
3465 }
3466 } else {
3467 MOZ_LOG(FILTERLOGMODULE, LogLevel::Info,
3468 ("(Imap) Action execution succeeded"));
3469 }
3470 }
3471 if (!msgIsNew) {
3472 int32_t numNewMessages;
3473 GetNumNewMessages(false, &numNewMessages);
3474 // When database notifications are active, new counts will be reset
3475 // to zero in nsMsgDBFolder::SendFlagNotifications by the call to
3476 // SetBiffState(nsMsgBiffState_NoMail), so don't repeat them here.
3477 if (!m_filterListRequiresBody) SetNumNewMessages(--numNewMessages);
3478 if (mDatabase) mDatabase->MarkHdrNotNew(msgHdr, nullptr);
3479 MOZ_LOG(FILTERLOGMODULE, LogLevel::Info,
3480 ("(Imap) Message will not be marked new"));
3481 }
3482 MOZ_LOG(FILTERLOGMODULE, LogLevel::Info,
3483 ("(Imap) Finished executing actions"));
3484 return finalResult;
3485 }
3486
SetImapFlags(const char * uids,int32_t flags,nsIURI ** url)3487 NS_IMETHODIMP nsImapMailFolder::SetImapFlags(const char* uids, int32_t flags,
3488 nsIURI** url) {
3489 nsresult rv;
3490 nsCOMPtr<nsIImapService> imapService =
3491 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
3492 NS_ENSURE_SUCCESS(rv, rv);
3493
3494 return imapService->SetMessageFlags(this, this, url, nsAutoCString(uids),
3495 flags, true);
3496 }
3497
3498 // "this" is the parent folder
PlaybackOfflineFolderCreate(const nsAString & aFolderName,nsIMsgWindow * aWindow,nsIURI ** url)3499 NS_IMETHODIMP nsImapMailFolder::PlaybackOfflineFolderCreate(
3500 const nsAString& aFolderName, nsIMsgWindow* aWindow, nsIURI** url) {
3501 nsresult rv;
3502 nsCOMPtr<nsIImapService> imapService =
3503 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
3504 NS_ENSURE_SUCCESS(rv, rv);
3505 return imapService->CreateFolder(this, aFolderName, this, url);
3506 }
3507
3508 NS_IMETHODIMP
ReplayOfflineMoveCopy(const nsTArray<nsMsgKey> & aMsgKeys,bool isMove,nsIMsgFolder * aDstFolder,nsIUrlListener * aUrlListener,nsIMsgWindow * aWindow)3509 nsImapMailFolder::ReplayOfflineMoveCopy(const nsTArray<nsMsgKey>& aMsgKeys,
3510 bool isMove, nsIMsgFolder* aDstFolder,
3511 nsIUrlListener* aUrlListener,
3512 nsIMsgWindow* aWindow) {
3513 nsresult rv;
3514
3515 nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(aDstFolder);
3516 if (imapFolder) {
3517 nsImapMailFolder* destImapFolder =
3518 static_cast<nsImapMailFolder*>(aDstFolder);
3519 nsCOMPtr<nsIMsgDatabase> dstFolderDB;
3520 aDstFolder->GetMsgDatabase(getter_AddRefs(dstFolderDB));
3521 if (dstFolderDB) {
3522 // find the fake header in the destination db, and use that to
3523 // set the pending attributes on the real headers. To do this,
3524 // we need to iterate over the offline ops in the destination db,
3525 // looking for ones with matching keys and source folder uri.
3526 // If we find that offline op, its "key" will be the key of the fake
3527 // header, so we just need to get the header for that key
3528 // from the dest db.
3529 nsTArray<nsMsgKey> offlineOps;
3530 if (NS_SUCCEEDED(dstFolderDB->ListAllOfflineOpIds(&offlineOps))) {
3531 nsTArray<RefPtr<nsIMsgDBHdr>> messages;
3532 nsCString srcFolderUri;
3533 GetURI(srcFolderUri);
3534 nsCOMPtr<nsIMsgOfflineImapOperation> currentOp;
3535 for (uint32_t opIndex = 0; opIndex < offlineOps.Length(); opIndex++) {
3536 dstFolderDB->GetOfflineOpForKey(offlineOps[opIndex], false,
3537 getter_AddRefs(currentOp));
3538 if (currentOp) {
3539 nsCString opSrcUri;
3540 currentOp->GetSourceFolderURI(opSrcUri);
3541 if (opSrcUri.Equals(srcFolderUri)) {
3542 nsMsgKey srcMessageKey;
3543 currentOp->GetSrcMessageKey(&srcMessageKey);
3544 for (auto key : aMsgKeys) {
3545 if (srcMessageKey == key) {
3546 nsCOMPtr<nsIMsgDBHdr> fakeDestHdr;
3547 dstFolderDB->GetMsgHdrForKey(offlineOps[opIndex],
3548 getter_AddRefs(fakeDestHdr));
3549 if (fakeDestHdr) messages.AppendElement(fakeDestHdr);
3550 break;
3551 }
3552 }
3553 }
3554 }
3555 }
3556 // 3rd parameter: Set offline flag.
3557 destImapFolder->SetPendingAttributes(messages, isMove, true);
3558 }
3559 }
3560 // if we can't get the dst folder db, we should still try to playback
3561 // the offline move/copy.
3562 }
3563
3564 nsCOMPtr<nsIImapService> imapService =
3565 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
3566 NS_ENSURE_SUCCESS(rv, rv);
3567 nsCOMPtr<nsIURI> resultUrl;
3568 nsAutoCString uids;
3569 AllocateUidStringFromKeys(aMsgKeys, uids);
3570 rv = imapService->OnlineMessageCopy(this, uids, aDstFolder, true, isMove,
3571 aUrlListener, getter_AddRefs(resultUrl),
3572 nullptr, aWindow);
3573 if (resultUrl) {
3574 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(resultUrl, &rv);
3575 NS_ENSURE_SUCCESS(rv, rv);
3576 nsCOMPtr<nsIUrlListener> folderListener = do_QueryInterface(aDstFolder);
3577 if (folderListener) mailnewsUrl->RegisterListener(folderListener);
3578 }
3579 return rv;
3580 }
3581
AddMoveResultPseudoKey(nsMsgKey aMsgKey)3582 NS_IMETHODIMP nsImapMailFolder::AddMoveResultPseudoKey(nsMsgKey aMsgKey) {
3583 nsresult rv = GetDatabase();
3584 NS_ENSURE_SUCCESS(rv, rv);
3585
3586 nsCOMPtr<nsIMsgDBHdr> pseudoHdr;
3587 rv = mDatabase->GetMsgHdrForKey(aMsgKey, getter_AddRefs(pseudoHdr));
3588 NS_ENSURE_SUCCESS(rv, rv);
3589 nsCString messageId;
3590 pseudoHdr->GetMessageId(getter_Copies(messageId));
3591 // err on the side of caution and ignore messages w/o messageid.
3592 if (messageId.IsEmpty()) return NS_OK;
3593 m_pseudoHdrs.InsertOrUpdate(messageId, aMsgKey);
3594 return NS_OK;
3595 }
3596
StoreImapFlags(int32_t flags,bool addFlags,const nsTArray<nsMsgKey> & keys,nsIUrlListener * aUrlListener)3597 NS_IMETHODIMP nsImapMailFolder::StoreImapFlags(int32_t flags, bool addFlags,
3598 const nsTArray<nsMsgKey>& keys,
3599 nsIUrlListener* aUrlListener) {
3600 nsresult rv;
3601 if (!WeAreOffline()) {
3602 nsCOMPtr<nsIImapService> imapService =
3603 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
3604 NS_ENSURE_SUCCESS(rv, rv);
3605 nsAutoCString msgIds;
3606 AllocateUidStringFromKeys(keys, msgIds);
3607 if (addFlags)
3608 imapService->AddMessageFlags(this, aUrlListener ? aUrlListener : this,
3609 nullptr, msgIds, flags, true);
3610 else
3611 imapService->SubtractMessageFlags(this,
3612 aUrlListener ? aUrlListener : this,
3613 nullptr, msgIds, flags, true);
3614 } else {
3615 rv = GetDatabase();
3616 if (NS_SUCCEEDED(rv) && mDatabase) {
3617 for (auto key : keys) {
3618 nsCOMPtr<nsIMsgOfflineImapOperation> op;
3619 rv = mDatabase->GetOfflineOpForKey(key, true, getter_AddRefs(op));
3620 SetFlag(nsMsgFolderFlags::OfflineEvents);
3621 if (NS_SUCCEEDED(rv) && op) {
3622 imapMessageFlagsType newFlags;
3623 op->GetNewFlags(&newFlags);
3624 op->SetFlagOperation(addFlags ? newFlags | flags : newFlags & ~flags);
3625 }
3626 }
3627 mDatabase->Commit(
3628 nsMsgDBCommitType::kLargeCommit); // flush offline flags
3629 }
3630 }
3631 return rv;
3632 }
3633
LiteSelect(nsIUrlListener * aUrlListener,nsIMsgWindow * aMsgWindow)3634 NS_IMETHODIMP nsImapMailFolder::LiteSelect(nsIUrlListener* aUrlListener,
3635 nsIMsgWindow* aMsgWindow) {
3636 nsresult rv;
3637 nsCOMPtr<nsIImapService> imapService =
3638 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
3639 NS_ENSURE_SUCCESS(rv, rv);
3640 return imapService->LiteSelectFolder(this, aUrlListener, aMsgWindow, nullptr);
3641 }
3642
GetFolderOwnerUserName(nsACString & userName)3643 nsresult nsImapMailFolder::GetFolderOwnerUserName(nsACString& userName) {
3644 if ((mFlags & nsMsgFolderFlags::ImapPersonal) ||
3645 !(mFlags &
3646 (nsMsgFolderFlags::ImapPublic | nsMsgFolderFlags::ImapOtherUser))) {
3647 // this is one of our personal mail folders
3648 // return our username on this host
3649 nsCOMPtr<nsIMsgIncomingServer> server;
3650 nsresult rv = GetServer(getter_AddRefs(server));
3651 return NS_FAILED(rv) ? rv : server->GetUsername(userName);
3652 }
3653
3654 // the only other type of owner is if it's in the other users' namespace
3655 if (!(mFlags & nsMsgFolderFlags::ImapOtherUser)) return NS_OK;
3656
3657 if (m_ownerUserName.IsEmpty()) {
3658 nsCString onlineName;
3659 GetOnlineName(onlineName);
3660 m_ownerUserName = nsImapNamespaceList::GetFolderOwnerNameFromPath(
3661 GetNamespaceForFolder(), onlineName.get());
3662 }
3663 userName = m_ownerUserName;
3664 return NS_OK;
3665 }
3666
GetNamespaceForFolder()3667 nsImapNamespace* nsImapMailFolder::GetNamespaceForFolder() {
3668 if (!m_namespace) {
3669 #ifdef DEBUG_bienvenu
3670 // Make sure this isn't causing us to open the database
3671 NS_ASSERTION(m_hierarchyDelimiter != kOnlineHierarchySeparatorUnknown,
3672 "haven't set hierarchy delimiter");
3673 #endif
3674 nsCString serverKey;
3675 nsCString onlineName;
3676 GetServerKey(serverKey);
3677 GetOnlineName(onlineName);
3678 char hierarchyDelimiter;
3679 GetHierarchyDelimiter(&hierarchyDelimiter);
3680
3681 m_namespace = nsImapNamespaceList::GetNamespaceForFolder(
3682 serverKey.get(), onlineName.get(), hierarchyDelimiter);
3683 NS_ASSERTION(m_namespace, "didn't get namespace for folder");
3684 if (m_namespace) {
3685 nsImapNamespaceList::SuggestHierarchySeparatorForNamespace(
3686 m_namespace, hierarchyDelimiter);
3687 m_folderIsNamespace = nsImapNamespaceList::GetFolderIsNamespace(
3688 serverKey.get(), onlineName.get(), hierarchyDelimiter, m_namespace);
3689 }
3690 }
3691 return m_namespace;
3692 }
3693
SetNamespaceForFolder(nsImapNamespace * ns)3694 void nsImapMailFolder::SetNamespaceForFolder(nsImapNamespace* ns) {
3695 #ifdef DEBUG_bienvenu
3696 NS_ASSERTION(ns, "null namespace");
3697 #endif
3698 m_namespace = ns;
3699 }
3700
FolderPrivileges(nsIMsgWindow * window)3701 NS_IMETHODIMP nsImapMailFolder::FolderPrivileges(nsIMsgWindow* window) {
3702 NS_ENSURE_ARG_POINTER(window);
3703 nsresult rv = NS_OK; // if no window...
3704 if (!m_adminUrl.IsEmpty()) {
3705 nsCOMPtr<nsIExternalProtocolService> extProtService =
3706 do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
3707 if (extProtService) {
3708 nsAutoCString scheme;
3709 nsCOMPtr<nsIURI> uri;
3710 if (NS_FAILED(rv = NS_NewURI(getter_AddRefs(uri), m_adminUrl.get())))
3711 return rv;
3712 uri->GetScheme(scheme);
3713 if (!scheme.IsEmpty()) {
3714 // if the URL scheme does not correspond to an exposed protocol, then we
3715 // need to hand this link click over to the external protocol handler.
3716 bool isExposed;
3717 rv = extProtService->IsExposedProtocol(scheme.get(), &isExposed);
3718 if (NS_SUCCEEDED(rv) && !isExposed)
3719 return extProtService->LoadURI(uri, nullptr, nullptr, nullptr, false);
3720 }
3721 }
3722 } else {
3723 nsCOMPtr<nsIImapService> imapService =
3724 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
3725 NS_ENSURE_SUCCESS(rv, rv);
3726 rv = imapService->GetFolderAdminUrl(this, window, this, nullptr);
3727 if (NS_SUCCEEDED(rv)) m_urlRunning = true;
3728 }
3729 return rv;
3730 }
3731
GetHasAdminUrl(bool * aBool)3732 NS_IMETHODIMP nsImapMailFolder::GetHasAdminUrl(bool* aBool) {
3733 NS_ENSURE_ARG_POINTER(aBool);
3734 nsCOMPtr<nsIImapIncomingServer> imapServer;
3735 nsresult rv = GetImapIncomingServer(getter_AddRefs(imapServer));
3736 nsCString manageMailAccountUrl;
3737 if (NS_SUCCEEDED(rv) && imapServer)
3738 rv = imapServer->GetManageMailAccountUrl(manageMailAccountUrl);
3739 *aBool = (NS_SUCCEEDED(rv) && !manageMailAccountUrl.IsEmpty());
3740 return rv;
3741 }
3742
GetAdminUrl(nsACString & aResult)3743 NS_IMETHODIMP nsImapMailFolder::GetAdminUrl(nsACString& aResult) {
3744 aResult = m_adminUrl;
3745 return NS_OK;
3746 }
3747
SetAdminUrl(const nsACString & adminUrl)3748 NS_IMETHODIMP nsImapMailFolder::SetAdminUrl(const nsACString& adminUrl) {
3749 m_adminUrl = adminUrl;
3750 return NS_OK;
3751 }
3752
GetHdrParser(nsIMsgParseMailMsgState ** aHdrParser)3753 NS_IMETHODIMP nsImapMailFolder::GetHdrParser(
3754 nsIMsgParseMailMsgState** aHdrParser) {
3755 NS_ENSURE_ARG_POINTER(aHdrParser);
3756 NS_IF_ADDREF(*aHdrParser = m_msgParser);
3757 return NS_OK;
3758 }
3759
3760 // this is used to issue an arbitrary imap command on the passed in msgs.
3761 // It assumes the command needs to be run in the selected state.
IssueCommandOnMsgs(const nsACString & command,const char * uids,nsIMsgWindow * aWindow,nsIURI ** url)3762 NS_IMETHODIMP nsImapMailFolder::IssueCommandOnMsgs(const nsACString& command,
3763 const char* uids,
3764 nsIMsgWindow* aWindow,
3765 nsIURI** url) {
3766 nsresult rv;
3767 nsCOMPtr<nsIImapService> imapService =
3768 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
3769 NS_ENSURE_SUCCESS(rv, rv);
3770 return imapService->IssueCommandOnMsgs(this, aWindow, command,
3771 nsDependentCString(uids), url);
3772 }
3773
FetchCustomMsgAttribute(const nsACString & attribute,const char * uids,nsIMsgWindow * aWindow,nsIURI ** url)3774 NS_IMETHODIMP nsImapMailFolder::FetchCustomMsgAttribute(
3775 const nsACString& attribute, const char* uids, nsIMsgWindow* aWindow,
3776 nsIURI** url) {
3777 nsresult rv;
3778 nsCOMPtr<nsIImapService> imapService =
3779 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
3780 NS_ENSURE_SUCCESS(rv, rv);
3781
3782 return imapService->FetchCustomMsgAttribute(this, aWindow, attribute,
3783 nsDependentCString(uids), url);
3784 }
3785
MoveIncorporatedMessage(nsIMsgDBHdr * mailHdr,nsIMsgDatabase * sourceDB,const nsACString & destFolderUri,nsIMsgFilter * filter,nsIMsgWindow * msgWindow)3786 nsresult nsImapMailFolder::MoveIncorporatedMessage(
3787 nsIMsgDBHdr* mailHdr, nsIMsgDatabase* sourceDB,
3788 const nsACString& destFolderUri, nsIMsgFilter* filter,
3789 nsIMsgWindow* msgWindow) {
3790 nsresult rv;
3791 if (m_moveCoalescer) {
3792 nsCOMPtr<nsIMsgFolder> destIFolder;
3793 rv = GetOrCreateFolder(destFolderUri, getter_AddRefs(destIFolder));
3794 NS_ENSURE_SUCCESS(rv, rv);
3795
3796 if (destIFolder) {
3797 // check if the destination is a real folder (by checking for null parent)
3798 // and if it can file messages (e.g., servers or news folders can't file
3799 // messages). Or read only imap folders...
3800 bool canFileMessages = true;
3801 nsCOMPtr<nsIMsgFolder> parentFolder;
3802 destIFolder->GetParent(getter_AddRefs(parentFolder));
3803 if (parentFolder) destIFolder->GetCanFileMessages(&canFileMessages);
3804 if (filter && (!parentFolder || !canFileMessages)) {
3805 filter->SetEnabled(false);
3806 m_filterList->SaveToDefaultFile();
3807 destIFolder->ThrowAlertMsg("filterDisabled", msgWindow);
3808 return NS_MSG_NOT_A_MAIL_FOLDER;
3809 }
3810 // put the header into the source db, since it needs to be there when we
3811 // copy it and we need a valid header to pass to
3812 // StartAsyncCopyMessagesInto
3813 nsMsgKey keyToFilter;
3814 mailHdr->GetMessageKey(&keyToFilter);
3815
3816 if (sourceDB && destIFolder) {
3817 bool imapDeleteIsMoveToTrash = DeleteIsMoveToTrash();
3818 m_moveCoalescer->AddMove(destIFolder, keyToFilter);
3819 // For each folder, we need to keep track of the ids we want to move to
3820 // that folder - we used to store them in the MSG_FolderInfo and then
3821 // when we'd finished downloading headers, we'd iterate through all the
3822 // folders looking for the ones that needed messages moved into them -
3823 // perhaps instead we could keep track of nsIMsgFolder,
3824 // nsTArray<nsMsgKey> pairs here in the imap code. nsTArray<nsMsgKey>
3825 // *idsToMoveFromInbox = msgFolder->GetImapIdsToMoveFromInbox();
3826 // idsToMoveFromInbox->AppendElement(keyToFilter);
3827 if (imapDeleteIsMoveToTrash) {
3828 }
3829 bool isRead = false;
3830 mailHdr->GetIsRead(&isRead);
3831 if (imapDeleteIsMoveToTrash) rv = NS_OK;
3832 }
3833 }
3834 } else
3835 rv = NS_ERROR_UNEXPECTED;
3836
3837 // we have to return an error because we do not actually move the message
3838 // it is done async and that can fail
3839 return rv;
3840 }
3841
3842 /**
3843 * This method assumes that key arrays and flag states are sorted by increasing
3844 * key.
3845 */
FindKeysToDelete(const nsTArray<nsMsgKey> & existingKeys,nsTArray<nsMsgKey> & keysToDelete,nsIImapFlagAndUidState * flagState,uint32_t boxFlags)3846 void nsImapMailFolder::FindKeysToDelete(const nsTArray<nsMsgKey>& existingKeys,
3847 nsTArray<nsMsgKey>& keysToDelete,
3848 nsIImapFlagAndUidState* flagState,
3849 uint32_t boxFlags) {
3850 bool showDeletedMessages = ShowDeletedMessages();
3851 int32_t numMessageInFlagState;
3852 bool partialUIDFetch;
3853 uint32_t uidOfMessage;
3854 imapMessageFlagsType flags;
3855
3856 flagState->GetNumberOfMessages(&numMessageInFlagState);
3857 flagState->GetPartialUIDFetch(&partialUIDFetch);
3858
3859 // if we're doing a partialUIDFetch, just delete the keys from the db
3860 // that have the deleted flag set (if not using imap delete model)
3861 // and return.
3862 if (partialUIDFetch) {
3863 if (!showDeletedMessages) {
3864 for (uint32_t i = 0; (int32_t)i < numMessageInFlagState; i++) {
3865 flagState->GetUidOfMessage(i, &uidOfMessage);
3866 // flag state will be zero filled up to first real uid, so ignore those.
3867 if (uidOfMessage) {
3868 flagState->GetMessageFlags(i, &flags);
3869 if (flags & kImapMsgDeletedFlag)
3870 keysToDelete.AppendElement(uidOfMessage);
3871 }
3872 }
3873 } else if (boxFlags & kJustExpunged) {
3874 // we've just issued an expunge with a partial flag state. We should
3875 // delete headers with the imap deleted flag set, because we can't
3876 // tell from the expunge response which messages were deleted.
3877 nsCOMPtr<nsIMsgEnumerator> hdrs;
3878 nsresult rv = GetMessages(getter_AddRefs(hdrs));
3879 NS_ENSURE_SUCCESS_VOID(rv);
3880 bool hasMore = false;
3881 while (NS_SUCCEEDED(rv = hdrs->HasMoreElements(&hasMore)) && hasMore) {
3882 nsCOMPtr<nsIMsgDBHdr> header;
3883 rv = hdrs->GetNext(getter_AddRefs(header));
3884 NS_ENSURE_SUCCESS_VOID(rv);
3885 uint32_t msgFlags;
3886 header->GetFlags(&msgFlags);
3887 if (msgFlags & nsMsgMessageFlags::IMAPDeleted) {
3888 nsMsgKey msgKey;
3889 header->GetMessageKey(&msgKey);
3890 keysToDelete.AppendElement(msgKey);
3891 }
3892 }
3893 }
3894 return;
3895 }
3896 // otherwise, we have a complete set of uid's and flags, so we delete
3897 // anything that's in existingKeys but not in the flag state, as well
3898 // as messages with the deleted flag set.
3899 uint32_t total = existingKeys.Length();
3900 int onlineIndex = 0; // current index into flagState
3901 for (uint32_t keyIndex = 0; keyIndex < total; keyIndex++) {
3902 while (
3903 (onlineIndex < numMessageInFlagState) &&
3904 NS_SUCCEEDED(flagState->GetUidOfMessage(onlineIndex, &uidOfMessage)) &&
3905 (existingKeys[keyIndex] > uidOfMessage))
3906 onlineIndex++;
3907
3908 flagState->GetUidOfMessage(onlineIndex, &uidOfMessage);
3909 flagState->GetMessageFlags(onlineIndex, &flags);
3910 // delete this key if it is not there or marked deleted
3911 if ((onlineIndex >= numMessageInFlagState) ||
3912 (existingKeys[keyIndex] != uidOfMessage) ||
3913 ((flags & kImapMsgDeletedFlag) && !showDeletedMessages)) {
3914 nsMsgKey doomedKey = existingKeys[keyIndex];
3915 if ((int32_t)doomedKey <= 0 && doomedKey != nsMsgKey_None) continue;
3916
3917 keysToDelete.AppendElement(existingKeys[keyIndex]);
3918 }
3919
3920 flagState->GetUidOfMessage(onlineIndex, &uidOfMessage);
3921 if (existingKeys[keyIndex] == uidOfMessage) onlineIndex++;
3922 }
3923 }
3924
FindKeysToAdd(const nsTArray<nsMsgKey> & existingKeys,nsTArray<nsMsgKey> & keysToFetch,uint32_t & numNewUnread,nsIImapFlagAndUidState * flagState)3925 void nsImapMailFolder::FindKeysToAdd(const nsTArray<nsMsgKey>& existingKeys,
3926 nsTArray<nsMsgKey>& keysToFetch,
3927 uint32_t& numNewUnread,
3928 nsIImapFlagAndUidState* flagState) {
3929 bool showDeletedMessages = ShowDeletedMessages();
3930 int dbIndex = 0; // current index into existingKeys
3931 int32_t existTotal, numberOfKnownKeys;
3932 int32_t messageIndex;
3933
3934 numNewUnread = 0;
3935 existTotal = numberOfKnownKeys = existingKeys.Length();
3936 flagState->GetNumberOfMessages(&messageIndex);
3937 bool partialUIDFetch;
3938 flagState->GetPartialUIDFetch(&partialUIDFetch);
3939
3940 for (int32_t flagIndex = 0; flagIndex < messageIndex; flagIndex++) {
3941 uint32_t uidOfMessage;
3942 flagState->GetUidOfMessage(flagIndex, &uidOfMessage);
3943 while ((flagIndex < numberOfKnownKeys) && (dbIndex < existTotal) &&
3944 existingKeys[dbIndex] < uidOfMessage)
3945 dbIndex++;
3946
3947 if ((flagIndex >= numberOfKnownKeys) || (dbIndex >= existTotal) ||
3948 (existingKeys[dbIndex] != uidOfMessage)) {
3949 numberOfKnownKeys++;
3950
3951 imapMessageFlagsType flags;
3952 flagState->GetMessageFlags(flagIndex, &flags);
3953 NS_ASSERTION(uidOfMessage != nsMsgKey_None, "got invalid msg key");
3954 if (uidOfMessage && uidOfMessage != nsMsgKey_None &&
3955 (showDeletedMessages || !(flags & kImapMsgDeletedFlag))) {
3956 if (mDatabase) {
3957 bool dbContainsKey;
3958 if (NS_SUCCEEDED(
3959 mDatabase->ContainsKey(uidOfMessage, &dbContainsKey)) &&
3960 dbContainsKey) {
3961 // this is expected in the partial uid fetch case because the
3962 // flag state does not contain all messages, so the db has
3963 // messages the flag state doesn't know about.
3964 if (!partialUIDFetch) NS_ERROR("db has key - flagState messed up?");
3965 continue;
3966 }
3967 }
3968 keysToFetch.AppendElement(uidOfMessage);
3969 if (!(flags & kImapMsgSeenFlag)) numNewUnread++;
3970 }
3971 }
3972 }
3973 }
3974
GetMsgHdrsToDownload(bool * aMoreToDownload,int32_t * aTotalCount,nsTArray<nsMsgKey> & aKeys)3975 NS_IMETHODIMP nsImapMailFolder::GetMsgHdrsToDownload(
3976 bool* aMoreToDownload, int32_t* aTotalCount, nsTArray<nsMsgKey>& aKeys) {
3977 NS_ENSURE_ARG_POINTER(aMoreToDownload);
3978 NS_ENSURE_ARG_POINTER(aTotalCount);
3979 aKeys.Clear();
3980
3981 *aMoreToDownload = false;
3982 *aTotalCount = m_totalKeysToFetch;
3983 if (m_keysToFetch.IsEmpty()) {
3984 return NS_OK;
3985 }
3986
3987 // if folder isn't open in a window, no reason to limit the number of headers
3988 // we download.
3989 nsCOMPtr<nsIMsgMailSession> session =
3990 do_GetService(NS_MSGMAILSESSION_CONTRACTID);
3991 bool folderOpen = false;
3992 if (session) session->IsFolderOpenInWindow(this, &folderOpen);
3993
3994 int32_t hdrChunkSize = 200;
3995 if (folderOpen) {
3996 nsresult rv;
3997 nsCOMPtr<nsIPrefBranch> prefBranch(
3998 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
3999 NS_ENSURE_SUCCESS(rv, rv);
4000 if (prefBranch)
4001 prefBranch->GetIntPref("mail.imap.hdr_chunk_size", &hdrChunkSize);
4002 }
4003 int32_t numKeysToFetch = m_keysToFetch.Length();
4004 int32_t startIndex = 0;
4005 if (folderOpen && hdrChunkSize > 0 &&
4006 (int32_t)m_keysToFetch.Length() > hdrChunkSize) {
4007 numKeysToFetch = hdrChunkSize;
4008 *aMoreToDownload = true;
4009 startIndex = m_keysToFetch.Length() - hdrChunkSize;
4010 }
4011 aKeys.AppendElements(&m_keysToFetch[startIndex], numKeysToFetch);
4012 // Remove these for the incremental header download case, so that
4013 // we know we don't have to download them again.
4014 m_keysToFetch.RemoveElementsAt(startIndex, numKeysToFetch);
4015
4016 return NS_OK;
4017 }
4018
PrepareToAddHeadersToMailDB(nsIImapProtocol * aProtocol)4019 void nsImapMailFolder::PrepareToAddHeadersToMailDB(nsIImapProtocol* aProtocol) {
4020 // now, tell it we don't need any bodies.
4021 nsTArray<nsMsgKey> noBodies;
4022 aProtocol->NotifyBodysToDownload(noBodies);
4023 }
4024
TweakHeaderFlags(nsIImapProtocol * aProtocol,nsIMsgDBHdr * tweakMe)4025 void nsImapMailFolder::TweakHeaderFlags(nsIImapProtocol* aProtocol,
4026 nsIMsgDBHdr* tweakMe) {
4027 if (mDatabase && aProtocol && tweakMe) {
4028 tweakMe->SetMessageKey(m_curMsgUid);
4029 tweakMe->SetMessageSize(m_nextMessageByteLength);
4030
4031 bool foundIt = false;
4032 imapMessageFlagsType imap_flags;
4033
4034 nsCString customFlags;
4035 nsresult rv = aProtocol->GetFlagsForUID(m_curMsgUid, &foundIt, &imap_flags,
4036 getter_Copies(customFlags));
4037 if (NS_SUCCEEDED(rv) && foundIt) {
4038 // make a mask and clear these message flags
4039 uint32_t mask = nsMsgMessageFlags::Read | nsMsgMessageFlags::Replied |
4040 nsMsgMessageFlags::Marked |
4041 nsMsgMessageFlags::IMAPDeleted |
4042 nsMsgMessageFlags::Labels;
4043 uint32_t dbHdrFlags;
4044
4045 tweakMe->GetFlags(&dbHdrFlags);
4046 tweakMe->AndFlags(~mask, &dbHdrFlags);
4047
4048 // set the new value for these flags
4049 uint32_t newFlags = 0;
4050 if (imap_flags & kImapMsgSeenFlag)
4051 newFlags |= nsMsgMessageFlags::Read;
4052 else // if (imap_flags & kImapMsgRecentFlag)
4053 newFlags |= nsMsgMessageFlags::New;
4054
4055 // Okay here is the MDN needed logic (if DNT header seen):
4056 /* if server support user defined flag:
4057 XXX TODO: Fix badly formatted comment which doesn't reflect the code.
4058 MDNSent flag set => clear kMDNNeeded flag
4059 MDNSent flag not set => do nothing, leave kMDNNeeded on
4060 else if
4061 not nsMsgMessageFlags::New => clear kMDNNeeded flag
4062 nsMsgMessageFlags::New => do nothing, leave kMDNNeeded on
4063 */
4064 uint16_t userFlags;
4065 rv = aProtocol->GetSupportedUserFlags(&userFlags);
4066 if (NS_SUCCEEDED(rv) && (userFlags & (kImapMsgSupportUserFlag |
4067 kImapMsgSupportMDNSentFlag))) {
4068 if (imap_flags & kImapMsgMDNSentFlag) {
4069 newFlags |= nsMsgMessageFlags::MDNReportSent;
4070 if (dbHdrFlags & nsMsgMessageFlags::MDNReportNeeded)
4071 tweakMe->AndFlags(~nsMsgMessageFlags::MDNReportNeeded, &dbHdrFlags);
4072 }
4073 }
4074
4075 if (imap_flags & kImapMsgAnsweredFlag)
4076 newFlags |= nsMsgMessageFlags::Replied;
4077 if (imap_flags & kImapMsgFlaggedFlag)
4078 newFlags |= nsMsgMessageFlags::Marked;
4079 if (imap_flags & kImapMsgDeletedFlag)
4080 newFlags |= nsMsgMessageFlags::IMAPDeleted;
4081 if (imap_flags & kImapMsgForwardedFlag)
4082 newFlags |= nsMsgMessageFlags::Forwarded;
4083
4084 // db label flags are 0x0E000000 and imap label flags are 0x0E00
4085 // so we need to shift 16 bits to the left to convert them.
4086 if (imap_flags & kImapMsgLabelFlags) {
4087 // we need to set label attribute on header because the dbview code
4088 // does msgHdr->GetLabel when asked to paint a row
4089 tweakMe->SetLabel((imap_flags & kImapMsgLabelFlags) >> 9);
4090 newFlags |= (imap_flags & kImapMsgLabelFlags) << 16;
4091 }
4092 if (newFlags) tweakMe->OrFlags(newFlags, &dbHdrFlags);
4093 if (!customFlags.IsEmpty())
4094 (void)HandleCustomFlags(m_curMsgUid, tweakMe, userFlags, customFlags);
4095 }
4096 }
4097 }
4098
4099 NS_IMETHODIMP
SetupMsgWriteStream(nsIFile * aFile,bool addDummyEnvelope)4100 nsImapMailFolder::SetupMsgWriteStream(nsIFile* aFile, bool addDummyEnvelope) {
4101 nsresult rv;
4102 aFile->Remove(false);
4103 rv = MsgNewBufferedFileOutputStream(
4104 getter_AddRefs(m_tempMessageStream), aFile,
4105 PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 00700);
4106 if (m_tempMessageStream && addDummyEnvelope) {
4107 nsAutoCString result;
4108 char* ct;
4109 uint32_t writeCount;
4110 time_t now = time((time_t*)0);
4111 ct = ctime(&now);
4112 ct[24] = 0;
4113 result = "From - ";
4114 result += ct;
4115 result += MSG_LINEBREAK;
4116
4117 m_tempMessageStream->Write(result.get(), result.Length(), &writeCount);
4118 result = "X-Mozilla-Status: 0001";
4119 result += MSG_LINEBREAK;
4120 m_tempMessageStream->Write(result.get(), result.Length(), &writeCount);
4121 result = "X-Mozilla-Status2: 00000000";
4122 result += MSG_LINEBREAK;
4123 m_tempMessageStream->Write(result.get(), result.Length(), &writeCount);
4124 }
4125 return rv;
4126 }
4127
DownloadMessagesForOffline(nsTArray<RefPtr<nsIMsgDBHdr>> const & messages,nsIMsgWindow * window)4128 NS_IMETHODIMP nsImapMailFolder::DownloadMessagesForOffline(
4129 nsTArray<RefPtr<nsIMsgDBHdr>> const& messages, nsIMsgWindow* window) {
4130 nsAutoCString messageIds;
4131 nsTArray<nsMsgKey> srcKeyArray;
4132 nsresult rv = BuildIdsAndKeyArray(messages, messageIds, srcKeyArray);
4133 if (NS_FAILED(rv) || messageIds.IsEmpty()) return rv;
4134
4135 nsCOMPtr<nsIImapService> imapService =
4136 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
4137 NS_ENSURE_SUCCESS(rv, rv);
4138
4139 rv = AcquireSemaphore(static_cast<nsIMsgFolder*>(this));
4140 if (NS_FAILED(rv)) {
4141 ThrowAlertMsg("operationFailedFolderBusy", window);
4142 return rv;
4143 }
4144 return imapService->DownloadMessagesForOffline(messageIds, this, this,
4145 window);
4146 }
4147
DownloadAllForOffline(nsIUrlListener * listener,nsIMsgWindow * msgWindow)4148 NS_IMETHODIMP nsImapMailFolder::DownloadAllForOffline(nsIUrlListener* listener,
4149 nsIMsgWindow* msgWindow) {
4150 nsresult rv;
4151 nsCOMPtr<nsIURI> runningURI;
4152 bool noSelect;
4153 GetFlag(nsMsgFolderFlags::ImapNoselect, &noSelect);
4154
4155 if (!noSelect) {
4156 nsAutoCString messageIdsToDownload;
4157 nsTArray<nsMsgKey> msgsToDownload;
4158
4159 GetDatabase();
4160 m_downloadingFolderForOfflineUse = true;
4161
4162 rv = AcquireSemaphore(static_cast<nsIMsgFolder*>(this));
4163 if (NS_FAILED(rv)) {
4164 m_downloadingFolderForOfflineUse = false;
4165 ThrowAlertMsg("operationFailedFolderBusy", msgWindow);
4166 return rv;
4167 }
4168 nsCOMPtr<nsIImapService> imapService =
4169 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
4170 NS_ENSURE_SUCCESS(rv, rv);
4171
4172 // Selecting the folder with nsIImapUrl::shouldStoreMsgOffline true will
4173 // cause us to fetch any message bodies we don't have.
4174 m_urlListener = listener;
4175 rv = imapService->SelectFolder(this, this, msgWindow,
4176 getter_AddRefs(runningURI));
4177 if (NS_SUCCEEDED(rv)) {
4178 nsCOMPtr<nsIImapUrl> imapUrl(do_QueryInterface(runningURI));
4179 if (imapUrl) imapUrl->SetStoreResultsOffline(true);
4180 m_urlRunning = true;
4181 }
4182 } else
4183 rv = NS_MSG_FOLDER_UNREADABLE;
4184 return rv;
4185 }
4186
4187 NS_IMETHODIMP
ParseAdoptedMsgLine(const char * adoptedMessageLine,nsMsgKey uidOfMessage,nsIImapUrl * aImapUrl)4188 nsImapMailFolder::ParseAdoptedMsgLine(const char* adoptedMessageLine,
4189 nsMsgKey uidOfMessage,
4190 nsIImapUrl* aImapUrl) {
4191 NS_ENSURE_ARG_POINTER(aImapUrl);
4192 uint32_t count = 0;
4193 nsresult rv;
4194 // remember the uid of the message we're downloading.
4195 m_curMsgUid = uidOfMessage;
4196 if (!m_offlineHeader) {
4197 rv = GetMessageHeader(uidOfMessage, getter_AddRefs(m_offlineHeader));
4198 if (NS_SUCCEEDED(rv) && !m_offlineHeader) rv = NS_ERROR_UNEXPECTED;
4199 NS_ENSURE_SUCCESS(rv, rv);
4200 rv = StartNewOfflineMessage();
4201 NS_ENSURE_SUCCESS(rv, rv);
4202 }
4203 // adoptedMessageLine is actually a string with a lot of message lines,
4204 // separated by native line terminators we need to count the number of
4205 // MSG_LINEBREAK's to determine how much to increment m_numOfflineMsgLines by.
4206 const char* nextLine = adoptedMessageLine;
4207 do {
4208 m_numOfflineMsgLines++;
4209 nextLine = PL_strstr(nextLine, MSG_LINEBREAK);
4210 if (nextLine) nextLine += MSG_LINEBREAK_LEN;
4211 } while (nextLine && *nextLine);
4212
4213 if (m_tempMessageStream) {
4214 nsCOMPtr<nsISeekableStream> seekable(
4215 do_QueryInterface(m_tempMessageStream));
4216 if (seekable) seekable->Seek(PR_SEEK_END, 0);
4217 rv = m_tempMessageStream->Write(adoptedMessageLine,
4218 PL_strlen(adoptedMessageLine), &count);
4219 NS_ENSURE_SUCCESS(rv, rv);
4220 }
4221 return NS_OK;
4222 }
4223
EndOfflineDownload()4224 void nsImapMailFolder::EndOfflineDownload() {
4225 if (m_tempMessageStream) {
4226 m_tempMessageStream->Close();
4227 m_tempMessageStream = nullptr;
4228 ReleaseSemaphore(static_cast<nsIMsgFolder*>(this));
4229 if (mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
4230 }
4231 m_offlineHeader = nullptr;
4232 }
4233
4234 NS_IMETHODIMP
NormalEndMsgWriteStream(nsMsgKey uidOfMessage,bool markRead,nsIImapUrl * imapUrl,int32_t updatedMessageSize)4235 nsImapMailFolder::NormalEndMsgWriteStream(nsMsgKey uidOfMessage, bool markRead,
4236 nsIImapUrl* imapUrl,
4237 int32_t updatedMessageSize) {
4238 if (updatedMessageSize != -1) {
4239 // retrieve the message header to update size, if we don't already have it
4240 nsCOMPtr<nsIMsgDBHdr> msgHeader = m_offlineHeader;
4241 if (!msgHeader) GetMessageHeader(uidOfMessage, getter_AddRefs(msgHeader));
4242 if (msgHeader) {
4243 uint32_t msgSize;
4244 msgHeader->GetMessageSize(&msgSize);
4245 MOZ_LOG(IMAP, mozilla::LogLevel::Debug,
4246 ("Updating stored message size from %u, new size %d", msgSize,
4247 updatedMessageSize));
4248 msgHeader->SetMessageSize(updatedMessageSize);
4249 // only commit here if this isn't an offline message
4250 // offline header gets committed in EndNewOfflineMessage() called below
4251 if (mDatabase && !m_offlineHeader)
4252 mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
4253 } else
4254 NS_WARNING(
4255 "Failed to get message header when trying to update message size");
4256 }
4257
4258 if (m_offlineHeader) EndNewOfflineMessage();
4259
4260 m_curMsgUid = uidOfMessage;
4261
4262 // Apply filter now if it needed a body
4263 if (m_filterListRequiresBody) {
4264 if (m_filterList) {
4265 nsCOMPtr<nsIMsgDBHdr> newMsgHdr;
4266 GetMessageHeader(uidOfMessage, getter_AddRefs(newMsgHdr));
4267 GetMoveCoalescer();
4268 nsCOMPtr<nsIMsgWindow> msgWindow;
4269 if (imapUrl) {
4270 nsresult rv;
4271 nsCOMPtr<nsIMsgMailNewsUrl> msgUrl;
4272 msgUrl = do_QueryInterface(imapUrl, &rv);
4273 if (msgUrl && NS_SUCCEEDED(rv))
4274 msgUrl->GetMsgWindow(getter_AddRefs(msgWindow));
4275 }
4276 m_filterList->ApplyFiltersToHdr(nsMsgFilterType::InboxRule, newMsgHdr,
4277 this, mDatabase, EmptyCString(), this,
4278 msgWindow);
4279 NotifyFolderEvent(kFiltersApplied);
4280 }
4281 // Process filter plugins and other items normally done at the end of
4282 // HeaderFetchCompleted.
4283 bool pendingMoves = m_moveCoalescer && m_moveCoalescer->HasPendingMoves();
4284 PlaybackCoalescedOperations();
4285
4286 bool filtersRun;
4287 CallFilterPlugins(nullptr, &filtersRun);
4288 int32_t numNewBiffMsgs = 0;
4289 if (m_performingBiff) GetNumNewMessages(false, &numNewBiffMsgs);
4290
4291 if (!filtersRun && m_performingBiff && mDatabase && numNewBiffMsgs > 0 &&
4292 (!pendingMoves || !ShowPreviewText())) {
4293 // If we are performing biff for this folder, tell the
4294 // stand-alone biff about the new high water mark
4295 // We must ensure that the server knows that we are performing biff.
4296 // Otherwise the stand-alone biff won't fire.
4297 nsCOMPtr<nsIMsgIncomingServer> server;
4298 if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server)
4299 server->SetPerformingBiff(true);
4300
4301 SetBiffState(nsIMsgFolder::nsMsgBiffState_NewMail);
4302 if (server) server->SetPerformingBiff(false);
4303 m_performingBiff = false;
4304 }
4305
4306 if (m_filterList) (void)m_filterList->FlushLogIfNecessary();
4307 }
4308
4309 return NS_OK;
4310 }
4311
4312 NS_IMETHODIMP
AbortMsgWriteStream()4313 nsImapMailFolder::AbortMsgWriteStream() {
4314 m_offlineHeader = nullptr;
4315 return NS_ERROR_FAILURE;
4316 }
4317
4318 // message move/copy related methods
4319 NS_IMETHODIMP
OnlineCopyCompleted(nsIImapProtocol * aProtocol,ImapOnlineCopyState aCopyState)4320 nsImapMailFolder::OnlineCopyCompleted(nsIImapProtocol* aProtocol,
4321 ImapOnlineCopyState aCopyState) {
4322 NS_ENSURE_ARG_POINTER(aProtocol);
4323
4324 nsresult rv;
4325 if (aCopyState == ImapOnlineCopyStateType::kSuccessfulCopy) {
4326 nsCOMPtr<nsIImapUrl> imapUrl;
4327 rv = aProtocol->GetRunningImapURL(getter_AddRefs(imapUrl));
4328 if (NS_FAILED(rv) || !imapUrl) return NS_ERROR_FAILURE;
4329 nsImapAction action;
4330 rv = imapUrl->GetImapAction(&action);
4331 if (NS_FAILED(rv)) return rv;
4332 if (action != nsIImapUrl::nsImapOnlineToOfflineMove)
4333 return NS_ERROR_FAILURE; // don't assert here...
4334 nsCString messageIds;
4335 rv = imapUrl->GetListOfMessageIds(messageIds);
4336 if (NS_FAILED(rv)) return rv;
4337 nsCOMPtr<nsIImapService> imapService =
4338 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
4339 NS_ENSURE_SUCCESS(rv, rv);
4340 return imapService->AddMessageFlags(this, nullptr, nullptr, messageIds,
4341 kImapMsgDeletedFlag, true);
4342 }
4343 /* unhandled copystate */
4344 if (m_copyState) // whoops, this is the wrong folder - should use the source
4345 // folder
4346 {
4347 nsCOMPtr<nsIMsgFolder> srcFolder;
4348 srcFolder = do_QueryInterface(m_copyState->m_srcSupport, &rv);
4349 if (srcFolder) srcFolder->NotifyFolderEvent(kDeleteOrMoveMsgCompleted);
4350 } else
4351 rv = NS_ERROR_FAILURE;
4352
4353 return rv;
4354 }
4355
4356 NS_IMETHODIMP
CloseMockChannel(nsIImapMockChannel * aChannel)4357 nsImapMailFolder::CloseMockChannel(nsIImapMockChannel* aChannel) {
4358 aChannel->Close();
4359 return NS_OK;
4360 }
4361
4362 NS_IMETHODIMP
ReleaseUrlCacheEntry(nsIMsgMailNewsUrl * aUrl)4363 nsImapMailFolder::ReleaseUrlCacheEntry(nsIMsgMailNewsUrl* aUrl) {
4364 NS_ENSURE_ARG_POINTER(aUrl);
4365 return aUrl->SetMemCacheEntry(nullptr);
4366 }
4367
4368 NS_IMETHODIMP
BeginMessageUpload()4369 nsImapMailFolder::BeginMessageUpload() { return NS_ERROR_FAILURE; }
4370
HandleCustomFlags(nsMsgKey uidOfMessage,nsIMsgDBHdr * dbHdr,uint16_t userFlags,nsCString & keywords)4371 nsresult nsImapMailFolder::HandleCustomFlags(nsMsgKey uidOfMessage,
4372 nsIMsgDBHdr* dbHdr,
4373 uint16_t userFlags,
4374 nsCString& keywords) {
4375 nsresult rv = GetDatabase();
4376 NS_ENSURE_SUCCESS(rv, rv);
4377
4378 ToLowerCase(keywords);
4379 bool messageClassified = true;
4380 // Mac Mail uses "NotJunk"
4381 if (keywords.Find("NonJunk", /* ignoreCase = */ true) != kNotFound ||
4382 keywords.Find("NotJunk", /* ignoreCase = */ true) != kNotFound) {
4383 nsAutoCString msgJunkScore;
4384 msgJunkScore.AppendInt(nsIJunkMailPlugin::IS_HAM_SCORE);
4385 mDatabase->SetStringProperty(uidOfMessage, "junkscore", msgJunkScore.get());
4386 }
4387 // ### TODO: we really should parse the keywords into space delimited keywords
4388 // before checking
4389 else if (keywords.Find("Junk", /* ignoreCase = */ true) != kNotFound) {
4390 uint32_t newFlags;
4391 dbHdr->AndFlags(~nsMsgMessageFlags::New, &newFlags);
4392 nsAutoCString msgJunkScore;
4393 msgJunkScore.AppendInt(nsIJunkMailPlugin::IS_SPAM_SCORE);
4394 mDatabase->SetStringProperty(uidOfMessage, "junkscore", msgJunkScore.get());
4395 } else
4396 messageClassified = false;
4397 if (messageClassified) {
4398 // only set the junkscore origin if it wasn't set before.
4399 nsCString existingProperty;
4400 dbHdr->GetStringProperty("junkscoreorigin",
4401 getter_Copies(existingProperty));
4402 if (existingProperty.IsEmpty())
4403 dbHdr->SetStringProperty("junkscoreorigin", "imapflag");
4404 }
4405
4406 if (!(userFlags & kImapMsgSupportUserFlag)) {
4407 nsCString localKeywords;
4408 nsCString prevKeywords;
4409 dbHdr->GetStringProperty("keywords", getter_Copies(localKeywords));
4410 dbHdr->GetStringProperty("prevkeywords", getter_Copies(prevKeywords));
4411 // localKeywords: tags currently stored in database for the message.
4412 // keywords: tags stored in server and obtained when flags for the message
4413 // were last fetched. (Parameter of this function.)
4414 // prevKeywords: saved keywords from previous call of this function.
4415 // clang-format off
4416 MOZ_LOG(IMAP_KW, mozilla::LogLevel::Debug,
4417 ("UID=%" PRIu32 ", localKeywords=|%s| keywords=|%s|, prevKeywords=|%s|",
4418 uidOfMessage, localKeywords.get(), keywords.get(), prevKeywords.get()));
4419 // clang-format on
4420
4421 // Store keywords to detect changes on next call of this function.
4422 dbHdr->SetStringProperty("prevkeywords", keywords.get());
4423
4424 // Parse the space separated strings into arrays.
4425 nsTArray<nsCString> localKeywordArray;
4426 nsTArray<nsCString> keywordArray;
4427 nsTArray<nsCString> prevKeywordArray;
4428 ParseString(localKeywords, ' ', localKeywordArray);
4429 ParseString(keywords, ' ', keywordArray);
4430 ParseString(prevKeywords, ' ', prevKeywordArray);
4431
4432 // If keyword not received now but was the last time, remove it from
4433 // the localKeywords. This means the keyword was removed by another user
4434 // sharing the folder.
4435 for (uint32_t i = 0; i < prevKeywordArray.Length(); i++) {
4436 bool inRcvd = keywordArray.Contains(prevKeywordArray[i]);
4437 bool inLocal = localKeywordArray.Contains(prevKeywordArray[i]);
4438 if (!inRcvd && inLocal)
4439 localKeywordArray.RemoveElement(prevKeywordArray[i]);
4440 }
4441
4442 // Combine local and rcvd keyword arrays into a single string
4443 // so it can be passed to SetStringProperty(). If element of
4444 // local already in rcvd, avoid duplicates in combined string.
4445 nsAutoCString combinedKeywords;
4446 for (uint32_t i = 0; i < localKeywordArray.Length(); i++) {
4447 if (!keywordArray.Contains(localKeywordArray[i])) {
4448 combinedKeywords.Append(localKeywordArray[i]);
4449 combinedKeywords.Append(' ');
4450 }
4451 }
4452 for (uint32_t i = 0; i < keywordArray.Length(); i++) {
4453 combinedKeywords.Append(keywordArray[i]);
4454 combinedKeywords.Append(' ');
4455 }
4456 MOZ_LOG(IMAP_KW, mozilla::LogLevel::Debug,
4457 ("combinedKeywords stored = |%s|", combinedKeywords.get()));
4458 // combinedKeywords are tags being stored in database for the message.
4459 return dbHdr->SetStringProperty("keywords", combinedKeywords.get());
4460 }
4461 return (userFlags & kImapMsgSupportUserFlag)
4462 ? dbHdr->SetStringProperty("keywords", keywords.get())
4463 : NS_OK;
4464 }
4465
4466 // synchronize the message flags in the database with the server flags
SyncFlags(nsIImapFlagAndUidState * flagState)4467 nsresult nsImapMailFolder::SyncFlags(nsIImapFlagAndUidState* flagState) {
4468 nsresult rv = GetDatabase(); // we need a database for this
4469 NS_ENSURE_SUCCESS(rv, rv);
4470 bool partialUIDFetch;
4471 flagState->GetPartialUIDFetch(&partialUIDFetch);
4472
4473 // update all of the database flags
4474 int32_t messageIndex;
4475 uint32_t messageSize;
4476
4477 // Take this opportunity to recalculate the folder size, if we're not a
4478 // partial (condstore) fetch.
4479 int64_t newFolderSize = 0;
4480
4481 flagState->GetNumberOfMessages(&messageIndex);
4482
4483 uint16_t supportedUserFlags;
4484 flagState->GetSupportedUserFlags(&supportedUserFlags);
4485
4486 for (int32_t flagIndex = 0; flagIndex < messageIndex; flagIndex++) {
4487 uint32_t uidOfMessage;
4488 flagState->GetUidOfMessage(flagIndex, &uidOfMessage);
4489 imapMessageFlagsType flags;
4490 flagState->GetMessageFlags(flagIndex, &flags);
4491 nsCOMPtr<nsIMsgDBHdr> dbHdr;
4492 bool containsKey;
4493 rv = mDatabase->ContainsKey(uidOfMessage, &containsKey);
4494 // if we don't have the header, don't diddle the flags.
4495 // GetMsgHdrForKey will create the header if it doesn't exist.
4496 if (NS_FAILED(rv) || !containsKey) continue;
4497
4498 rv = mDatabase->GetMsgHdrForKey(uidOfMessage, getter_AddRefs(dbHdr));
4499 if (NS_SUCCEEDED(dbHdr->GetMessageSize(&messageSize)))
4500 newFolderSize += messageSize;
4501
4502 nsCString keywords;
4503 if (NS_SUCCEEDED(
4504 flagState->GetCustomFlags(uidOfMessage, getter_Copies(keywords))))
4505 HandleCustomFlags(uidOfMessage, dbHdr, supportedUserFlags, keywords);
4506
4507 NotifyMessageFlagsFromHdr(dbHdr, uidOfMessage, flags);
4508 }
4509 if (!partialUIDFetch && newFolderSize != mFolderSize) {
4510 int64_t oldFolderSize = mFolderSize;
4511 mFolderSize = newFolderSize;
4512 NotifyIntPropertyChanged(kFolderSize, oldFolderSize, mFolderSize);
4513 }
4514
4515 return NS_OK;
4516 }
4517
4518 // helper routine to sync the flags on a given header
NotifyMessageFlagsFromHdr(nsIMsgDBHdr * dbHdr,nsMsgKey msgKey,uint32_t flags)4519 nsresult nsImapMailFolder::NotifyMessageFlagsFromHdr(nsIMsgDBHdr* dbHdr,
4520 nsMsgKey msgKey,
4521 uint32_t flags) {
4522 nsresult rv = GetDatabase();
4523 NS_ENSURE_SUCCESS(rv, rv);
4524
4525 // Although it may seem strange to keep a local reference of mDatabase here,
4526 // the current lifetime management of databases requires that methods
4527 // sometimes null the database when they think they opened it. Unfortunately
4528 // experience shows this happens when we don't expect, so for crash protection
4529 // best practice with the current flawed database management is to keep a
4530 // local reference when there will be complex calls in a method. See bug
4531 // 1312254.
4532 nsCOMPtr<nsIMsgDatabase> database(mDatabase);
4533 NS_ENSURE_STATE(database);
4534
4535 database->MarkHdrRead(dbHdr, (flags & kImapMsgSeenFlag) != 0, nullptr);
4536 database->MarkHdrReplied(dbHdr, (flags & kImapMsgAnsweredFlag) != 0, nullptr);
4537 database->MarkHdrMarked(dbHdr, (flags & kImapMsgFlaggedFlag) != 0, nullptr);
4538 database->MarkImapDeleted(msgKey, (flags & kImapMsgDeletedFlag) != 0,
4539 nullptr);
4540
4541 uint32_t supportedFlags;
4542 GetSupportedUserFlags(&supportedFlags);
4543 if (supportedFlags & kImapMsgSupportForwardedFlag)
4544 database->MarkForwarded(msgKey, (flags & kImapMsgForwardedFlag) != 0,
4545 nullptr);
4546 // this turns on labels, but it doesn't handle the case where the user
4547 // unlabels a message on one machine, and expects it to be unlabeled
4548 // on their other machines. If I turn that on, I'll be removing all the labels
4549 // that were assigned before we started storing them on the server, which will
4550 // make some people very unhappy.
4551 if (flags & kImapMsgLabelFlags)
4552 database->SetLabel(msgKey, (flags & kImapMsgLabelFlags) >> 9);
4553 else {
4554 if (supportedFlags & kImapMsgLabelFlags) database->SetLabel(msgKey, 0);
4555 }
4556 if (supportedFlags & kImapMsgSupportMDNSentFlag)
4557 database->MarkMDNSent(msgKey, (flags & kImapMsgMDNSentFlag) != 0, nullptr);
4558
4559 return NS_OK;
4560 }
4561
4562 // message flags operation - this is called from the imap protocol,
4563 // proxied over from the imap thread to the ui thread, when a flag changes
4564 NS_IMETHODIMP
NotifyMessageFlags(uint32_t aFlags,const nsACString & aKeywords,nsMsgKey aMsgKey,uint64_t aHighestModSeq)4565 nsImapMailFolder::NotifyMessageFlags(uint32_t aFlags,
4566 const nsACString& aKeywords,
4567 nsMsgKey aMsgKey,
4568 uint64_t aHighestModSeq) {
4569 if (NS_SUCCEEDED(GetDatabase()) && mDatabase) {
4570 bool msgDeleted = aFlags & kImapMsgDeletedFlag;
4571 if (aHighestModSeq || msgDeleted) {
4572 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
4573 mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
4574 if (dbFolderInfo) {
4575 if (aHighestModSeq) {
4576 char intStrBuf[40];
4577 PR_snprintf(intStrBuf, sizeof(intStrBuf), "%llu", aHighestModSeq);
4578 MOZ_LOG(IMAP_CS, mozilla::LogLevel::Debug,
4579 ("NotifyMessageFlags(): Store highest MODSEQ=%" PRIu64
4580 " for folder=%s",
4581 aHighestModSeq, m_onlineFolderName.get()));
4582 dbFolderInfo->SetCharProperty(kModSeqPropertyName,
4583 nsDependentCString(intStrBuf));
4584 }
4585 if (msgDeleted) {
4586 uint32_t oldDeletedCount;
4587 dbFolderInfo->GetUint32Property(kDeletedHdrCountPropertyName, 0,
4588 &oldDeletedCount);
4589 dbFolderInfo->SetUint32Property(kDeletedHdrCountPropertyName,
4590 oldDeletedCount + 1);
4591 }
4592 }
4593 }
4594 nsCOMPtr<nsIMsgDBHdr> dbHdr;
4595 bool containsKey;
4596 nsresult rv = mDatabase->ContainsKey(aMsgKey, &containsKey);
4597 // if we don't have the header, don't diddle the flags.
4598 // GetMsgHdrForKey will create the header if it doesn't exist.
4599 if (NS_FAILED(rv) || !containsKey) return rv;
4600 rv = mDatabase->GetMsgHdrForKey(aMsgKey, getter_AddRefs(dbHdr));
4601 if (NS_SUCCEEDED(rv) && dbHdr) {
4602 uint32_t supportedUserFlags;
4603 GetSupportedUserFlags(&supportedUserFlags);
4604 NotifyMessageFlagsFromHdr(dbHdr, aMsgKey, aFlags);
4605 nsCString keywords(aKeywords);
4606 HandleCustomFlags(aMsgKey, dbHdr, supportedUserFlags, keywords);
4607 }
4608 }
4609 return NS_OK;
4610 }
4611
4612 NS_IMETHODIMP
NotifyMessageDeleted(const char * onlineFolderName,bool deleteAllMsgs,const char * msgIdString)4613 nsImapMailFolder::NotifyMessageDeleted(const char* onlineFolderName,
4614 bool deleteAllMsgs,
4615 const char* msgIdString) {
4616 if (deleteAllMsgs) return NS_OK;
4617
4618 if (!msgIdString) return NS_OK;
4619
4620 nsTArray<nsMsgKey> affectedMessages;
4621 ParseUidString(msgIdString, affectedMessages);
4622
4623 if (!ShowDeletedMessages()) {
4624 GetDatabase();
4625 NS_ENSURE_TRUE(mDatabase, NS_OK);
4626 if (!ShowDeletedMessages()) {
4627 if (!affectedMessages.IsEmpty()) // perhaps Search deleted these messages
4628 {
4629 DeleteStoreMessages(affectedMessages);
4630 mDatabase->DeleteMessages(affectedMessages, nullptr);
4631 }
4632 } else // && !imapDeleteIsMoveToTrash // TODO: can this ever be executed?
4633 SetIMAPDeletedFlag(mDatabase, affectedMessages, false);
4634 }
4635 return NS_OK;
4636 }
4637
ShowDeletedMessages()4638 bool nsImapMailFolder::ShowDeletedMessages() {
4639 nsresult rv;
4640 nsCOMPtr<nsIImapHostSessionList> hostSession =
4641 do_GetService(kCImapHostSessionList, &rv);
4642 NS_ENSURE_SUCCESS(rv, false);
4643
4644 bool showDeleted = false;
4645 nsCString serverKey;
4646 GetServerKey(serverKey);
4647 hostSession->GetShowDeletedMessagesForHost(serverKey.get(), showDeleted);
4648
4649 return showDeleted;
4650 }
4651
DeleteIsMoveToTrash()4652 bool nsImapMailFolder::DeleteIsMoveToTrash() {
4653 nsresult err;
4654 nsCOMPtr<nsIImapHostSessionList> hostSession =
4655 do_GetService(kCImapHostSessionList, &err);
4656 NS_ENSURE_SUCCESS(err, true);
4657 bool rv = true;
4658
4659 nsCString serverKey;
4660 GetServerKey(serverKey);
4661 hostSession->GetDeleteIsMoveToTrashForHost(serverKey.get(), rv);
4662 return rv;
4663 }
4664
GetTrashFolder(nsIMsgFolder ** pTrashFolder)4665 nsresult nsImapMailFolder::GetTrashFolder(nsIMsgFolder** pTrashFolder) {
4666 NS_ENSURE_ARG_POINTER(pTrashFolder);
4667 nsCOMPtr<nsIMsgFolder> rootFolder;
4668 nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
4669 if (NS_SUCCEEDED(rv) && rootFolder) {
4670 rv = rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Trash, pTrashFolder);
4671 if (!*pTrashFolder) rv = NS_ERROR_FAILURE;
4672 }
4673 return rv;
4674 }
4675
4676 // store nsMsgMessageFlags::IMAPDeleted in the specified mailhdr records
SetIMAPDeletedFlag(nsIMsgDatabase * mailDB,const nsTArray<nsMsgKey> & msgids,bool markDeleted)4677 void nsImapMailFolder::SetIMAPDeletedFlag(nsIMsgDatabase* mailDB,
4678 const nsTArray<nsMsgKey>& msgids,
4679 bool markDeleted) {
4680 nsresult markStatus = NS_OK;
4681 uint32_t total = msgids.Length();
4682
4683 for (uint32_t msgIndex = 0; NS_SUCCEEDED(markStatus) && (msgIndex < total);
4684 msgIndex++)
4685 markStatus =
4686 mailDB->MarkImapDeleted(msgids[msgIndex], markDeleted, nullptr);
4687 }
4688
4689 NS_IMETHODIMP
GetMessageSizeFromDB(const char * id,uint32_t * size)4690 nsImapMailFolder::GetMessageSizeFromDB(const char* id, uint32_t* size) {
4691 NS_ENSURE_ARG_POINTER(size);
4692
4693 *size = 0;
4694 nsresult rv = GetDatabase();
4695 NS_ENSURE_SUCCESS(rv, rv);
4696 if (id) {
4697 nsMsgKey key = msgKeyFromInt(ParseUint64Str(id));
4698 nsCOMPtr<nsIMsgDBHdr> mailHdr;
4699 rv = mDatabase->GetMsgHdrForKey(key, getter_AddRefs(mailHdr));
4700 if (NS_SUCCEEDED(rv) && mailHdr) rv = mailHdr->GetMessageSize(size);
4701 }
4702 return rv;
4703 }
4704
4705 NS_IMETHODIMP
SetContentModified(nsIImapUrl * aImapUrl,nsImapContentModifiedType modified)4706 nsImapMailFolder::SetContentModified(nsIImapUrl* aImapUrl,
4707 nsImapContentModifiedType modified) {
4708 return aImapUrl->SetContentModified(modified);
4709 }
4710
4711 NS_IMETHODIMP
GetCurMoveCopyMessageInfo(nsIImapUrl * runningUrl,PRTime * aDate,nsACString & aKeywords,uint32_t * aResult)4712 nsImapMailFolder::GetCurMoveCopyMessageInfo(nsIImapUrl* runningUrl,
4713 PRTime* aDate,
4714 nsACString& aKeywords,
4715 uint32_t* aResult) {
4716 nsCOMPtr<nsISupports> copyState;
4717 runningUrl->GetCopyState(getter_AddRefs(copyState));
4718 if (copyState) {
4719 nsCOMPtr<nsImapMailCopyState> mailCopyState = do_QueryInterface(copyState);
4720 uint32_t supportedFlags = 0;
4721 GetSupportedUserFlags(&supportedFlags);
4722 if (mailCopyState &&
4723 mailCopyState->m_curIndex < mailCopyState->m_messages.Length()) {
4724 nsIMsgDBHdr* message =
4725 mailCopyState->m_messages[mailCopyState->m_curIndex];
4726 nsMsgLabelValue label;
4727 message->GetFlags(aResult);
4728 if (supportedFlags & (kImapMsgSupportUserFlag | kImapMsgLabelFlags)) {
4729 message->GetLabel(&label);
4730 if (label != 0) *aResult |= label << 25;
4731 }
4732 if (aDate) message->GetDate(aDate);
4733 if (supportedFlags & kImapMsgSupportUserFlag) {
4734 // setup the custom imap keywords, which includes the message keywords
4735 // plus any junk status
4736 nsCString junkscore;
4737 message->GetStringProperty("junkscore", getter_Copies(junkscore));
4738 bool isJunk = false, isNotJunk = false;
4739 if (!junkscore.IsEmpty()) {
4740 if (junkscore.EqualsLiteral("0"))
4741 isNotJunk = true;
4742 else
4743 isJunk = true;
4744 }
4745
4746 nsCString keywords; // MsgFindKeyword can't use nsACString
4747 message->GetStringProperty("keywords", getter_Copies(keywords));
4748 int32_t start;
4749 int32_t length;
4750 bool hasJunk = MsgFindKeyword("junk"_ns, keywords, &start, &length);
4751 if (hasJunk && !isJunk)
4752 keywords.Cut(start, length);
4753 else if (!hasJunk && isJunk)
4754 keywords.AppendLiteral(" Junk");
4755 bool hasNonJunk =
4756 MsgFindKeyword("nonjunk"_ns, keywords, &start, &length);
4757 if (!hasNonJunk)
4758 hasNonJunk = MsgFindKeyword("notjunk"_ns, keywords, &start, &length);
4759 if (hasNonJunk && !isNotJunk)
4760 keywords.Cut(start, length);
4761 else if (!hasNonJunk && isNotJunk)
4762 keywords.AppendLiteral(" NonJunk");
4763
4764 // Cleanup extra spaces
4765 while (!keywords.IsEmpty() && keywords.First() == ' ')
4766 keywords.Cut(0, 1);
4767 while (!keywords.IsEmpty() && keywords.Last() == ' ')
4768 keywords.Cut(keywords.Length() - 1, 1);
4769 while (!keywords.IsEmpty() && (start = keywords.Find(" "_ns)) >= 0)
4770 keywords.Cut(start, 1);
4771 aKeywords.Assign(keywords);
4772 }
4773 }
4774 // if we don't have a source header, and it's not the drafts folder,
4775 // then mark the message read, since it must be an append to the
4776 // fcc or templates folder.
4777 else if (mailCopyState) {
4778 *aResult = mailCopyState->m_newMsgFlags;
4779 if (supportedFlags & kImapMsgSupportUserFlag)
4780 aKeywords.Assign(mailCopyState->m_newMsgKeywords);
4781 }
4782 }
4783 return NS_OK;
4784 }
4785
4786 NS_IMETHODIMP
OnStartRunningUrl(nsIURI * aUrl)4787 nsImapMailFolder::OnStartRunningUrl(nsIURI* aUrl) {
4788 NS_ASSERTION(aUrl, "sanity check - need to be be running non-null url");
4789 nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(aUrl);
4790 if (mailUrl) {
4791 bool updatingFolder;
4792 mailUrl->GetUpdatingFolder(&updatingFolder);
4793 m_updatingFolder = updatingFolder;
4794 }
4795 m_urlRunning = true;
4796 return NS_OK;
4797 }
4798
4799 NS_IMETHODIMP
OnStopRunningUrl(nsIURI * aUrl,nsresult aExitCode)4800 nsImapMailFolder::OnStopRunningUrl(nsIURI* aUrl, nsresult aExitCode) {
4801 nsresult rv;
4802 bool endedOfflineDownload = false;
4803 nsImapAction imapAction = nsIImapUrl::nsImapTest;
4804 m_urlRunning = false;
4805 m_updatingFolder = false;
4806 nsCOMPtr<nsIMsgMailSession> session =
4807 do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
4808 NS_ENSURE_SUCCESS(rv, rv);
4809 if (aUrl) {
4810 nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(aUrl, &rv);
4811 NS_ENSURE_SUCCESS(rv, rv);
4812 bool downloadingForOfflineUse;
4813 imapUrl->GetStoreResultsOffline(&downloadingForOfflineUse);
4814 bool hasSemaphore = false;
4815 // if we have the folder locked, clear it.
4816 TestSemaphore(static_cast<nsIMsgFolder*>(this), &hasSemaphore);
4817 if (hasSemaphore) ReleaseSemaphore(static_cast<nsIMsgFolder*>(this));
4818 if (downloadingForOfflineUse) {
4819 endedOfflineDownload = true;
4820 EndOfflineDownload();
4821 }
4822 nsCOMPtr<nsIMsgWindow> msgWindow;
4823 nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(aUrl);
4824 bool folderOpen = false;
4825 if (mailUrl) mailUrl->GetMsgWindow(getter_AddRefs(msgWindow));
4826 if (session) session->IsFolderOpenInWindow(this, &folderOpen);
4827 #ifdef DEBUG_bienvenu
4828 printf("stop running url %s\n", aUrl->GetSpecOrDefault().get());
4829 #endif
4830
4831 if (imapUrl) {
4832 DisplayStatusMsg(imapUrl, EmptyString());
4833 imapUrl->GetImapAction(&imapAction);
4834 if (imapAction == nsIImapUrl::nsImapMsgFetch ||
4835 imapAction == nsIImapUrl::nsImapMsgDownloadForOffline) {
4836 ReleaseSemaphore(static_cast<nsIMsgFolder*>(this));
4837 if (!endedOfflineDownload) EndOfflineDownload();
4838 }
4839
4840 // Notify move, copy or delete (online operations)
4841 // Not sure whether nsImapDeleteMsg is even used, deletes in all three
4842 // models use nsImapAddMsgFlags.
4843 nsCOMPtr<nsIMsgFolderNotificationService> notifier(
4844 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
4845 if (notifier && m_copyState) {
4846 if (imapAction == nsIImapUrl::nsImapOnlineMove) {
4847 notifier->NotifyMsgsMoveCopyCompleted(true, m_copyState->m_messages,
4848 this, {});
4849 } else if (imapAction == nsIImapUrl::nsImapOnlineCopy) {
4850 notifier->NotifyMsgsMoveCopyCompleted(false, m_copyState->m_messages,
4851 this, {});
4852 } else if (imapAction == nsIImapUrl::nsImapDeleteMsg) {
4853 notifier->NotifyMsgsDeleted(m_copyState->m_messages);
4854 }
4855 }
4856
4857 switch (imapAction) {
4858 case nsIImapUrl::nsImapDeleteMsg:
4859 case nsIImapUrl::nsImapOnlineMove:
4860 case nsIImapUrl::nsImapOnlineCopy:
4861 if (NS_SUCCEEDED(aExitCode)) {
4862 if (folderOpen)
4863 UpdateFolder(msgWindow);
4864 else
4865 UpdatePendingCounts();
4866 }
4867
4868 if (m_copyState) {
4869 nsCOMPtr<nsIMsgFolder> srcFolder =
4870 do_QueryInterface(m_copyState->m_srcSupport, &rv);
4871 if (m_copyState->m_isMove && !m_copyState->m_isCrossServerOp) {
4872 if (NS_SUCCEEDED(aExitCode)) {
4873 nsCOMPtr<nsIMsgDatabase> srcDB;
4874 if (srcFolder)
4875 rv = srcFolder->GetMsgDatabase(getter_AddRefs(srcDB));
4876 if (NS_SUCCEEDED(rv) && srcDB) {
4877 RefPtr<nsImapMoveCopyMsgTxn> msgTxn;
4878 nsTArray<nsMsgKey> srcKeyArray;
4879 if (m_copyState->m_allowUndo) {
4880 msgTxn = m_copyState->m_undoMsgTxn;
4881 if (msgTxn) msgTxn->GetSrcKeyArray(srcKeyArray);
4882 } else {
4883 nsAutoCString messageIds;
4884 rv = BuildIdsAndKeyArray(m_copyState->m_messages,
4885 messageIds, srcKeyArray);
4886 NS_ENSURE_SUCCESS(rv, rv);
4887 }
4888
4889 if (!ShowDeletedMessages()) {
4890 // We only reach here for same-server operations
4891 // (!m_copyState->m_isCrossServerOp in if above), so we can
4892 // assume that the src is also imap that uses offline
4893 // storage.
4894 DeleteStoreMessages(srcKeyArray, srcFolder);
4895 srcDB->DeleteMessages(srcKeyArray, nullptr);
4896 } else
4897 MarkMessagesImapDeleted(&srcKeyArray, true, srcDB);
4898 }
4899 srcFolder->EnableNotifications(allMessageCountNotifications,
4900 true);
4901 // even if we're showing deleted messages,
4902 // we still need to notify FE so it will show the imap deleted
4903 // flag
4904 srcFolder->NotifyFolderEvent(kDeleteOrMoveMsgCompleted);
4905 // is there a way to see that we think we have new msgs?
4906 nsCOMPtr<nsIPrefBranch> prefBranch(
4907 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
4908 if (NS_SUCCEEDED(rv)) {
4909 bool showPreviewText;
4910 prefBranch->GetBoolPref("mail.biff.alert.show_preview",
4911 &showPreviewText);
4912 // if we're showing preview text, update ourselves if we got a
4913 // new unread message copied so that we can download the new
4914 // headers and have a chance to preview the msg bodies.
4915 if (!folderOpen && showPreviewText &&
4916 m_copyState->m_unreadCount > 0 &&
4917 !(mFlags &
4918 (nsMsgFolderFlags::Trash | nsMsgFolderFlags::Junk)))
4919 UpdateFolder(msgWindow);
4920 }
4921 } else {
4922 srcFolder->EnableNotifications(allMessageCountNotifications,
4923 true);
4924 srcFolder->NotifyFolderEvent(kDeleteOrMoveMsgFailed);
4925 }
4926 }
4927 if (m_copyState->m_msgWindow &&
4928 m_copyState->m_undoMsgTxn && // may be null from filters
4929 NS_SUCCEEDED(
4930 aExitCode)) // we should do this only if move/copy succeeds
4931 {
4932 nsCOMPtr<nsITransactionManager> txnMgr;
4933 m_copyState->m_msgWindow->GetTransactionManager(
4934 getter_AddRefs(txnMgr));
4935 if (txnMgr) {
4936 RefPtr<nsImapMoveCopyMsgTxn> txn = m_copyState->m_undoMsgTxn;
4937 mozilla::DebugOnly<nsresult> rv2 = txnMgr->DoTransaction(txn);
4938 NS_ASSERTION(NS_SUCCEEDED(rv2), "doing transaction failed");
4939 }
4940 }
4941 // nsImapUrl can hold a pointer to our m_copyState, so force a
4942 // release here (see Bug 1586494).
4943 imapUrl->SetCopyState(nullptr);
4944 (void)OnCopyCompleted(m_copyState->m_srcSupport, aExitCode);
4945 }
4946
4947 // we're the dest folder of a move/copy - if we're not open in the ui,
4948 // then we should clear our nsMsgDatabase pointer. Otherwise, the db
4949 // would be open until the user selected it and then selected another
4950 // folder. but don't do this for the trash or inbox - we'll leave them
4951 // open
4952 if (!folderOpen &&
4953 !(mFlags & (nsMsgFolderFlags::Trash | nsMsgFolderFlags::Inbox)))
4954 SetMsgDatabase(nullptr);
4955 break;
4956 case nsIImapUrl::nsImapSubtractMsgFlags: {
4957 // this isn't really right - we'd like to know we were
4958 // deleting a message to start with, but it probably
4959 // won't do any harm.
4960 imapMessageFlagsType flags = 0;
4961 imapUrl->GetMsgFlags(&flags);
4962 // we need to subtract the delete flag in db only in case when we show
4963 // deleted msgs
4964 if (flags & kImapMsgDeletedFlag && ShowDeletedMessages()) {
4965 nsCOMPtr<nsIMsgDatabase> db;
4966 rv = GetMsgDatabase(getter_AddRefs(db));
4967 if (NS_SUCCEEDED(rv) && db) {
4968 nsTArray<nsMsgKey> keyArray;
4969 nsCString keyString;
4970 imapUrl->GetListOfMessageIds(keyString);
4971 ParseUidString(keyString.get(), keyArray);
4972 MarkMessagesImapDeleted(&keyArray, false, db);
4973 db->Commit(nsMsgDBCommitType::kLargeCommit);
4974 }
4975 }
4976 } break;
4977 case nsIImapUrl::nsImapAddMsgFlags: {
4978 imapMessageFlagsType flags = 0;
4979 imapUrl->GetMsgFlags(&flags);
4980 if (flags & kImapMsgDeletedFlag) {
4981 // we need to delete headers from db only when we don't show deleted
4982 // msgs
4983 if (!ShowDeletedMessages()) {
4984 nsCOMPtr<nsIMsgDatabase> db;
4985 rv = GetMsgDatabase(getter_AddRefs(db));
4986 if (NS_SUCCEEDED(rv) && db) {
4987 nsTArray<nsMsgKey> keyArray;
4988 nsCString keyString;
4989 imapUrl->GetListOfMessageIds(keyString);
4990 ParseUidString(keyString.get(), keyArray);
4991
4992 // For pluggable stores that do not support compaction, we need
4993 // to delete the messages now.
4994 bool supportsCompaction = false;
4995 nsCOMPtr<nsIMsgPluggableStore> offlineStore;
4996 (void)GetMsgStore(getter_AddRefs(offlineStore));
4997 if (offlineStore)
4998 offlineStore->GetSupportsCompaction(&supportsCompaction);
4999
5000 nsTArray<RefPtr<nsIMsgDBHdr>> msgHdrs;
5001 if (notifier || !supportsCompaction) {
5002 MsgGetHeadersFromKeys(db, keyArray, msgHdrs);
5003 }
5004
5005 // Notify listeners of delete.
5006 if (notifier && !msgHdrs.IsEmpty()) {
5007 // XXX Currently, the DeleteMessages below gets executed twice
5008 // on deletes. Once in DeleteMessages, once here. The second
5009 // time, it silently fails to delete. This is why we're also
5010 // checking whether the array is empty.
5011 notifier->NotifyMsgsDeleted(msgHdrs);
5012 }
5013
5014 if (!supportsCompaction && !msgHdrs.IsEmpty())
5015 DeleteStoreMessages(msgHdrs);
5016
5017 db->DeleteMessages(keyArray, nullptr);
5018 db->SetSummaryValid(true);
5019 db->Commit(nsMsgDBCommitType::kLargeCommit);
5020 }
5021 }
5022 }
5023 } break;
5024 case nsIImapUrl::nsImapAppendMsgFromFile:
5025 case nsIImapUrl::nsImapAppendDraftFromFile:
5026 if (m_copyState) {
5027 if (NS_SUCCEEDED(aExitCode)) {
5028 UpdatePendingCounts();
5029
5030 m_copyState->m_curIndex++;
5031 if (m_copyState->m_curIndex >= m_copyState->m_messages.Length()) {
5032 nsCOMPtr<nsIUrlListener> saveUrlListener = m_urlListener;
5033 if (folderOpen) {
5034 // This gives a way for the caller to get notified
5035 // when the UpdateFolder url is done.
5036 // (if the nsIMsgCopyServiceListener also implements
5037 // nsIUrlListener)
5038 if (m_copyState->m_listener)
5039 m_urlListener = do_QueryInterface(m_copyState->m_listener);
5040 }
5041 if (m_copyState->m_msgWindow && m_copyState->m_undoMsgTxn) {
5042 nsCOMPtr<nsITransactionManager> txnMgr;
5043 m_copyState->m_msgWindow->GetTransactionManager(
5044 getter_AddRefs(txnMgr));
5045 if (txnMgr) {
5046 RefPtr<nsImapMoveCopyMsgTxn> txn =
5047 m_copyState->m_undoMsgTxn;
5048 txnMgr->DoTransaction(txn);
5049 }
5050 }
5051 (void)OnCopyCompleted(m_copyState->m_srcSupport, aExitCode);
5052 if (folderOpen ||
5053 imapAction == nsIImapUrl::nsImapAppendDraftFromFile) {
5054 UpdateFolderWithListener(msgWindow, m_urlListener);
5055 m_urlListener = saveUrlListener;
5056 }
5057 }
5058 } else {
5059 // clear the copyState if copy has failed
5060 (void)OnCopyCompleted(m_copyState->m_srcSupport, aExitCode);
5061 }
5062 }
5063 break;
5064 case nsIImapUrl::nsImapMoveFolderHierarchy:
5065 if (m_copyState) // delete folder gets here, but w/o an m_copyState
5066 {
5067 nsCOMPtr<nsIMsgCopyService> copyService =
5068 do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
5069 NS_ENSURE_SUCCESS(rv, rv);
5070 nsCOMPtr<nsIMsgFolder> srcFolder =
5071 do_QueryInterface(m_copyState->m_srcSupport);
5072 if (srcFolder) {
5073 copyService->NotifyCompletion(m_copyState->m_srcSupport, this,
5074 aExitCode);
5075 }
5076 m_copyState = nullptr;
5077 }
5078 break;
5079 case nsIImapUrl::nsImapRenameFolder:
5080 if (NS_FAILED(aExitCode)) {
5081 NotifyFolderEvent(kRenameCompleted);
5082 }
5083 break;
5084 case nsIImapUrl::nsImapDeleteAllMsgs:
5085 if (NS_SUCCEEDED(aExitCode)) {
5086 if (folderOpen)
5087 UpdateFolder(msgWindow);
5088 else {
5089 ChangeNumPendingTotalMessages(-mNumPendingTotalMessages);
5090 ChangeNumPendingUnread(-mNumPendingUnreadMessages);
5091 m_numServerUnseenMessages = 0;
5092 }
5093 }
5094 break;
5095 case nsIImapUrl::nsImapListFolder:
5096 if (NS_SUCCEEDED(aExitCode)) {
5097 // listing folder will open db; don't leave the db open.
5098 SetMsgDatabase(nullptr);
5099 if (!m_verifiedAsOnlineFolder) {
5100 // If folder is not verified, we remove it.
5101 nsCOMPtr<nsIMsgFolder> parent;
5102 rv = GetParent(getter_AddRefs(parent));
5103 if (NS_SUCCEEDED(rv) && parent) {
5104 nsCOMPtr<nsIMsgImapMailFolder> imapParent =
5105 do_QueryInterface(parent);
5106 if (imapParent) this->RemoveLocalSelf();
5107 }
5108 }
5109 }
5110 break;
5111 case nsIImapUrl::nsImapRefreshFolderUrls:
5112 // we finished getting an admin url for the folder.
5113 if (!m_adminUrl.IsEmpty()) FolderPrivileges(msgWindow);
5114 break;
5115 case nsIImapUrl::nsImapCreateFolder:
5116 if (NS_FAILED(aExitCode)) // if success notification already done
5117 {
5118 NotifyFolderEvent(kFolderCreateFailed);
5119 }
5120 break;
5121 case nsIImapUrl::nsImapSubscribe:
5122 if (NS_SUCCEEDED(aExitCode) && msgWindow) {
5123 nsCString canonicalFolderName;
5124 imapUrl->CreateCanonicalSourceFolderPathString(
5125 getter_Copies(canonicalFolderName));
5126 nsCOMPtr<nsIMsgFolder> rootFolder;
5127 nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
5128 if (NS_SUCCEEDED(rv) && rootFolder) {
5129 nsCOMPtr<nsIMsgImapMailFolder> imapRoot =
5130 do_QueryInterface(rootFolder);
5131 if (imapRoot) {
5132 nsCOMPtr<nsIMsgImapMailFolder> foundFolder;
5133 rv = imapRoot->FindOnlineSubFolder(canonicalFolderName,
5134 getter_AddRefs(foundFolder));
5135 if (NS_SUCCEEDED(rv) && foundFolder) {
5136 nsCString uri;
5137 nsCOMPtr<nsIMsgFolder> msgFolder =
5138 do_QueryInterface(foundFolder);
5139 if (msgFolder) {
5140 msgFolder->GetURI(uri);
5141 nsCOMPtr<nsIMsgWindowCommands> windowCommands;
5142 msgWindow->GetWindowCommands(
5143 getter_AddRefs(windowCommands));
5144 if (windowCommands) windowCommands->SelectFolder(uri);
5145 }
5146 }
5147 }
5148 }
5149 }
5150 break;
5151 case nsIImapUrl::nsImapExpungeFolder:
5152 m_expunging = false;
5153 break;
5154 default:
5155 break;
5156 }
5157 }
5158 // give base class a chance to send folder loaded notification...
5159 rv = nsMsgDBFolder::OnStopRunningUrl(aUrl, aExitCode);
5160 }
5161 // if we're not running a url, we must not be getting new mail.
5162 SetGettingNewMessages(false);
5163 // don't send OnStopRunning notification if still compacting offline store.
5164 if (m_urlListener && (imapAction != nsIImapUrl::nsImapExpungeFolder ||
5165 !m_compactingOfflineStore)) {
5166 nsCOMPtr<nsIUrlListener> saveListener = m_urlListener;
5167 m_urlListener = nullptr;
5168 saveListener->OnStopRunningUrl(aUrl, aExitCode);
5169 }
5170 return rv;
5171 }
5172
UpdatePendingCounts()5173 void nsImapMailFolder::UpdatePendingCounts() {
5174 if (m_copyState) {
5175 int32_t delta =
5176 m_copyState->m_isCrossServerOp ? 1 : m_copyState->m_messages.Length();
5177 if (!m_copyState->m_selectedState && m_copyState->m_messages.IsEmpty()) {
5178 // special case from CopyFileMessage():
5179 // - copied a single message in from a file
5180 // - no previously-existing messages are involved
5181 delta = 1;
5182 }
5183 ChangePendingTotal(delta);
5184
5185 // count the moves that were unread
5186 int numUnread = m_copyState->m_unreadCount;
5187 if (numUnread) {
5188 m_numServerUnseenMessages +=
5189 numUnread; // adjust last status count by this delta.
5190 ChangeNumPendingUnread(numUnread);
5191 }
5192 SummaryChanged();
5193 }
5194 }
5195
5196 NS_IMETHODIMP
ClearFolderRights()5197 nsImapMailFolder::ClearFolderRights() {
5198 SetFolderNeedsACLListed(false);
5199 delete m_folderACL;
5200 m_folderACL = new nsMsgIMAPFolderACL(this);
5201 return NS_OK;
5202 }
5203
5204 NS_IMETHODIMP
AddFolderRights(const nsACString & userName,const nsACString & rights)5205 nsImapMailFolder::AddFolderRights(const nsACString& userName,
5206 const nsACString& rights) {
5207 SetFolderNeedsACLListed(false);
5208 GetFolderACL()->SetFolderRightsForUser(userName, rights);
5209 return NS_OK;
5210 }
5211
5212 NS_IMETHODIMP
RefreshFolderRights()5213 nsImapMailFolder::RefreshFolderRights() {
5214 if (GetFolderACL()->GetIsFolderShared())
5215 SetFlag(nsMsgFolderFlags::PersonalShared);
5216 else
5217 ClearFlag(nsMsgFolderFlags::PersonalShared);
5218 return NS_OK;
5219 }
5220
5221 NS_IMETHODIMP
SetCopyResponseUid(const char * msgIdString,nsIImapUrl * aUrl)5222 nsImapMailFolder::SetCopyResponseUid(const char* msgIdString,
5223 nsIImapUrl* aUrl) { // CopyMessages() only
5224 nsresult rv = NS_OK;
5225 RefPtr<nsImapMoveCopyMsgTxn> msgTxn;
5226 nsCOMPtr<nsISupports> copyState;
5227
5228 if (aUrl) aUrl->GetCopyState(getter_AddRefs(copyState));
5229
5230 if (copyState) {
5231 nsCOMPtr<nsImapMailCopyState> mailCopyState =
5232 do_QueryInterface(copyState, &rv);
5233 if (NS_FAILED(rv)) return rv;
5234 if (mailCopyState->m_undoMsgTxn) msgTxn = mailCopyState->m_undoMsgTxn;
5235 } else if (aUrl && m_pendingOfflineMoves.Length()) {
5236 nsCString urlSourceMsgIds, undoTxnSourceMsgIds;
5237 aUrl->GetListOfMessageIds(urlSourceMsgIds);
5238 RefPtr<nsImapMoveCopyMsgTxn> imapUndo = m_pendingOfflineMoves[0];
5239 if (imapUndo) {
5240 imapUndo->GetSrcMsgIds(undoTxnSourceMsgIds);
5241 if (undoTxnSourceMsgIds.Equals(urlSourceMsgIds)) msgTxn = imapUndo;
5242 // ### we should handle batched moves, but lets keep it simple for a2.
5243 m_pendingOfflineMoves.Clear();
5244 }
5245 }
5246 if (msgTxn) msgTxn->SetCopyResponseUid(msgIdString);
5247 return NS_OK;
5248 }
5249
5250 NS_IMETHODIMP
StartMessage(nsIMsgMailNewsUrl * aUrl)5251 nsImapMailFolder::StartMessage(nsIMsgMailNewsUrl* aUrl) {
5252 nsCOMPtr<nsIImapUrl> imapUrl(do_QueryInterface(aUrl));
5253 nsCOMPtr<nsISupports> copyState;
5254 NS_ENSURE_TRUE(imapUrl, NS_ERROR_FAILURE);
5255
5256 imapUrl->GetCopyState(getter_AddRefs(copyState));
5257 if (copyState) {
5258 nsCOMPtr<nsICopyMessageStreamListener> listener =
5259 do_QueryInterface(copyState);
5260 if (listener) listener->StartMessage();
5261 }
5262 return NS_OK;
5263 }
5264
5265 NS_IMETHODIMP
EndMessage(nsIMsgMailNewsUrl * aUrl,nsMsgKey uidOfMessage)5266 nsImapMailFolder::EndMessage(nsIMsgMailNewsUrl* aUrl, nsMsgKey uidOfMessage) {
5267 nsCOMPtr<nsIImapUrl> imapUrl(do_QueryInterface(aUrl));
5268 nsCOMPtr<nsISupports> copyState;
5269 NS_ENSURE_TRUE(imapUrl, NS_ERROR_FAILURE);
5270 imapUrl->GetCopyState(getter_AddRefs(copyState));
5271 if (copyState) {
5272 nsCOMPtr<nsICopyMessageStreamListener> listener =
5273 do_QueryInterface(copyState);
5274 if (listener) listener->EndMessage(uidOfMessage);
5275 }
5276 return NS_OK;
5277 }
5278
5279 #define WHITESPACE " \015\012" // token delimiter
5280
5281 NS_IMETHODIMP
NotifySearchHit(nsIMsgMailNewsUrl * aUrl,const char * searchHitLine)5282 nsImapMailFolder::NotifySearchHit(nsIMsgMailNewsUrl* aUrl,
5283 const char* searchHitLine) {
5284 NS_ENSURE_ARG_POINTER(aUrl);
5285 nsresult rv = GetDatabase();
5286 NS_ENSURE_SUCCESS(rv, rv);
5287
5288 // expect search results in the form of "* SEARCH <hit> <hit> ..."
5289 // expect search results in the form of "* SEARCH <hit> <hit> ..."
5290 nsCString tokenString(searchHitLine);
5291 char* currentPosition = PL_strcasestr(tokenString.get(), "SEARCH");
5292 if (currentPosition) {
5293 currentPosition += strlen("SEARCH");
5294 bool shownUpdateAlert = false;
5295 char* hitUidToken = NS_strtok(WHITESPACE, ¤tPosition);
5296 while (hitUidToken) {
5297 long naturalLong; // %l is 64 bits on OSF1
5298 sscanf(hitUidToken, "%ld", &naturalLong);
5299 nsMsgKey hitUid = (nsMsgKey)naturalLong;
5300
5301 nsCOMPtr<nsIMsgDBHdr> hitHeader;
5302 rv = mDatabase->GetMsgHdrForKey(hitUid, getter_AddRefs(hitHeader));
5303 if (NS_SUCCEEDED(rv) && hitHeader) {
5304 nsCOMPtr<nsIMsgSearchSession> searchSession;
5305 nsCOMPtr<nsIMsgSearchAdapter> searchAdapter;
5306 aUrl->GetSearchSession(getter_AddRefs(searchSession));
5307 if (searchSession) {
5308 searchSession->GetRunningAdapter(getter_AddRefs(searchAdapter));
5309 if (searchAdapter) searchAdapter->AddResultElement(hitHeader);
5310 }
5311 } else if (!shownUpdateAlert) {
5312 }
5313
5314 hitUidToken = NS_strtok(WHITESPACE, ¤tPosition);
5315 }
5316 }
5317 return NS_OK;
5318 }
5319
5320 NS_IMETHODIMP
SetAppendMsgUid(nsMsgKey aKey,nsIImapUrl * aUrl)5321 nsImapMailFolder::SetAppendMsgUid(nsMsgKey aKey, nsIImapUrl* aUrl) {
5322 nsresult rv;
5323 nsCOMPtr<nsISupports> copyState;
5324 if (aUrl) aUrl->GetCopyState(getter_AddRefs(copyState));
5325 if (copyState) {
5326 nsCOMPtr<nsImapMailCopyState> mailCopyState =
5327 do_QueryInterface(copyState, &rv);
5328 if (NS_FAILED(rv)) return rv;
5329
5330 if (mailCopyState->m_undoMsgTxn) // CopyMessages()
5331 {
5332 RefPtr<nsImapMoveCopyMsgTxn> msgTxn;
5333 msgTxn = mailCopyState->m_undoMsgTxn;
5334 msgTxn->AddDstKey(aKey);
5335 } else if (mailCopyState->m_listener) // CopyFileMessage();
5336 // Draft/Template goes here
5337 {
5338 mailCopyState->m_appendUID = aKey;
5339 mailCopyState->m_listener->SetMessageKey(aKey);
5340 }
5341 }
5342 return NS_OK;
5343 }
5344
5345 NS_IMETHODIMP
GetMessageId(nsIImapUrl * aUrl,nsACString & messageId)5346 nsImapMailFolder::GetMessageId(nsIImapUrl* aUrl, nsACString& messageId) {
5347 nsresult rv = NS_OK;
5348 nsCOMPtr<nsISupports> copyState;
5349
5350 if (aUrl) aUrl->GetCopyState(getter_AddRefs(copyState));
5351 if (copyState) {
5352 nsCOMPtr<nsImapMailCopyState> mailCopyState =
5353 do_QueryInterface(copyState, &rv);
5354 if (NS_FAILED(rv)) return rv;
5355 if (mailCopyState->m_listener)
5356 rv = mailCopyState->m_listener->GetMessageId(messageId);
5357 }
5358 if (NS_SUCCEEDED(rv) && messageId.Length() > 0) {
5359 if (messageId.First() == '<') messageId.Cut(0, 1);
5360 if (messageId.Last() == '>') messageId.SetLength(messageId.Length() - 1);
5361 }
5362 return rv;
5363 }
5364
5365 NS_IMETHODIMP
HeaderFetchCompleted(nsIImapProtocol * aProtocol)5366 nsImapMailFolder::HeaderFetchCompleted(nsIImapProtocol* aProtocol) {
5367 nsCOMPtr<nsIMsgWindow>
5368 msgWindow; // we might need this for the filter plugins.
5369 if (mBackupDatabase) RemoveBackupMsgDatabase();
5370
5371 SetSizeOnDisk(mFolderSize);
5372 int32_t numNewBiffMsgs = 0;
5373 if (m_performingBiff) GetNumNewMessages(false, &numNewBiffMsgs);
5374
5375 bool pendingMoves = m_moveCoalescer && m_moveCoalescer->HasPendingMoves();
5376 PlaybackCoalescedOperations();
5377 if (aProtocol) {
5378 // check if we should download message bodies because it's the inbox and
5379 // the server is specified as one where where we download msg bodies
5380 // automatically. Or if we autosyncing all offline folders.
5381 nsCOMPtr<nsIImapIncomingServer> imapServer;
5382 GetImapIncomingServer(getter_AddRefs(imapServer));
5383
5384 bool autoDownloadNewHeaders = false;
5385 bool autoSyncOfflineStores = false;
5386
5387 if (imapServer) {
5388 imapServer->GetAutoSyncOfflineStores(&autoSyncOfflineStores);
5389 imapServer->GetDownloadBodiesOnGetNewMail(&autoDownloadNewHeaders);
5390 if (m_filterListRequiresBody) autoDownloadNewHeaders = true;
5391 }
5392 bool notifiedBodies = false;
5393 if (m_downloadingFolderForOfflineUse || autoSyncOfflineStores ||
5394 autoDownloadNewHeaders) {
5395 nsTArray<nsMsgKey> keysToDownload;
5396 GetBodysToDownload(&keysToDownload);
5397 // this is the case when DownloadAllForOffline is called.
5398 if (!keysToDownload.IsEmpty() &&
5399 (m_downloadingFolderForOfflineUse || autoDownloadNewHeaders)) {
5400 notifiedBodies = true;
5401 aProtocol->NotifyBodysToDownload(keysToDownload);
5402 } else {
5403 // create auto-sync state object lazily
5404 InitAutoSyncState();
5405
5406 // make enough room for new downloads
5407 m_autoSyncStateObj->ManageStorageSpace();
5408 m_autoSyncStateObj->SetServerCounts(
5409 m_numServerTotalMessages, m_numServerRecentMessages,
5410 m_numServerUnseenMessages, m_nextUID);
5411 m_autoSyncStateObj->OnNewHeaderFetchCompleted(keysToDownload);
5412 }
5413 }
5414 if (!notifiedBodies) {
5415 nsTArray<nsMsgKey> noBodies;
5416 aProtocol->NotifyBodysToDownload(noBodies);
5417 }
5418
5419 nsCOMPtr<nsIURI> runningUri;
5420 aProtocol->GetRunningUrl(getter_AddRefs(runningUri));
5421 if (runningUri) {
5422 nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(runningUri);
5423 if (mailnewsUrl) mailnewsUrl->GetMsgWindow(getter_AddRefs(msgWindow));
5424 }
5425 }
5426
5427 // delay calling plugins if filter application is also delayed
5428 if (!m_filterListRequiresBody) {
5429 bool filtersRun;
5430 CallFilterPlugins(msgWindow, &filtersRun);
5431 if (!filtersRun && m_performingBiff && mDatabase && numNewBiffMsgs > 0 &&
5432 (!pendingMoves || !ShowPreviewText())) {
5433 // If we are performing biff for this folder, tell the
5434 // stand-alone biff about the new high water mark
5435 // We must ensure that the server knows that we are performing biff.
5436 // Otherwise the stand-alone biff won't fire.
5437 nsCOMPtr<nsIMsgIncomingServer> server;
5438 if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))) && server)
5439 server->SetPerformingBiff(true);
5440
5441 SetBiffState(nsIMsgFolder::nsMsgBiffState_NewMail);
5442 if (server) server->SetPerformingBiff(false);
5443 m_performingBiff = false;
5444 }
5445
5446 if (m_filterList) (void)m_filterList->FlushLogIfNecessary();
5447 }
5448
5449 return NS_OK;
5450 }
5451
5452 NS_IMETHODIMP
SetBiffStateAndUpdate(nsMsgBiffState biffState)5453 nsImapMailFolder::SetBiffStateAndUpdate(nsMsgBiffState biffState) {
5454 SetBiffState(biffState);
5455 return NS_OK;
5456 }
5457
5458 NS_IMETHODIMP
GetUidValidity(int32_t * uidValidity)5459 nsImapMailFolder::GetUidValidity(int32_t* uidValidity) {
5460 NS_ENSURE_ARG(uidValidity);
5461 if ((int32_t)m_uidValidity == kUidUnknown) {
5462 nsCOMPtr<nsIMsgDatabase> db;
5463 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
5464 (void)GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo),
5465 getter_AddRefs(db));
5466 if (db) db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
5467
5468 if (dbFolderInfo)
5469 dbFolderInfo->GetImapUidValidity((int32_t*)&m_uidValidity);
5470 }
5471 *uidValidity = m_uidValidity;
5472 return NS_OK;
5473 }
5474
5475 NS_IMETHODIMP
SetUidValidity(int32_t uidValidity)5476 nsImapMailFolder::SetUidValidity(int32_t uidValidity) {
5477 m_uidValidity = uidValidity;
5478 return NS_OK;
5479 }
5480
5481 NS_IMETHODIMP
FillInFolderProps(nsIMsgImapFolderProps * aFolderProps)5482 nsImapMailFolder::FillInFolderProps(nsIMsgImapFolderProps* aFolderProps) {
5483 NS_ENSURE_ARG(aFolderProps);
5484 const char* folderTypeStringID;
5485 const char* folderTypeDescStringID = nullptr;
5486 const char* folderQuotaStatusStringID;
5487 nsString folderType;
5488 nsString folderTypeDesc;
5489 nsString folderQuotaStatusDesc;
5490 nsCOMPtr<nsIStringBundle> bundle;
5491 nsresult rv = IMAPGetStringBundle(getter_AddRefs(bundle));
5492 NS_ENSURE_SUCCESS(rv, rv);
5493
5494 nsCOMPtr<nsIImapIncomingServer> imapServer;
5495 rv = GetImapIncomingServer(getter_AddRefs(imapServer));
5496 // if for some bizarre reason this fails, we'll still fall through to the
5497 // normal sharing code
5498 if (NS_SUCCEEDED(rv)) {
5499 // get the latest committed imap capabilities bit mask.
5500 eIMAPCapabilityFlags capability = kCapabilityUndefined;
5501 imapServer->GetCapability(&capability);
5502 bool haveACL = capability & kACLCapability;
5503 bool haveQuota = capability & kQuotaCapability;
5504
5505 // Figure out what to display in the Quota tab of the folder properties.
5506 // Does the server support quotas? This depends on the latest imap
5507 // CAPABILITY response.
5508 if (haveQuota) {
5509 // Have quota capability. Have we asked the server for quota information?
5510 if (m_folderQuotaCommandIssued) {
5511 // Has the server replied with all the quota info?
5512 if (m_folderQuotaDataIsValid) {
5513 if (!m_folderQuota.IsEmpty()) {
5514 // If so, set quota data to show in the quota tab
5515 folderQuotaStatusStringID = nullptr;
5516 aFolderProps->SetQuotaData(m_folderQuota);
5517 } else {
5518 // The server reported no quota limits on this folder.
5519 folderQuotaStatusStringID = "imapQuotaStatusNoQuota2";
5520 }
5521 } else {
5522 // The getquotaroot command was sent to the server but the complete
5523 // response was not yet received when the folder properties were
5524 // requested. This is rare. Request the folder properties again to
5525 // obtain the quota data.
5526 folderQuotaStatusStringID = "imapQuotaStatusInProgress";
5527 }
5528 } else {
5529 // The folder is not open, so no quota information is available
5530 folderQuotaStatusStringID = "imapQuotaStatusFolderNotOpen";
5531 }
5532 } else {
5533 // Either the server doesn't support quotas, or we don't know if it does
5534 // (e.g., because we don't have a connection yet). If the latter, we fall
5535 // back to saying that no information is available because the folder is
5536 // not yet open.
5537 folderQuotaStatusStringID = (capability == kCapabilityUndefined)
5538 ? "imapQuotaStatusFolderNotOpen"
5539 : "imapQuotaStatusNotSupported";
5540 }
5541
5542 if (!folderQuotaStatusStringID) {
5543 // Display quota data
5544 aFolderProps->ShowQuotaData(true);
5545 } else {
5546 // Hide quota data and show reason why it is not available
5547 aFolderProps->ShowQuotaData(false);
5548
5549 rv = IMAPGetStringByName(folderQuotaStatusStringID,
5550 getter_Copies(folderQuotaStatusDesc));
5551 if (NS_SUCCEEDED(rv)) aFolderProps->SetQuotaStatus(folderQuotaStatusDesc);
5552 }
5553
5554 // See if the server supports ACL.
5555 // If not, just set the folder description to a string that says
5556 // the server doesn't support sharing, and return.
5557 if (!haveACL) {
5558 rv = IMAPGetStringByName("imapServerDoesntSupportAcl",
5559 getter_Copies(folderTypeDesc));
5560 if (NS_SUCCEEDED(rv))
5561 aFolderProps->SetFolderTypeDescription(folderTypeDesc);
5562 aFolderProps->ServerDoesntSupportACL();
5563 return NS_OK;
5564 }
5565 }
5566 if (mFlags & nsMsgFolderFlags::ImapPublic) {
5567 folderTypeStringID = "imapPublicFolderTypeName";
5568 folderTypeDescStringID = "imapPublicFolderTypeDescription";
5569 } else if (mFlags & nsMsgFolderFlags::ImapOtherUser) {
5570 folderTypeStringID = "imapOtherUsersFolderTypeName";
5571 nsCString owner;
5572 nsString uniOwner;
5573 GetFolderOwnerUserName(owner);
5574 if (owner.IsEmpty()) {
5575 rv = IMAPGetStringByName(folderTypeStringID, getter_Copies(uniOwner));
5576 // Another user's folder, for which we couldn't find an owner name
5577 NS_ASSERTION(false, "couldn't get owner name for other user's folder");
5578 } else {
5579 // is this right? It doesn't leak, does it?
5580 CopyUTF8toUTF16(owner, uniOwner);
5581 }
5582 AutoTArray<nsString, 1> params = {uniOwner};
5583 rv = bundle->FormatStringFromName("imapOtherUsersFolderTypeDescription",
5584 params, folderTypeDesc);
5585 } else if (GetFolderACL()->GetIsFolderShared()) {
5586 folderTypeStringID = "imapPersonalSharedFolderTypeName";
5587 folderTypeDescStringID = "imapPersonalSharedFolderTypeDescription";
5588 } else {
5589 folderTypeStringID = "imapPersonalSharedFolderTypeName";
5590 folderTypeDescStringID = "imapPersonalFolderTypeDescription";
5591 }
5592
5593 rv = IMAPGetStringByName(folderTypeStringID, getter_Copies(folderType));
5594 if (NS_SUCCEEDED(rv)) aFolderProps->SetFolderType(folderType);
5595
5596 if (folderTypeDesc.IsEmpty() && folderTypeDescStringID)
5597 rv = IMAPGetStringByName(folderTypeDescStringID,
5598 getter_Copies(folderTypeDesc));
5599 if (!folderTypeDesc.IsEmpty())
5600 aFolderProps->SetFolderTypeDescription(folderTypeDesc);
5601
5602 nsString rightsString;
5603 rv = CreateACLRightsStringForFolder(rightsString);
5604 if (NS_SUCCEEDED(rv)) aFolderProps->SetFolderPermissions(rightsString);
5605 return NS_OK;
5606 }
5607
SetAclFlags(uint32_t aclFlags)5608 NS_IMETHODIMP nsImapMailFolder::SetAclFlags(uint32_t aclFlags) {
5609 nsresult rv = NS_OK;
5610 if (m_aclFlags != aclFlags) {
5611 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
5612 bool dbWasOpen = (mDatabase != nullptr);
5613 rv = GetDatabase();
5614
5615 m_aclFlags = aclFlags;
5616 if (mDatabase) {
5617 rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
5618 if (NS_SUCCEEDED(rv) && dbFolderInfo)
5619 dbFolderInfo->SetUint32Property("aclFlags", aclFlags);
5620 // if setting the acl flags caused us to open the db, release the ref
5621 // because on startup, we might get acl on all folders,which will
5622 // leave a lot of db's open.
5623 if (!dbWasOpen) {
5624 mDatabase->Close(true /* commit changes */);
5625 mDatabase = nullptr;
5626 }
5627 }
5628 }
5629 return rv;
5630 }
5631
GetAclFlags(uint32_t * aclFlags)5632 NS_IMETHODIMP nsImapMailFolder::GetAclFlags(uint32_t* aclFlags) {
5633 NS_ENSURE_ARG_POINTER(aclFlags);
5634 nsresult rv;
5635 ReadDBFolderInfo(false); // update cache first.
5636 if (m_aclFlags == kAclInvalid) // -1 means invalid value, so get it from db.
5637 {
5638 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
5639 bool dbWasOpen = (mDatabase != nullptr);
5640 rv = GetDatabase();
5641
5642 if (mDatabase) {
5643 rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
5644 if (NS_SUCCEEDED(rv) && dbFolderInfo) {
5645 rv = dbFolderInfo->GetUint32Property("aclFlags", 0, aclFlags);
5646 m_aclFlags = *aclFlags;
5647 }
5648 // if getting the acl flags caused us to open the db, release the ref
5649 // because on startup, we might get acl on all folders,which will
5650 // leave a lot of db's open.
5651 if (!dbWasOpen) {
5652 mDatabase->Close(true /* commit changes */);
5653 mDatabase = nullptr;
5654 }
5655 }
5656 } else
5657 *aclFlags = m_aclFlags;
5658 return NS_OK;
5659 }
5660
SetSupportedUserFlags(uint32_t userFlags)5661 nsresult nsImapMailFolder::SetSupportedUserFlags(uint32_t userFlags) {
5662 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
5663 nsresult rv = GetDatabase();
5664
5665 m_supportedUserFlags = userFlags;
5666 if (mDatabase) {
5667 rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
5668 if (NS_SUCCEEDED(rv) && dbFolderInfo)
5669 dbFolderInfo->SetUint32Property("imapFlags", userFlags);
5670 }
5671 return rv;
5672 }
5673
GetSupportedUserFlags(uint32_t * userFlags)5674 nsresult nsImapMailFolder::GetSupportedUserFlags(uint32_t* userFlags) {
5675 NS_ENSURE_ARG_POINTER(userFlags);
5676
5677 nsresult rv = NS_OK;
5678
5679 ReadDBFolderInfo(false); // update cache first.
5680 if (m_supportedUserFlags == 0) // 0 means invalid value, so get it from db.
5681 {
5682 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
5683 rv = GetDatabase();
5684
5685 if (mDatabase) {
5686 rv = mDatabase->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
5687 if (NS_SUCCEEDED(rv) && dbFolderInfo) {
5688 rv = dbFolderInfo->GetUint32Property("imapFlags", 0, userFlags);
5689 m_supportedUserFlags = *userFlags;
5690 }
5691 }
5692 } else
5693 *userFlags = m_supportedUserFlags;
5694 return rv;
5695 }
5696
GetCanOpenFolder(bool * aBool)5697 NS_IMETHODIMP nsImapMailFolder::GetCanOpenFolder(bool* aBool) {
5698 NS_ENSURE_ARG_POINTER(aBool);
5699 bool noSelect;
5700 GetFlag(nsMsgFolderFlags::ImapNoselect, &noSelect);
5701 *aBool = (noSelect) ? false : GetFolderACL()->GetCanIReadFolder();
5702 return NS_OK;
5703 }
5704
5705 ///////// nsMsgIMAPFolderACL class ///////////////////////////////
5706
5707 // This string is defined in the ACL RFC to be "anyone"
5708 #define IMAP_ACL_ANYONE_STRING "anyone"
5709
nsMsgIMAPFolderACL(nsImapMailFolder * folder)5710 nsMsgIMAPFolderACL::nsMsgIMAPFolderACL(nsImapMailFolder* folder)
5711 : m_rightsHash(24) {
5712 NS_ASSERTION(folder, "need folder");
5713 m_folder = folder;
5714 m_aclCount = 0;
5715 BuildInitialACLFromCache();
5716 }
5717
~nsMsgIMAPFolderACL()5718 nsMsgIMAPFolderACL::~nsMsgIMAPFolderACL() {}
5719
5720 // We cache most of our own rights in the MSG_FOLDER_PREF_* flags
BuildInitialACLFromCache()5721 void nsMsgIMAPFolderACL::BuildInitialACLFromCache() {
5722 nsAutoCString myrights;
5723
5724 uint32_t startingFlags;
5725 m_folder->GetAclFlags(&startingFlags);
5726
5727 if (startingFlags & IMAP_ACL_READ_FLAG) myrights += "r";
5728 if (startingFlags & IMAP_ACL_STORE_SEEN_FLAG) myrights += "s";
5729 if (startingFlags & IMAP_ACL_WRITE_FLAG) myrights += "w";
5730 if (startingFlags & IMAP_ACL_INSERT_FLAG) myrights += "i";
5731 if (startingFlags & IMAP_ACL_POST_FLAG) myrights += "p";
5732 if (startingFlags & IMAP_ACL_CREATE_SUBFOLDER_FLAG) myrights += "c";
5733 if (startingFlags & IMAP_ACL_DELETE_FLAG) myrights += "dt";
5734 if (startingFlags & IMAP_ACL_ADMINISTER_FLAG) myrights += "a";
5735 if (startingFlags & IMAP_ACL_EXPUNGE_FLAG) myrights += "e";
5736
5737 if (!myrights.IsEmpty()) SetFolderRightsForUser(EmptyCString(), myrights);
5738 }
5739
UpdateACLCache()5740 void nsMsgIMAPFolderACL::UpdateACLCache() {
5741 uint32_t startingFlags = 0;
5742 m_folder->GetAclFlags(&startingFlags);
5743
5744 if (GetCanIReadFolder())
5745 startingFlags |= IMAP_ACL_READ_FLAG;
5746 else
5747 startingFlags &= ~IMAP_ACL_READ_FLAG;
5748
5749 if (GetCanIStoreSeenInFolder())
5750 startingFlags |= IMAP_ACL_STORE_SEEN_FLAG;
5751 else
5752 startingFlags &= ~IMAP_ACL_STORE_SEEN_FLAG;
5753
5754 if (GetCanIWriteFolder())
5755 startingFlags |= IMAP_ACL_WRITE_FLAG;
5756 else
5757 startingFlags &= ~IMAP_ACL_WRITE_FLAG;
5758
5759 if (GetCanIInsertInFolder())
5760 startingFlags |= IMAP_ACL_INSERT_FLAG;
5761 else
5762 startingFlags &= ~IMAP_ACL_INSERT_FLAG;
5763
5764 if (GetCanIPostToFolder())
5765 startingFlags |= IMAP_ACL_POST_FLAG;
5766 else
5767 startingFlags &= ~IMAP_ACL_POST_FLAG;
5768
5769 if (GetCanICreateSubfolder())
5770 startingFlags |= IMAP_ACL_CREATE_SUBFOLDER_FLAG;
5771 else
5772 startingFlags &= ~IMAP_ACL_CREATE_SUBFOLDER_FLAG;
5773
5774 if (GetCanIDeleteInFolder())
5775 startingFlags |= IMAP_ACL_DELETE_FLAG;
5776 else
5777 startingFlags &= ~IMAP_ACL_DELETE_FLAG;
5778
5779 if (GetCanIAdministerFolder())
5780 startingFlags |= IMAP_ACL_ADMINISTER_FLAG;
5781 else
5782 startingFlags &= ~IMAP_ACL_ADMINISTER_FLAG;
5783
5784 if (GetCanIExpungeFolder())
5785 startingFlags |= IMAP_ACL_EXPUNGE_FLAG;
5786 else
5787 startingFlags &= ~IMAP_ACL_EXPUNGE_FLAG;
5788
5789 m_folder->SetAclFlags(startingFlags);
5790 }
5791
SetFolderRightsForUser(const nsACString & userName,const nsACString & rights)5792 bool nsMsgIMAPFolderACL::SetFolderRightsForUser(const nsACString& userName,
5793 const nsACString& rights) {
5794 nsCString myUserName;
5795 nsCOMPtr<nsIMsgIncomingServer> server;
5796 nsresult rv = m_folder->GetServer(getter_AddRefs(server));
5797 NS_ENSURE_SUCCESS(rv, false);
5798
5799 // we need the real user name to match with what the imap server returns
5800 // in the acl response.
5801 server->GetRealUsername(myUserName);
5802
5803 nsAutoCString ourUserName;
5804 if (userName.IsEmpty())
5805 ourUserName.Assign(myUserName);
5806 else
5807 ourUserName.Assign(userName);
5808
5809 if (ourUserName.IsEmpty()) return false;
5810
5811 ToLowerCase(ourUserName);
5812 nsCString oldValue = m_rightsHash.Get(ourUserName);
5813 if (!oldValue.IsEmpty()) {
5814 m_rightsHash.Remove(ourUserName);
5815 m_aclCount--;
5816 NS_ASSERTION(m_aclCount >= 0, "acl count can't go negative");
5817 }
5818 m_aclCount++;
5819 m_rightsHash.InsertOrUpdate(ourUserName, PromiseFlatCString(rights));
5820
5821 if (myUserName.Equals(ourUserName) ||
5822 ourUserName.EqualsLiteral(IMAP_ACL_ANYONE_STRING))
5823 // if this is setting an ACL for me, cache it in the folder pref flags
5824 UpdateACLCache();
5825
5826 return true;
5827 }
5828
GetOtherUsersWithAccess(nsIUTF8StringEnumerator ** aResult)5829 NS_IMETHODIMP nsImapMailFolder::GetOtherUsersWithAccess(
5830 nsIUTF8StringEnumerator** aResult) {
5831 return GetFolderACL()->GetOtherUsers(aResult);
5832 }
5833
GetOtherUsers(nsIUTF8StringEnumerator ** aResult)5834 nsresult nsMsgIMAPFolderACL::GetOtherUsers(nsIUTF8StringEnumerator** aResult) {
5835 nsCString myUserName;
5836 nsCOMPtr<nsIMsgIncomingServer> server;
5837 nsresult rv = m_folder->GetServer(getter_AddRefs(server));
5838 NS_ENSURE_SUCCESS(rv, rv);
5839 server->GetRealUsername(myUserName);
5840
5841 // We need to filter out myUserName from m_rightsHash.
5842 nsTArray<nsCString>* resultArray = new nsTArray<nsCString>;
5843 for (auto iter = m_rightsHash.Iter(); !iter.Done(); iter.Next()) {
5844 if (!iter.Key().Equals(myUserName)) resultArray->AppendElement(iter.Key());
5845 }
5846
5847 // enumerator will free resultArray
5848 return NS_NewAdoptingUTF8StringEnumerator(aResult, resultArray);
5849 }
5850
GetPermissionsForUser(const nsACString & otherUser,nsACString & aResult)5851 nsresult nsImapMailFolder::GetPermissionsForUser(const nsACString& otherUser,
5852 nsACString& aResult) {
5853 nsCString str;
5854 nsresult rv = GetFolderACL()->GetRightsStringForUser(otherUser, str);
5855 NS_ENSURE_SUCCESS(rv, rv);
5856 aResult = str;
5857 return NS_OK;
5858 }
5859
GetRightsStringForUser(const nsACString & inUserName,nsCString & rights)5860 nsresult nsMsgIMAPFolderACL::GetRightsStringForUser(
5861 const nsACString& inUserName, nsCString& rights) {
5862 nsCString userName;
5863 userName.Assign(inUserName);
5864 if (userName.IsEmpty()) {
5865 nsCOMPtr<nsIMsgIncomingServer> server;
5866
5867 nsresult rv = m_folder->GetServer(getter_AddRefs(server));
5868 NS_ENSURE_SUCCESS(rv, rv);
5869 // we need the real user name to match with what the imap server returns
5870 // in the acl response.
5871 server->GetRealUsername(userName);
5872 }
5873 ToLowerCase(userName);
5874 rights = m_rightsHash.Get(userName);
5875 return NS_OK;
5876 }
5877
5878 // First looks for individual user; then looks for 'anyone' if the user isn't
5879 // found. Returns defaultIfNotFound, if neither are found.
GetFlagSetInRightsForUser(const nsACString & userName,char flag,bool defaultIfNotFound)5880 bool nsMsgIMAPFolderACL::GetFlagSetInRightsForUser(const nsACString& userName,
5881 char flag,
5882 bool defaultIfNotFound) {
5883 nsCString flags;
5884 nsresult rv = GetRightsStringForUser(userName, flags);
5885 NS_ENSURE_SUCCESS(rv, defaultIfNotFound);
5886 if (flags.IsEmpty()) {
5887 nsCString anyoneFlags;
5888 GetRightsStringForUser(nsLiteralCString(IMAP_ACL_ANYONE_STRING),
5889 anyoneFlags);
5890 if (anyoneFlags.IsEmpty()) return defaultIfNotFound;
5891 return (anyoneFlags.FindChar(flag) != kNotFound);
5892 }
5893 return (flags.FindChar(flag) != kNotFound);
5894 }
5895
GetCanUserLookupFolder(const nsACString & userName)5896 bool nsMsgIMAPFolderACL::GetCanUserLookupFolder(const nsACString& userName) {
5897 return GetFlagSetInRightsForUser(userName, 'l', false);
5898 }
5899
GetCanUserReadFolder(const nsACString & userName)5900 bool nsMsgIMAPFolderACL::GetCanUserReadFolder(const nsACString& userName) {
5901 return GetFlagSetInRightsForUser(userName, 'r', false);
5902 }
5903
GetCanUserStoreSeenInFolder(const nsACString & userName)5904 bool nsMsgIMAPFolderACL::GetCanUserStoreSeenInFolder(
5905 const nsACString& userName) {
5906 return GetFlagSetInRightsForUser(userName, 's', false);
5907 }
5908
GetCanUserWriteFolder(const nsACString & userName)5909 bool nsMsgIMAPFolderACL::GetCanUserWriteFolder(const nsACString& userName) {
5910 return GetFlagSetInRightsForUser(userName, 'w', false);
5911 }
5912
GetCanUserInsertInFolder(const nsACString & userName)5913 bool nsMsgIMAPFolderACL::GetCanUserInsertInFolder(const nsACString& userName) {
5914 return GetFlagSetInRightsForUser(userName, 'i', false);
5915 }
5916
GetCanUserPostToFolder(const nsACString & userName)5917 bool nsMsgIMAPFolderACL::GetCanUserPostToFolder(const nsACString& userName) {
5918 return GetFlagSetInRightsForUser(userName, 'p', false);
5919 }
5920
GetCanUserCreateSubfolder(const nsACString & userName)5921 bool nsMsgIMAPFolderACL::GetCanUserCreateSubfolder(const nsACString& userName) {
5922 return GetFlagSetInRightsForUser(userName, 'c', false);
5923 }
5924
GetCanUserDeleteInFolder(const nsACString & userName)5925 bool nsMsgIMAPFolderACL::GetCanUserDeleteInFolder(const nsACString& userName) {
5926 return GetFlagSetInRightsForUser(userName, 'd', false) ||
5927 GetFlagSetInRightsForUser(userName, 't', false);
5928 }
5929
GetCanUserAdministerFolder(const nsACString & userName)5930 bool nsMsgIMAPFolderACL::GetCanUserAdministerFolder(
5931 const nsACString& userName) {
5932 return GetFlagSetInRightsForUser(userName, 'a', false);
5933 }
5934
GetCanILookupFolder()5935 bool nsMsgIMAPFolderACL::GetCanILookupFolder() {
5936 return GetFlagSetInRightsForUser(EmptyCString(), 'l', true);
5937 }
5938
GetCanIReadFolder()5939 bool nsMsgIMAPFolderACL::GetCanIReadFolder() {
5940 return GetFlagSetInRightsForUser(EmptyCString(), 'r', true);
5941 }
5942
GetCanIStoreSeenInFolder()5943 bool nsMsgIMAPFolderACL::GetCanIStoreSeenInFolder() {
5944 return GetFlagSetInRightsForUser(EmptyCString(), 's', true);
5945 }
5946
GetCanIWriteFolder()5947 bool nsMsgIMAPFolderACL::GetCanIWriteFolder() {
5948 return GetFlagSetInRightsForUser(EmptyCString(), 'w', true);
5949 }
5950
GetCanIInsertInFolder()5951 bool nsMsgIMAPFolderACL::GetCanIInsertInFolder() {
5952 return GetFlagSetInRightsForUser(EmptyCString(), 'i', true);
5953 }
5954
GetCanIPostToFolder()5955 bool nsMsgIMAPFolderACL::GetCanIPostToFolder() {
5956 return GetFlagSetInRightsForUser(EmptyCString(), 'p', true);
5957 }
5958
GetCanICreateSubfolder()5959 bool nsMsgIMAPFolderACL::GetCanICreateSubfolder() {
5960 return GetFlagSetInRightsForUser(EmptyCString(), 'c', true);
5961 }
5962
GetCanIDeleteInFolder()5963 bool nsMsgIMAPFolderACL::GetCanIDeleteInFolder() {
5964 return GetFlagSetInRightsForUser(EmptyCString(), 'd', true) ||
5965 GetFlagSetInRightsForUser(EmptyCString(), 't', true);
5966 }
5967
GetCanIAdministerFolder()5968 bool nsMsgIMAPFolderACL::GetCanIAdministerFolder() {
5969 return GetFlagSetInRightsForUser(EmptyCString(), 'a', true);
5970 }
5971
GetCanIExpungeFolder()5972 bool nsMsgIMAPFolderACL::GetCanIExpungeFolder() {
5973 return GetFlagSetInRightsForUser(EmptyCString(), 'e', true) ||
5974 GetFlagSetInRightsForUser(EmptyCString(), 'd', true);
5975 }
5976
5977 // We use this to see if the ACLs think a folder is shared or not.
5978 // We will define "Shared" in 5.0 to mean:
5979 // At least one user other than the currently authenticated user has at least
5980 // one explicitly-listed ACL right on that folder.
GetIsFolderShared()5981 bool nsMsgIMAPFolderACL::GetIsFolderShared() {
5982 // If we have more than one ACL count for this folder, which means that
5983 // someone other than ourself has rights on it, then it is "shared."
5984 if (m_aclCount > 1) return true;
5985
5986 // Or, if "anyone" has rights to it, it is shared.
5987 nsCString anyonesRights =
5988 m_rightsHash.Get(nsLiteralCString(IMAP_ACL_ANYONE_STRING));
5989 return (!anyonesRights.IsEmpty());
5990 }
5991
GetDoIHaveFullRightsForFolder()5992 bool nsMsgIMAPFolderACL::GetDoIHaveFullRightsForFolder() {
5993 return (GetCanIReadFolder() && GetCanIWriteFolder() &&
5994 GetCanIInsertInFolder() && GetCanIAdministerFolder() &&
5995 GetCanICreateSubfolder() && GetCanIDeleteInFolder() &&
5996 GetCanILookupFolder() && GetCanIStoreSeenInFolder() &&
5997 GetCanIExpungeFolder() && GetCanIPostToFolder());
5998 }
5999
6000 // Returns a newly allocated string describing these rights
CreateACLRightsString(nsAString & aRightsString)6001 nsresult nsMsgIMAPFolderACL::CreateACLRightsString(nsAString& aRightsString) {
6002 nsString curRight;
6003 nsCOMPtr<nsIStringBundle> bundle;
6004 nsresult rv = IMAPGetStringBundle(getter_AddRefs(bundle));
6005 NS_ENSURE_SUCCESS(rv, rv);
6006
6007 if (GetDoIHaveFullRightsForFolder()) {
6008 nsAutoString result;
6009 rv = bundle->GetStringFromName("imapAclFullRights", result);
6010 aRightsString.Assign(result);
6011 return rv;
6012 }
6013
6014 if (GetCanIReadFolder()) {
6015 bundle->GetStringFromName("imapAclReadRight", curRight);
6016 aRightsString.Append(curRight);
6017 }
6018 if (GetCanIWriteFolder()) {
6019 if (!aRightsString.IsEmpty()) aRightsString.AppendLiteral(", ");
6020 bundle->GetStringFromName("imapAclWriteRight", curRight);
6021 aRightsString.Append(curRight);
6022 }
6023 if (GetCanIInsertInFolder()) {
6024 if (!aRightsString.IsEmpty()) aRightsString.AppendLiteral(", ");
6025 bundle->GetStringFromName("imapAclInsertRight", curRight);
6026 aRightsString.Append(curRight);
6027 }
6028 if (GetCanILookupFolder()) {
6029 if (!aRightsString.IsEmpty()) aRightsString.AppendLiteral(", ");
6030 bundle->GetStringFromName("imapAclLookupRight", curRight);
6031 aRightsString.Append(curRight);
6032 }
6033 if (GetCanIStoreSeenInFolder()) {
6034 if (!aRightsString.IsEmpty()) aRightsString.AppendLiteral(", ");
6035 bundle->GetStringFromName("imapAclSeenRight", curRight);
6036 aRightsString.Append(curRight);
6037 }
6038 if (GetCanIDeleteInFolder()) {
6039 if (!aRightsString.IsEmpty()) aRightsString.AppendLiteral(", ");
6040 bundle->GetStringFromName("imapAclDeleteRight", curRight);
6041 aRightsString.Append(curRight);
6042 }
6043 if (GetCanIExpungeFolder()) {
6044 if (!aRightsString.IsEmpty()) aRightsString.AppendLiteral(", ");
6045 bundle->GetStringFromName("imapAclExpungeRight", curRight);
6046 aRightsString.Append(curRight);
6047 }
6048 if (GetCanICreateSubfolder()) {
6049 if (!aRightsString.IsEmpty()) aRightsString.AppendLiteral(", ");
6050 bundle->GetStringFromName("imapAclCreateRight", curRight);
6051 aRightsString.Append(curRight);
6052 }
6053 if (GetCanIPostToFolder()) {
6054 if (!aRightsString.IsEmpty()) aRightsString.AppendLiteral(", ");
6055 bundle->GetStringFromName("imapAclPostRight", curRight);
6056 aRightsString.Append(curRight);
6057 }
6058 if (GetCanIAdministerFolder()) {
6059 if (!aRightsString.IsEmpty()) aRightsString.AppendLiteral(", ");
6060 bundle->GetStringFromName("imapAclAdministerRight", curRight);
6061 aRightsString.Append(curRight);
6062 }
6063 return rv;
6064 }
6065
GetFilePath(nsIFile ** aPathName)6066 NS_IMETHODIMP nsImapMailFolder::GetFilePath(nsIFile** aPathName) {
6067 // this will return a copy of mPath, which is what we want.
6068 // this will also initialize mPath using parseURI if it isn't already done
6069 return nsMsgDBFolder::GetFilePath(aPathName);
6070 }
6071
SetFilePath(nsIFile * aPathName)6072 NS_IMETHODIMP nsImapMailFolder::SetFilePath(nsIFile* aPathName) {
6073 return nsMsgDBFolder::SetFilePath(
6074 aPathName); // call base class so mPath will get set
6075 }
6076
DisplayStatusMsg(nsIImapUrl * aImapUrl,const nsAString & msg)6077 nsresult nsImapMailFolder::DisplayStatusMsg(nsIImapUrl* aImapUrl,
6078 const nsAString& msg) {
6079 nsCOMPtr<nsIImapMockChannel> mockChannel;
6080 aImapUrl->GetMockChannel(getter_AddRefs(mockChannel));
6081 if (mockChannel) {
6082 nsCOMPtr<nsIProgressEventSink> progressSink;
6083 mockChannel->GetProgressEventSink(getter_AddRefs(progressSink));
6084 if (progressSink) {
6085 progressSink->OnStatus(mockChannel, NS_OK,
6086 PromiseFlatString(msg).get()); // XXX i18n message
6087 }
6088 }
6089 return NS_OK;
6090 }
6091
6092 NS_IMETHODIMP
ProgressStatusString(nsIImapProtocol * aProtocol,const char * aMsgName,const char16_t * extraInfo)6093 nsImapMailFolder::ProgressStatusString(nsIImapProtocol* aProtocol,
6094 const char* aMsgName,
6095 const char16_t* extraInfo) {
6096 nsString progressMsg;
6097
6098 nsCOMPtr<nsIMsgIncomingServer> server;
6099 nsresult rv = GetServer(getter_AddRefs(server));
6100 if (NS_SUCCEEDED(rv) && server) {
6101 nsCOMPtr<nsIImapServerSink> serverSink = do_QueryInterface(server);
6102 if (serverSink) serverSink->GetImapStringByName(aMsgName, progressMsg);
6103 }
6104 if (progressMsg.IsEmpty())
6105 IMAPGetStringByName(aMsgName, getter_Copies(progressMsg));
6106
6107 if (aProtocol && !progressMsg.IsEmpty()) {
6108 nsCOMPtr<nsIImapUrl> imapUrl;
6109 aProtocol->GetRunningImapURL(getter_AddRefs(imapUrl));
6110 if (imapUrl) {
6111 if (extraInfo) {
6112 nsString printfString;
6113 nsTextFormatter::ssprintf(printfString, progressMsg.get(), extraInfo);
6114 progressMsg = printfString;
6115 }
6116
6117 DisplayStatusMsg(imapUrl, progressMsg);
6118 }
6119 }
6120 return NS_OK;
6121 }
6122
6123 NS_IMETHODIMP
PercentProgress(nsIImapProtocol * aProtocol,nsACString const & aFmtStringName,nsAString const & aMailboxName,int64_t aCurrentProgress,int64_t aMaxProgress)6124 nsImapMailFolder::PercentProgress(nsIImapProtocol* aProtocol,
6125 nsACString const& aFmtStringName,
6126 nsAString const& aMailboxName,
6127 int64_t aCurrentProgress,
6128 int64_t aMaxProgress) {
6129 if (aProtocol) {
6130 nsCOMPtr<nsIImapUrl> imapUrl;
6131 aProtocol->GetRunningImapURL(getter_AddRefs(imapUrl));
6132 if (imapUrl) {
6133 nsCOMPtr<nsIImapMockChannel> mockChannel;
6134 imapUrl->GetMockChannel(getter_AddRefs(mockChannel));
6135 if (mockChannel) {
6136 nsCOMPtr<nsIProgressEventSink> progressSink;
6137 mockChannel->GetProgressEventSink(getter_AddRefs(progressSink));
6138 if (progressSink) {
6139 progressSink->OnProgress(mockChannel, aCurrentProgress, aMaxProgress);
6140
6141 if (!aFmtStringName.IsEmpty()) {
6142 // There's a progress message to format (the progress messages are
6143 // all localized and expect three params).
6144 nsAutoString current;
6145 current.AppendInt(aCurrentProgress);
6146 nsAutoString expected;
6147 expected.AppendInt(aMaxProgress);
6148 nsAutoString mailbox(aMailboxName);
6149 AutoTArray<nsString, 3> params = {current, expected, mailbox};
6150
6151 nsCOMPtr<nsIStringBundle> bundle;
6152 nsresult rv = IMAPGetStringBundle(getter_AddRefs(bundle));
6153 NS_ENSURE_SUCCESS(rv, rv);
6154
6155 nsString progressText;
6156 rv = bundle->FormatStringFromName(
6157 PromiseFlatCString(aFmtStringName).get(), params, progressText);
6158 NS_ENSURE_SUCCESS(rv, rv);
6159 if (!progressText.IsEmpty()) {
6160 progressSink->OnStatus(mockChannel, NS_OK, progressText.get());
6161 }
6162 }
6163 }
6164 }
6165 }
6166 }
6167 return NS_OK;
6168 }
6169
6170 NS_IMETHODIMP
CopyNextStreamMessage(bool copySucceeded,nsISupports * copyState)6171 nsImapMailFolder::CopyNextStreamMessage(bool copySucceeded,
6172 nsISupports* copyState) {
6173 // if copy has failed it could be either user interrupted it or for some other
6174 // reason don't do any subsequent copies or delete src messages if it is move
6175 if (!copySucceeded) return NS_OK;
6176 nsresult rv;
6177 nsCOMPtr<nsImapMailCopyState> mailCopyState =
6178 do_QueryInterface(copyState, &rv);
6179 if (NS_FAILED(rv)) {
6180 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
6181 ("QI copyState failed: %" PRIx32, static_cast<uint32_t>(rv)));
6182 return rv; // this can fail...
6183 }
6184
6185 if (!mailCopyState->m_streamCopy) return NS_OK;
6186
6187 if (mailCopyState->m_curIndex < mailCopyState->m_messages.Length()) {
6188 MOZ_LOG(
6189 IMAP, mozilla::LogLevel::Info,
6190 ("CopyNextStreamMessage: Copying %u of %u", mailCopyState->m_curIndex,
6191 (uint32_t)mailCopyState->m_messages.Length()));
6192 nsIMsgDBHdr* message = mailCopyState->m_messages[mailCopyState->m_curIndex];
6193 bool isRead;
6194 message->GetIsRead(&isRead);
6195 mailCopyState->m_unreadCount = (isRead) ? 0 : 1;
6196 rv = CopyStreamMessage(message, this, mailCopyState->m_msgWindow,
6197 mailCopyState->m_isMove);
6198 } else {
6199 // Notify of move/copy completion in case we have some source headers
6200 nsCOMPtr<nsIMsgFolderNotificationService> notifier(
6201 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
6202 if (notifier && !mailCopyState->m_messages.IsEmpty()) {
6203 notifier->NotifyMsgsMoveCopyCompleted(
6204 mailCopyState->m_isMove, mailCopyState->m_messages, this, {});
6205 }
6206 if (mailCopyState->m_isMove) {
6207 nsCOMPtr<nsIMsgFolder> srcFolder(
6208 do_QueryInterface(mailCopyState->m_srcSupport, &rv));
6209 if (NS_SUCCEEDED(rv) && srcFolder) {
6210 srcFolder->DeleteMessages(mailCopyState->m_messages, nullptr, true,
6211 true, nullptr, false);
6212 // we want to send this notification after the source messages have
6213 // been deleted.
6214 nsCOMPtr<nsIMsgLocalMailFolder> popFolder(do_QueryInterface(srcFolder));
6215 if (popFolder) // needed if move pop->imap to notify FE
6216 srcFolder->NotifyFolderEvent(kDeleteOrMoveMsgCompleted);
6217 }
6218 }
6219 }
6220 if (NS_FAILED(rv)) (void)OnCopyCompleted(mailCopyState->m_srcSupport, rv);
6221
6222 return rv;
6223 }
6224
6225 NS_IMETHODIMP
SetUrlState(nsIImapProtocol * aProtocol,nsIMsgMailNewsUrl * aUrl,bool isRunning,bool aSuspend,nsresult statusCode)6226 nsImapMailFolder::SetUrlState(nsIImapProtocol* aProtocol,
6227 nsIMsgMailNewsUrl* aUrl, bool isRunning,
6228 bool aSuspend, nsresult statusCode) {
6229 // If we have no path, then the folder has been shutdown, and there's
6230 // no point in doing anything...
6231 if (!mPath) return NS_OK;
6232 if (!isRunning) {
6233 ProgressStatusString(aProtocol, "imapDone", nullptr);
6234 m_urlRunning = false;
6235 // if no protocol, then we're reading from the mem or disk cache
6236 // and we don't want to end the offline download just yet.
6237 if (aProtocol) {
6238 EndOfflineDownload();
6239 m_downloadingFolderForOfflineUse = false;
6240 }
6241 nsCOMPtr<nsIImapUrl> imapUrl(do_QueryInterface(aUrl));
6242 if (imapUrl) {
6243 nsImapAction imapAction;
6244 imapUrl->GetImapAction(&imapAction);
6245 // if the server doesn't support copyUID, then SetCopyResponseUid won't
6246 // get called, so we need to clear m_pendingOfflineMoves when the online
6247 // move operation has finished.
6248 if (imapAction == nsIImapUrl::nsImapOnlineMove)
6249 m_pendingOfflineMoves.Clear();
6250 }
6251 }
6252 if (aUrl && !aSuspend) return aUrl->SetUrlState(isRunning, statusCode);
6253 return statusCode;
6254 }
6255
6256 // used when copying from local mail folder, or other imap server)
CopyMessagesWithStream(nsIMsgFolder * srcFolder,nsTArray<RefPtr<nsIMsgDBHdr>> const & messages,bool isMove,bool isCrossServerOp,nsIMsgWindow * msgWindow,nsIMsgCopyServiceListener * listener,bool allowUndo)6257 nsresult nsImapMailFolder::CopyMessagesWithStream(
6258 nsIMsgFolder* srcFolder, nsTArray<RefPtr<nsIMsgDBHdr>> const& messages,
6259 bool isMove, bool isCrossServerOp, nsIMsgWindow* msgWindow,
6260 nsIMsgCopyServiceListener* listener, bool allowUndo) {
6261 NS_ENSURE_ARG_POINTER(srcFolder);
6262 nsresult rv;
6263 nsCOMPtr<nsISupports> aSupport(do_QueryInterface(srcFolder, &rv));
6264 NS_ENSURE_SUCCESS(rv, rv);
6265 rv = InitCopyState(aSupport, messages, isMove, false, isCrossServerOp, 0,
6266 EmptyCString(), listener, msgWindow, allowUndo);
6267 if (NS_FAILED(rv)) return rv;
6268
6269 m_copyState->m_streamCopy = true;
6270
6271 // ** jt - needs to create server to server move/copy undo msg txn
6272 if (m_copyState->m_allowUndo) {
6273 nsAutoCString messageIds;
6274 nsTArray<nsMsgKey> srcKeyArray;
6275 rv = BuildIdsAndKeyArray(messages, messageIds, srcKeyArray);
6276
6277 RefPtr<nsImapMoveCopyMsgTxn> undoMsgTxn = new nsImapMoveCopyMsgTxn;
6278
6279 if (!undoMsgTxn ||
6280 NS_FAILED(undoMsgTxn->Init(srcFolder, &srcKeyArray, messageIds.get(),
6281 this, true, isMove)))
6282 return NS_ERROR_OUT_OF_MEMORY;
6283
6284 if (isMove) {
6285 if (mFlags & nsMsgFolderFlags::Trash)
6286 undoMsgTxn->SetTransactionType(nsIMessenger::eDeleteMsg);
6287 else
6288 undoMsgTxn->SetTransactionType(nsIMessenger::eMoveMsg);
6289 } else
6290 undoMsgTxn->SetTransactionType(nsIMessenger::eCopyMsg);
6291 m_copyState->m_undoMsgTxn = undoMsgTxn;
6292 }
6293 if (NS_SUCCEEDED(rv)) CopyStreamMessage(messages[0], this, msgWindow, isMove);
6294 return rv; // we are clearing copy state in CopyMessages on failure
6295 }
6296
GetClearedOriginalOp(nsIMsgOfflineImapOperation * op,nsIMsgOfflineImapOperation ** originalOp,nsIMsgDatabase ** originalDB)6297 nsresult nsImapMailFolder::GetClearedOriginalOp(
6298 nsIMsgOfflineImapOperation* op, nsIMsgOfflineImapOperation** originalOp,
6299 nsIMsgDatabase** originalDB) {
6300 nsCOMPtr<nsIMsgOfflineImapOperation> returnOp;
6301 nsOfflineImapOperationType opType;
6302 op->GetOperation(&opType);
6303 NS_ASSERTION(opType & nsIMsgOfflineImapOperation::kMoveResult,
6304 "not an offline move op");
6305
6306 nsCString sourceFolderURI;
6307 op->GetSourceFolderURI(sourceFolderURI);
6308
6309 nsresult rv;
6310 nsCOMPtr<nsIMsgFolder> sourceFolder;
6311 rv = GetOrCreateFolder(sourceFolderURI, getter_AddRefs(sourceFolder));
6312 NS_ENSURE_SUCCESS(rv, rv);
6313
6314 nsCOMPtr<nsIDBFolderInfo> folderInfo;
6315 sourceFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), originalDB);
6316 if (*originalDB) {
6317 nsMsgKey originalKey;
6318 op->GetMessageKey(&originalKey);
6319 rv = (*originalDB)
6320 ->GetOfflineOpForKey(originalKey, false, getter_AddRefs(returnOp));
6321 if (NS_SUCCEEDED(rv) && returnOp) {
6322 nsCString moveDestination;
6323 nsCString thisFolderURI;
6324 GetURI(thisFolderURI);
6325 returnOp->GetDestinationFolderURI(moveDestination);
6326 if (moveDestination.Equals(thisFolderURI))
6327 returnOp->ClearOperation(nsIMsgOfflineImapOperation::kMoveResult);
6328 }
6329 }
6330 returnOp.forget(originalOp);
6331 return rv;
6332 }
6333
GetOriginalOp(nsIMsgOfflineImapOperation * op,nsIMsgOfflineImapOperation ** originalOp,nsIMsgDatabase ** originalDB)6334 nsresult nsImapMailFolder::GetOriginalOp(
6335 nsIMsgOfflineImapOperation* op, nsIMsgOfflineImapOperation** originalOp,
6336 nsIMsgDatabase** originalDB) {
6337 nsCOMPtr<nsIMsgOfflineImapOperation> returnOp;
6338 nsCString sourceFolderURI;
6339 op->GetSourceFolderURI(sourceFolderURI);
6340
6341 nsresult rv;
6342 nsCOMPtr<nsIMsgFolder> sourceFolder;
6343 rv = GetOrCreateFolder(sourceFolderURI, getter_AddRefs(sourceFolder));
6344 NS_ENSURE_SUCCESS(rv, rv);
6345 nsCOMPtr<nsIDBFolderInfo> folderInfo;
6346 sourceFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), originalDB);
6347 if (*originalDB) {
6348 nsMsgKey originalKey;
6349 op->GetMessageKey(&originalKey);
6350 rv = (*originalDB)
6351 ->GetOfflineOpForKey(originalKey, false, getter_AddRefs(returnOp));
6352 }
6353 returnOp.forget(originalOp);
6354 return rv;
6355 }
6356
CopyOfflineMsgBody(nsIMsgFolder * srcFolder,nsIMsgDBHdr * destHdr,nsIMsgDBHdr * origHdr,nsIInputStream * inputStream,nsIOutputStream * outputStream)6357 nsresult nsImapMailFolder::CopyOfflineMsgBody(nsIMsgFolder* srcFolder,
6358 nsIMsgDBHdr* destHdr,
6359 nsIMsgDBHdr* origHdr,
6360 nsIInputStream* inputStream,
6361 nsIOutputStream* outputStream) {
6362 nsresult rv;
6363 nsCOMPtr<nsISeekableStream> seekable(do_QueryInterface(outputStream, &rv));
6364 NS_ENSURE_SUCCESS(rv, rv);
6365 uint64_t messageOffset;
6366 uint32_t messageSize;
6367 origHdr->GetMessageOffset(&messageOffset);
6368 if (!messageOffset) {
6369 // Some offline stores may contain a bug where the storeToken is set but
6370 // the messageOffset is zero. Detect cases like this, and use storeToken
6371 // to set the missing messageOffset. Note this assumes mbox.
6372 nsCOMPtr<nsIMsgPluggableStore> offlineStore;
6373 (void)GetMsgStore(getter_AddRefs(offlineStore));
6374 if (offlineStore) {
6375 nsAutoCString type;
6376 offlineStore->GetStoreType(type);
6377 if (type.EqualsLiteral("mbox")) {
6378 nsCString storeToken;
6379 origHdr->GetStringProperty("storeToken", getter_Copies(storeToken));
6380 if (!storeToken.IsEmpty())
6381 messageOffset = ParseUint64Str(storeToken.get());
6382 }
6383 }
6384 }
6385 origHdr->GetOfflineMessageSize(&messageSize);
6386 if (!messageSize) {
6387 nsCOMPtr<nsIMsgLocalMailFolder> localFolder = do_QueryInterface(srcFolder);
6388 if (localFolder) // can just use regular message size
6389 origHdr->GetMessageSize(&messageSize);
6390 }
6391 int64_t tellPos;
6392 seekable->Tell(&tellPos);
6393 destHdr->SetMessageOffset(tellPos);
6394 nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(inputStream);
6395 NS_ASSERTION(seekStream, "non seekable stream - can't read from offline msg");
6396 if (seekStream) {
6397 rv = seekStream->Seek(nsISeekableStream::NS_SEEK_SET, messageOffset);
6398 if (NS_SUCCEEDED(rv)) {
6399 // now, copy the dest folder offline store msg to the temp file
6400 char* inputBuffer = (char*)PR_Malloc(FILE_IO_BUFFER_SIZE);
6401 int32_t bytesLeft;
6402 uint32_t bytesRead, bytesWritten;
6403 bytesLeft = messageSize;
6404 rv = (inputBuffer) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
6405 while (bytesLeft > 0 && NS_SUCCEEDED(rv)) {
6406 rv = inputStream->Read(inputBuffer, FILE_IO_BUFFER_SIZE, &bytesRead);
6407 if (NS_SUCCEEDED(rv) && bytesRead > 0) {
6408 rv = outputStream->Write(inputBuffer,
6409 std::min((int32_t)bytesRead, bytesLeft),
6410 &bytesWritten);
6411 NS_ASSERTION(
6412 (int32_t)bytesWritten == std::min((int32_t)bytesRead, bytesLeft),
6413 "wrote out incorrect number of bytes");
6414 } else
6415 break;
6416 bytesLeft -= bytesRead;
6417 }
6418 PR_FREEIF(inputBuffer);
6419 }
6420 }
6421 if (NS_SUCCEEDED(rv)) {
6422 outputStream->Flush();
6423 uint32_t resultFlags;
6424 destHdr->OrFlags(nsMsgMessageFlags::Offline, &resultFlags);
6425 destHdr->SetOfflineMessageSize(messageSize);
6426 }
6427 return rv;
6428 }
6429
FindOpenRange(nsMsgKey & fakeBase,uint32_t srcCount)6430 nsresult nsImapMailFolder::FindOpenRange(nsMsgKey& fakeBase,
6431 uint32_t srcCount) {
6432 nsresult rv = GetDatabase();
6433 NS_ENSURE_SUCCESS(rv, rv);
6434
6435 nsMsgKey newBase = fakeBase - 1;
6436 uint32_t freeCount = 0;
6437 while (freeCount != srcCount && newBase > 0) {
6438 bool containsKey;
6439 if (NS_SUCCEEDED(mDatabase->ContainsKey(newBase, &containsKey)) &&
6440 !containsKey)
6441 freeCount++;
6442 else
6443 freeCount = 0;
6444 newBase--;
6445 }
6446 if (!newBase) return NS_ERROR_FAILURE;
6447 fakeBase = newBase;
6448 return NS_OK;
6449 }
6450
6451 // this imap folder is the destination of an offline move/copy.
6452 // We are either offline, or doing a pseudo-offline delete (where we do an
6453 // offline delete, load the next message, then playback the offline delete).
CopyMessagesOffline(nsIMsgFolder * srcFolder,nsTArray<RefPtr<nsIMsgDBHdr>> const & messages,bool isMove,nsIMsgWindow * msgWindow,nsIMsgCopyServiceListener * listener)6454 nsresult nsImapMailFolder::CopyMessagesOffline(
6455 nsIMsgFolder* srcFolder, nsTArray<RefPtr<nsIMsgDBHdr>> const& messages,
6456 bool isMove, nsIMsgWindow* msgWindow, nsIMsgCopyServiceListener* listener) {
6457 nsresult rv;
6458 nsresult stopit = NS_OK;
6459 nsCOMPtr<nsIMsgDatabase> sourceMailDB;
6460 nsCOMPtr<nsIDBFolderInfo> srcDbFolderInfo;
6461 srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(srcDbFolderInfo),
6462 getter_AddRefs(sourceMailDB));
6463 bool deleteToTrash = false;
6464 bool deleteImmediately = false;
6465 uint32_t srcCount = messages.Length();
6466 nsCOMPtr<nsIImapIncomingServer> imapServer;
6467 rv = GetImapIncomingServer(getter_AddRefs(imapServer));
6468
6469 nsTArray<RefPtr<nsIMsgDBHdr>> msgHdrsCopied;
6470 nsTArray<RefPtr<nsIMsgDBHdr>> destMsgHdrs;
6471
6472 if (NS_SUCCEEDED(rv) && imapServer) {
6473 nsMsgImapDeleteModel deleteModel;
6474 imapServer->GetDeleteModel(&deleteModel);
6475 deleteToTrash = (deleteModel == nsMsgImapDeleteModels::MoveToTrash);
6476 deleteImmediately = (deleteModel == nsMsgImapDeleteModels::DeleteNoTrash);
6477 }
6478
6479 // This array is used only when we are actually removing the messages from the
6480 // source database.
6481 nsTArray<nsMsgKey> keysToDelete(
6482 (isMove && (deleteToTrash || deleteImmediately)) ? srcCount : 0);
6483
6484 if (sourceMailDB) {
6485 // save the future ops in the source DB, if this is not a imap->local
6486 // copy/move
6487 nsCOMPtr<nsITransactionManager> txnMgr;
6488 if (msgWindow) msgWindow->GetTransactionManager(getter_AddRefs(txnMgr));
6489 if (txnMgr) txnMgr->BeginBatch(nullptr);
6490 nsCOMPtr<nsIMsgDatabase> database;
6491 GetMsgDatabase(getter_AddRefs(database));
6492 if (database) {
6493 // get the highest key in the dest db, so we can make up our fake keys
6494 nsMsgKey fakeBase = 1;
6495 nsCOMPtr<nsIDBFolderInfo> folderInfo;
6496 rv = database->GetDBFolderInfo(getter_AddRefs(folderInfo));
6497 NS_ENSURE_SUCCESS(rv, rv);
6498 nsMsgKey highWaterMark = nsMsgKey_None;
6499 folderInfo->GetHighWater(&highWaterMark);
6500 fakeBase += highWaterMark;
6501 nsMsgKey fakeTop = fakeBase + srcCount;
6502 // Check that we have enough room for the fake headers. If fakeTop
6503 // is <= highWaterMark, we've overflowed.
6504 if (fakeTop <= highWaterMark || fakeTop == nsMsgKey_None) {
6505 rv = FindOpenRange(fakeBase, srcCount);
6506 NS_ENSURE_SUCCESS(rv, rv);
6507 }
6508 // N.B. We must not return out of the for loop - we need the matching
6509 // end notifications to be sent.
6510 // We don't need to acquire the semaphor since this is synchronous
6511 // on the UI thread but we should check if the offline store is locked.
6512 bool isLocked;
6513 GetLocked(&isLocked);
6514 nsCOMPtr<nsIInputStream> inputStream;
6515 bool reusable = false;
6516 nsCOMPtr<nsIOutputStream> outputStream;
6517 nsTArray<nsMsgKey> addedKeys;
6518 nsTArray<nsMsgKey> srcKeyArray;
6519 nsCOMArray<nsIMsgDBHdr> addedHdrs;
6520 nsCOMArray<nsIMsgDBHdr> srcMsgs;
6521 nsOfflineImapOperationType moveCopyOpType;
6522 nsOfflineImapOperationType deleteOpType =
6523 nsIMsgOfflineImapOperation::kDeletedMsg;
6524 if (!deleteToTrash)
6525 deleteOpType = nsIMsgOfflineImapOperation::kMsgMarkedDeleted;
6526 nsCString messageIds;
6527 rv = BuildIdsAndKeyArray(messages, messageIds, srcKeyArray);
6528 // put fake message in destination db, delete source if move
6529 EnableNotifications(nsIMsgFolder::allMessageCountNotifications, false);
6530 for (uint32_t sourceKeyIndex = 0;
6531 NS_SUCCEEDED(stopit) && (sourceKeyIndex < srcCount);
6532 sourceKeyIndex++) {
6533 bool messageReturningHome = false;
6534 nsCString originalSrcFolderURI;
6535 srcFolder->GetURI(originalSrcFolderURI);
6536 RefPtr<nsIMsgDBHdr> message = messages[sourceKeyIndex];
6537 nsMsgKey originalKey;
6538 if (message)
6539 rv = message->GetMessageKey(&originalKey);
6540 else {
6541 NS_ERROR("bad msg in src array");
6542 continue;
6543 }
6544 nsCOMPtr<nsIMsgOfflineImapOperation> sourceOp;
6545 rv = sourceMailDB->GetOfflineOpForKey(originalKey, true,
6546 getter_AddRefs(sourceOp));
6547 if (NS_SUCCEEDED(rv) && sourceOp) {
6548 srcFolder->SetFlag(nsMsgFolderFlags::OfflineEvents);
6549 nsCOMPtr<nsIMsgDatabase> originalDB;
6550 nsOfflineImapOperationType opType;
6551 sourceOp->GetOperation(&opType);
6552 // if we already have an offline op for this key, then we need to see
6553 // if it was moved into the source folder while offline
6554 if (opType ==
6555 nsIMsgOfflineImapOperation::kMoveResult) // offline move
6556 {
6557 // gracious me, we are moving something we already moved while
6558 // offline! find the original operation and clear it!
6559 nsCOMPtr<nsIMsgOfflineImapOperation> originalOp;
6560 rv = GetClearedOriginalOp(sourceOp, getter_AddRefs(originalOp),
6561 getter_AddRefs(originalDB));
6562 if (originalOp) {
6563 nsCString srcFolderURI;
6564 srcFolder->GetURI(srcFolderURI);
6565 sourceOp->GetSourceFolderURI(originalSrcFolderURI);
6566 sourceOp->GetMessageKey(&originalKey);
6567 if (isMove) sourceMailDB->RemoveOfflineOp(sourceOp);
6568 sourceOp = originalOp;
6569 if (originalSrcFolderURI.Equals(srcFolderURI)) {
6570 messageReturningHome = true;
6571 originalDB->RemoveOfflineOp(originalOp);
6572 }
6573 }
6574 }
6575 if (!messageReturningHome) {
6576 nsCString folderURI;
6577 GetURI(folderURI);
6578 if (isMove) {
6579 uint32_t msgSize;
6580 uint32_t msgFlags;
6581 imapMessageFlagsType newImapFlags = 0;
6582 message->GetMessageSize(&msgSize);
6583 message->GetFlags(&msgFlags);
6584 sourceOp->SetDestinationFolderURI(folderURI); // offline move
6585 sourceOp->SetOperation(nsIMsgOfflineImapOperation::kMsgMoved);
6586 sourceOp->SetMsgSize(msgSize);
6587 newImapFlags = msgFlags & 0x7;
6588 if (msgFlags & nsMsgMessageFlags::Forwarded)
6589 newImapFlags |= kImapMsgForwardedFlag;
6590 sourceOp->SetNewFlags(newImapFlags);
6591 } else
6592 sourceOp->AddMessageCopyOperation(folderURI); // offline copy
6593
6594 sourceOp->GetOperation(&moveCopyOpType);
6595 srcMsgs.AppendObject(message);
6596 }
6597 bool hasMsgOffline = false;
6598 srcFolder->HasMsgOffline(originalKey, &hasMsgOffline);
6599 } else
6600 stopit = NS_ERROR_FAILURE;
6601
6602 nsCOMPtr<nsIMsgDBHdr> mailHdr;
6603 rv =
6604 sourceMailDB->GetMsgHdrForKey(originalKey, getter_AddRefs(mailHdr));
6605 if (NS_SUCCEEDED(rv) && mailHdr) {
6606 bool successfulCopy = false;
6607 nsMsgKey srcDBhighWaterMark;
6608 srcDbFolderInfo->GetHighWater(&srcDBhighWaterMark);
6609
6610 nsCOMPtr<nsIMsgDBHdr> newMailHdr;
6611 rv = database->CopyHdrFromExistingHdr(fakeBase + sourceKeyIndex,
6612 mailHdr, true,
6613 getter_AddRefs(newMailHdr));
6614 if (!newMailHdr || NS_FAILED(rv)) {
6615 NS_ASSERTION(false, "failed to copy hdr");
6616 stopit = rv;
6617 }
6618
6619 if (NS_SUCCEEDED(stopit)) {
6620 bool hasMsgOffline = false;
6621
6622 destMsgHdrs.AppendElement(newMailHdr);
6623 srcFolder->HasMsgOffline(originalKey, &hasMsgOffline);
6624 newMailHdr->SetUint32Property("pseudoHdr", 1);
6625 if (!reusable)
6626 (void)srcFolder->GetMsgInputStream(newMailHdr, &reusable,
6627 getter_AddRefs(inputStream));
6628
6629 if (inputStream && hasMsgOffline && !isLocked) {
6630 rv = GetOfflineStoreOutputStream(newMailHdr,
6631 getter_AddRefs(outputStream));
6632 NS_ENSURE_SUCCESS(rv, rv);
6633
6634 CopyOfflineMsgBody(srcFolder, newMailHdr, mailHdr, inputStream,
6635 outputStream);
6636 nsCOMPtr<nsIMsgPluggableStore> offlineStore;
6637 (void)GetMsgStore(getter_AddRefs(offlineStore));
6638 if (offlineStore)
6639 offlineStore->FinishNewMessage(outputStream, newMailHdr);
6640 } else
6641 database->MarkOffline(fakeBase + sourceKeyIndex, false, nullptr);
6642
6643 nsCOMPtr<nsIMsgOfflineImapOperation> destOp;
6644 database->GetOfflineOpForKey(fakeBase + sourceKeyIndex, true,
6645 getter_AddRefs(destOp));
6646 if (destOp) {
6647 // check if this is a move back to the original mailbox, in which
6648 // case we just delete the offline operation.
6649 if (messageReturningHome)
6650 database->RemoveOfflineOp(destOp);
6651 else {
6652 SetFlag(nsMsgFolderFlags::OfflineEvents);
6653 destOp->SetSourceFolderURI(originalSrcFolderURI);
6654 destOp->SetSrcMessageKey(originalKey);
6655 addedKeys.AppendElement(fakeBase + sourceKeyIndex);
6656 addedHdrs.AppendObject(newMailHdr);
6657 }
6658 } else
6659 stopit = NS_ERROR_FAILURE;
6660 }
6661 successfulCopy = NS_SUCCEEDED(stopit);
6662 nsMsgKey msgKey;
6663 mailHdr->GetMessageKey(&msgKey);
6664 if (isMove && successfulCopy) {
6665 if (deleteToTrash || deleteImmediately)
6666 keysToDelete.AppendElement(msgKey);
6667 else
6668 sourceMailDB->MarkImapDeleted(msgKey, true,
6669 nullptr); // offline delete
6670 }
6671 if (successfulCopy)
6672 // This is for both moves and copies
6673 msgHdrsCopied.AppendElement(mailHdr);
6674 }
6675 }
6676 EnableNotifications(nsIMsgFolder::allMessageCountNotifications, true);
6677 RefPtr<nsImapOfflineTxn> addHdrMsgTxn = new nsImapOfflineTxn(
6678 this, &addedKeys, nullptr, this, isMove,
6679 nsIMsgOfflineImapOperation::kAddedHeader, addedHdrs);
6680 if (addHdrMsgTxn && txnMgr) txnMgr->DoTransaction(addHdrMsgTxn);
6681 RefPtr<nsImapOfflineTxn> undoMsgTxn =
6682 new nsImapOfflineTxn(srcFolder, &srcKeyArray, messageIds.get(), this,
6683 isMove, moveCopyOpType, srcMsgs);
6684 if (undoMsgTxn) {
6685 if (isMove) {
6686 undoMsgTxn->SetTransactionType(nsIMessenger::eMoveMsg);
6687 nsCOMPtr<nsIMsgImapMailFolder> srcIsImap(
6688 do_QueryInterface(srcFolder));
6689 // remember this undo transaction so we can hook up the result
6690 // msg ids in the undo transaction.
6691 if (srcIsImap) {
6692 nsImapMailFolder* srcImapFolder =
6693 static_cast<nsImapMailFolder*>(srcFolder);
6694 srcImapFolder->m_pendingOfflineMoves.AppendElement(undoMsgTxn);
6695 }
6696 } else
6697 undoMsgTxn->SetTransactionType(nsIMessenger::eCopyMsg);
6698 // we're adding this undo action before the delete is successful. This
6699 // is evil, but 4.5 did it as well.
6700 if (txnMgr) txnMgr->DoTransaction(undoMsgTxn);
6701 }
6702 undoMsgTxn =
6703 new nsImapOfflineTxn(srcFolder, &srcKeyArray, messageIds.get(), this,
6704 isMove, deleteOpType, srcMsgs);
6705 if (undoMsgTxn) {
6706 if (isMove) {
6707 if (mFlags & nsMsgFolderFlags::Trash)
6708 undoMsgTxn->SetTransactionType(nsIMessenger::eDeleteMsg);
6709 else
6710 undoMsgTxn->SetTransactionType(nsIMessenger::eMoveMsg);
6711 } else
6712 undoMsgTxn->SetTransactionType(nsIMessenger::eCopyMsg);
6713 if (txnMgr) txnMgr->DoTransaction(undoMsgTxn);
6714 }
6715 if (outputStream) outputStream->Close();
6716
6717 if (isMove) sourceMailDB->Commit(nsMsgDBCommitType::kLargeCommit);
6718 database->Commit(nsMsgDBCommitType::kLargeCommit);
6719 SummaryChanged();
6720 srcFolder->SummaryChanged();
6721 }
6722 if (txnMgr) txnMgr->EndBatch(false);
6723 }
6724
6725 // Do this before delete, as it destroys the messages
6726 if (!msgHdrsCopied.IsEmpty()) {
6727 nsCOMPtr<nsIMsgFolderNotificationService> notifier(
6728 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
6729 if (notifier) {
6730 notifier->NotifyMsgsMoveCopyCompleted(isMove, msgHdrsCopied, this,
6731 destMsgHdrs);
6732 }
6733 }
6734
6735 if (isMove && NS_SUCCEEDED(rv) && (deleteToTrash || deleteImmediately)) {
6736 DeleteStoreMessages(keysToDelete, srcFolder);
6737 srcFolder->EnableNotifications(nsIMsgFolder::allMessageCountNotifications,
6738 false);
6739 sourceMailDB->DeleteMessages(keysToDelete, nullptr);
6740 srcFolder->EnableNotifications(nsIMsgFolder::allMessageCountNotifications,
6741 true);
6742 }
6743
6744 nsCOMPtr<nsISupports> srcSupport = do_QueryInterface(srcFolder);
6745 OnCopyCompleted(srcSupport, rv);
6746
6747 if (isMove)
6748 srcFolder->NotifyFolderEvent(NS_SUCCEEDED(rv) ? kDeleteOrMoveMsgCompleted
6749 : kDeleteOrMoveMsgFailed);
6750 return rv;
6751 }
6752
SetPendingAttributes(const nsTArray<RefPtr<nsIMsgDBHdr>> & messages,bool aIsMove,bool aSetOffline)6753 void nsImapMailFolder::SetPendingAttributes(
6754 const nsTArray<RefPtr<nsIMsgDBHdr>>& messages, bool aIsMove,
6755 bool aSetOffline) {
6756 GetDatabase();
6757 if (!mDatabase) return;
6758
6759 uint32_t supportedUserFlags;
6760 GetSupportedUserFlags(&supportedUserFlags);
6761
6762 nsresult rv;
6763 nsCOMPtr<nsIPrefBranch> prefBranch(
6764 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
6765 NS_ENSURE_SUCCESS_VOID(rv);
6766
6767 nsCString dontPreserve;
6768
6769 // These preferences exist so that extensions can control which properties
6770 // are preserved in the database when a message is moved or copied. All
6771 // properties are preserved except those listed in these preferences
6772 if (aIsMove)
6773 prefBranch->GetCharPref("mailnews.database.summary.dontPreserveOnMove",
6774 dontPreserve);
6775 else
6776 prefBranch->GetCharPref("mailnews.database.summary.dontPreserveOnCopy",
6777 dontPreserve);
6778
6779 // We'll add spaces at beginning and end so we can search for space-name-space
6780 nsCString dontPreserveEx(" "_ns);
6781 dontPreserveEx.Append(dontPreserve);
6782 dontPreserveEx.Append(' ');
6783
6784 // these properties are set as integers below, so don't set them again
6785 // in the iteration through the properties
6786 dontPreserveEx.AppendLiteral(
6787 "offlineMsgSize msgOffset flags priority pseudoHdr ");
6788
6789 // these fields are either copied separately when the server does not support
6790 // custom IMAP flags, or managed directly through the flags
6791 dontPreserveEx.AppendLiteral("keywords label ");
6792
6793 // check if any msg hdr has special flags or properties set
6794 // that we need to set on the dest hdr
6795 for (auto msgDBHdr : messages) {
6796 if (!(supportedUserFlags & kImapMsgSupportUserFlag)) {
6797 nsMsgLabelValue label;
6798 msgDBHdr->GetLabel(&label);
6799 if (label != 0) {
6800 nsAutoCString labelStr;
6801 labelStr.AppendInt(label);
6802 mDatabase->SetAttributeOnPendingHdr(msgDBHdr, "label", labelStr.get());
6803 }
6804 nsCString keywords;
6805 msgDBHdr->GetStringProperty("keywords", getter_Copies(keywords));
6806 if (!keywords.IsEmpty())
6807 mDatabase->SetAttributeOnPendingHdr(msgDBHdr, "keywords",
6808 keywords.get());
6809 }
6810
6811 // do this even if the server supports user-defined flags.
6812 nsCOMPtr<nsIUTF8StringEnumerator> propertyEnumerator;
6813 nsresult rv =
6814 msgDBHdr->GetPropertyEnumerator(getter_AddRefs(propertyEnumerator));
6815 NS_ENSURE_SUCCESS_VOID(rv);
6816
6817 nsAutoCString property;
6818 nsCString sourceString;
6819 bool hasMore;
6820 while (NS_SUCCEEDED(propertyEnumerator->HasMore(&hasMore)) && hasMore) {
6821 propertyEnumerator->GetNext(property);
6822 nsAutoCString propertyEx(" "_ns);
6823 propertyEx.Append(property);
6824 propertyEx.Append(' ');
6825 if (dontPreserveEx.Find(propertyEx) != kNotFound) continue;
6826
6827 nsCString sourceString;
6828 msgDBHdr->GetStringProperty(property.get(), getter_Copies(sourceString));
6829 mDatabase->SetAttributeOnPendingHdr(msgDBHdr, property.get(),
6830 sourceString.get());
6831 }
6832
6833 // Carry over HasRe flag.
6834 uint32_t flags;
6835 uint32_t storeFlags = 0;
6836 msgDBHdr->GetFlags(&flags);
6837 if (flags & nsMsgMessageFlags::HasRe) {
6838 storeFlags = nsMsgMessageFlags::HasRe;
6839 mDatabase->SetUint32AttributeOnPendingHdr(msgDBHdr, "flags", storeFlags);
6840 }
6841
6842 uint32_t messageSize;
6843 uint64_t messageOffset;
6844 nsCString storeToken;
6845 msgDBHdr->GetMessageOffset(&messageOffset);
6846 msgDBHdr->GetOfflineMessageSize(&messageSize);
6847 msgDBHdr->GetStringProperty("storeToken", getter_Copies(storeToken));
6848 if (messageSize) {
6849 mDatabase->SetUint32AttributeOnPendingHdr(msgDBHdr, "offlineMsgSize",
6850 messageSize);
6851 mDatabase->SetUint64AttributeOnPendingHdr(msgDBHdr, "msgOffset",
6852 messageOffset);
6853 // Not always setting "flags" attribute to nsMsgMessageFlags::Offline
6854 // here because it can cause missing parts (inline or attachments)
6855 // when messages are moved or copied manually or by filter action.
6856 if (aSetOffline)
6857 mDatabase->SetUint32AttributeOnPendingHdr(
6858 msgDBHdr, "flags", storeFlags | nsMsgMessageFlags::Offline);
6859 mDatabase->SetAttributeOnPendingHdr(msgDBHdr, "storeToken",
6860 storeToken.get());
6861 }
6862 nsMsgPriorityValue priority;
6863 msgDBHdr->GetPriority(&priority);
6864 if (priority != 0) {
6865 nsAutoCString priorityStr;
6866 priorityStr.AppendInt(priority);
6867 mDatabase->SetAttributeOnPendingHdr(msgDBHdr, "priority",
6868 priorityStr.get());
6869 }
6870 }
6871 }
6872
6873 NS_IMETHODIMP
CopyMessages(nsIMsgFolder * srcFolder,nsTArray<RefPtr<nsIMsgDBHdr>> const & messages,bool isMove,nsIMsgWindow * msgWindow,nsIMsgCopyServiceListener * listener,bool isFolder,bool allowUndo)6874 nsImapMailFolder::CopyMessages(
6875 nsIMsgFolder* srcFolder, nsTArray<RefPtr<nsIMsgDBHdr>> const& messages,
6876 bool isMove, nsIMsgWindow* msgWindow, nsIMsgCopyServiceListener* listener,
6877 bool isFolder, // isFolder for future use when we do cross-server folder
6878 // move/copy
6879 bool allowUndo) {
6880 UpdateTimestamps(allowUndo);
6881
6882 nsresult rv;
6883 nsCOMPtr<nsIMsgIncomingServer> srcServer;
6884 nsCOMPtr<nsIMsgIncomingServer> dstServer;
6885 nsCOMPtr<nsISupports> srcSupport = do_QueryInterface(srcFolder);
6886 bool sameServer = false;
6887
6888 rv = srcFolder->GetServer(getter_AddRefs(srcServer));
6889 if (NS_FAILED(rv)) goto done;
6890
6891 rv = GetServer(getter_AddRefs(dstServer));
6892 if (NS_FAILED(rv)) goto done;
6893
6894 NS_ENSURE_TRUE(dstServer, NS_ERROR_NULL_POINTER);
6895
6896 rv = dstServer->Equals(srcServer, &sameServer);
6897 if (NS_FAILED(rv)) goto done;
6898
6899 // in theory, if allowUndo is true, then this is a user initiated
6900 // action, and we should do it pseudo-offline. If it's not
6901 // user initiated (e.g., mail filters firing), then allowUndo is
6902 // false, and we should just do the action.
6903 if (!WeAreOffline() && sameServer && allowUndo) {
6904 // complete the copy operation as in offline mode
6905 rv = CopyMessagesOffline(srcFolder, messages, isMove, msgWindow, listener);
6906
6907 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "error offline copy");
6908 // We'll warn if this fails, but we should still try to play back
6909 // offline ops, because it's possible the copy got far enough to
6910 // create the offline ops.
6911
6912 // We make sure that the source folder is an imap folder by limiting
6913 // pseudo-offline operations to the same imap server. If we extend the code
6914 // to cover non imap folders in the future (i.e. imap folder->local folder),
6915 // then the following downcast will cause either a crash or compiler error.
6916 // Do not forget to change it accordingly.
6917 nsImapMailFolder* srcImapFolder = static_cast<nsImapMailFolder*>(srcFolder);
6918
6919 // lazily create playback timer if it is not already
6920 // created
6921 if (!srcImapFolder->m_playbackTimer) {
6922 rv = srcImapFolder->CreatePlaybackTimer();
6923 NS_ENSURE_SUCCESS(rv, rv);
6924 }
6925
6926 if (srcImapFolder->m_playbackTimer) {
6927 // if there is no pending request, create a new one, and set the timer.
6928 // Otherwise use the existing one to reset the timer. it is callback
6929 // function's responsibility to delete the new request object
6930 if (!srcImapFolder->m_pendingPlaybackReq) {
6931 srcImapFolder->m_pendingPlaybackReq =
6932 new nsPlaybackRequest(srcImapFolder, msgWindow);
6933 if (!srcImapFolder->m_pendingPlaybackReq) return NS_ERROR_OUT_OF_MEMORY;
6934 }
6935
6936 srcImapFolder->m_playbackTimer->InitWithNamedFuncCallback(
6937 PlaybackTimerCallback, (void*)srcImapFolder->m_pendingPlaybackReq,
6938 PLAYBACK_TIMER_INTERVAL_IN_MS, nsITimer::TYPE_ONE_SHOT,
6939 "nsImapMailFolder::PlaybackTimerCallback");
6940 }
6941 return rv;
6942 } else {
6943 // sort the message array by key
6944
6945 nsTArray<nsMsgKey> keyArray(messages.Length());
6946 for (nsIMsgDBHdr* aMessage : messages) {
6947 if (!aMessage) {
6948 continue;
6949 }
6950 nsMsgKey key;
6951 aMessage->GetMessageKey(&key);
6952 keyArray.AppendElement(key);
6953 }
6954 keyArray.Sort();
6955
6956 nsTArray<RefPtr<nsIMsgDBHdr>> sortedMsgs;
6957 rv = MessagesInKeyOrder(keyArray, srcFolder, sortedMsgs);
6958 NS_ENSURE_SUCCESS(rv, rv);
6959
6960 if (WeAreOffline())
6961 return CopyMessagesOffline(srcFolder, sortedMsgs, isMove, msgWindow,
6962 listener);
6963
6964 nsCOMPtr<nsIImapService> imapService =
6965 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
6966 NS_ENSURE_SUCCESS(rv, rv);
6967
6968 // 3rd parameter: Do not set offline flag.
6969 SetPendingAttributes(sortedMsgs, isMove, false);
6970
6971 // if the folders aren't on the same server, do a stream base copy
6972 if (!sameServer) {
6973 rv = CopyMessagesWithStream(srcFolder, sortedMsgs, isMove, true,
6974 msgWindow, listener, allowUndo);
6975 goto done;
6976 }
6977
6978 nsAutoCString messageIds;
6979 rv = AllocateUidStringFromKeys(keyArray, messageIds);
6980 if (NS_FAILED(rv)) goto done;
6981
6982 nsCOMPtr<nsIUrlListener> urlListener;
6983 rv =
6984 QueryInterface(NS_GET_IID(nsIUrlListener), getter_AddRefs(urlListener));
6985 rv = InitCopyState(srcSupport, sortedMsgs, isMove, true, false, 0,
6986 EmptyCString(), listener, msgWindow, allowUndo);
6987 if (NS_FAILED(rv)) goto done;
6988
6989 m_copyState->m_curIndex = m_copyState->m_messages.Length();
6990
6991 if (isMove)
6992 srcFolder->EnableNotifications(
6993 allMessageCountNotifications,
6994 false); // disable message count notification
6995
6996 nsCOMPtr<nsISupports> copySupport = do_QueryInterface(m_copyState);
6997 rv = imapService->OnlineMessageCopy(srcFolder, messageIds, this, true,
6998 isMove, urlListener, nullptr,
6999 copySupport, msgWindow);
7000 if (NS_SUCCEEDED(rv) && m_copyState->m_allowUndo) {
7001 RefPtr<nsImapMoveCopyMsgTxn> undoMsgTxn = new nsImapMoveCopyMsgTxn;
7002 if (!undoMsgTxn ||
7003 NS_FAILED(undoMsgTxn->Init(srcFolder, &keyArray, messageIds.get(),
7004 this, true, isMove)))
7005 return NS_ERROR_OUT_OF_MEMORY;
7006
7007 if (isMove) {
7008 if (mFlags & nsMsgFolderFlags::Trash)
7009 undoMsgTxn->SetTransactionType(nsIMessenger::eDeleteMsg);
7010 else
7011 undoMsgTxn->SetTransactionType(nsIMessenger::eMoveMsg);
7012 } else
7013 undoMsgTxn->SetTransactionType(nsIMessenger::eCopyMsg);
7014 m_copyState->m_undoMsgTxn = undoMsgTxn;
7015 }
7016
7017 } // endif
7018
7019 done:
7020 if (NS_FAILED(rv)) {
7021 (void)OnCopyCompleted(srcSupport, rv);
7022 if (isMove) {
7023 srcFolder->EnableNotifications(
7024 allMessageCountNotifications,
7025 true); // enable message count notification
7026 NotifyFolderEvent(kDeleteOrMoveMsgFailed);
7027 }
7028 }
7029 return rv;
7030 }
7031
7032 class nsImapFolderCopyState final : public nsIUrlListener,
7033 public nsIMsgCopyServiceListener {
7034 public:
7035 nsImapFolderCopyState(nsIMsgFolder* destParent, nsIMsgFolder* srcFolder,
7036 bool isMoveFolder, nsIMsgWindow* msgWindow,
7037 nsIMsgCopyServiceListener* listener);
7038
7039 NS_DECL_ISUPPORTS
7040 NS_DECL_NSIURLLISTENER
7041 NS_DECL_NSIMSGCOPYSERVICELISTENER
7042
7043 nsresult StartNextCopy();
7044 nsresult AdvanceToNextFolder(nsresult aStatus);
7045
7046 protected:
7047 ~nsImapFolderCopyState();
7048 RefPtr<nsImapMailFolder> m_newDestFolder;
7049 nsCOMPtr<nsISupports> m_origSrcFolder;
7050 nsCOMPtr<nsIMsgFolder> m_curDestParent;
7051 nsCOMPtr<nsIMsgFolder> m_curSrcFolder;
7052 bool m_isMoveFolder;
7053 nsCOMPtr<nsIMsgCopyServiceListener> m_copySrvcListener;
7054 nsCOMPtr<nsIMsgWindow> m_msgWindow;
7055 int32_t m_childIndex;
7056 nsCOMArray<nsIMsgFolder> m_srcChildFolders;
7057 nsCOMArray<nsIMsgFolder> m_destParents;
7058 };
7059
NS_IMPL_ISUPPORTS(nsImapFolderCopyState,nsIUrlListener,nsIMsgCopyServiceListener)7060 NS_IMPL_ISUPPORTS(nsImapFolderCopyState, nsIUrlListener,
7061 nsIMsgCopyServiceListener)
7062
7063 nsImapFolderCopyState::nsImapFolderCopyState(
7064 nsIMsgFolder* destParent, nsIMsgFolder* srcFolder, bool isMoveFolder,
7065 nsIMsgWindow* msgWindow, nsIMsgCopyServiceListener* listener) {
7066 m_origSrcFolder = do_QueryInterface(srcFolder);
7067 m_curDestParent = destParent;
7068 m_curSrcFolder = srcFolder;
7069 m_isMoveFolder = isMoveFolder;
7070 m_msgWindow = msgWindow;
7071 m_copySrvcListener = listener;
7072 m_childIndex = -1;
7073 // NOTE: The nsImapMailFolder doesn't keep a reference to us, so we're
7074 // relying on our use as a listener by nsImapService and nsMsgCopyService
7075 // to keep our refcount from zeroing!
7076 // Might be safer to add a kungfudeathgrip on ourselves for the duration
7077 // of the operation? Would need to make sure we catch all error conditions.
7078 }
7079
~nsImapFolderCopyState()7080 nsImapFolderCopyState::~nsImapFolderCopyState() {}
7081
StartNextCopy()7082 nsresult nsImapFolderCopyState::StartNextCopy() {
7083 nsresult rv;
7084 // Create the destination folder (our OnStopRunningUrl() will be called
7085 // when done).
7086 nsCOMPtr<nsIImapService> imapService =
7087 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
7088 NS_ENSURE_SUCCESS(rv, rv);
7089 nsString folderName;
7090 m_curSrcFolder->GetName(folderName);
7091 return imapService->EnsureFolderExists(m_curDestParent, folderName,
7092 m_msgWindow, this, nullptr);
7093 }
7094
AdvanceToNextFolder(nsresult aStatus)7095 nsresult nsImapFolderCopyState::AdvanceToNextFolder(nsresult aStatus) {
7096 nsresult rv = NS_OK;
7097 m_childIndex++;
7098 if (m_childIndex >= m_srcChildFolders.Count()) {
7099 if (m_newDestFolder)
7100 m_newDestFolder->OnCopyCompleted(m_origSrcFolder, aStatus);
7101 } else {
7102 m_curDestParent = m_destParents[m_childIndex];
7103 m_curSrcFolder = m_srcChildFolders[m_childIndex];
7104 rv = StartNextCopy();
7105 }
7106 return rv;
7107 }
7108
7109 NS_IMETHODIMP
OnStartRunningUrl(nsIURI * aUrl)7110 nsImapFolderCopyState::OnStartRunningUrl(nsIURI* aUrl) {
7111 NS_ASSERTION(aUrl, "sanity check - need to be be running non-null url");
7112 return NS_OK;
7113 }
7114
7115 NS_IMETHODIMP
OnStopRunningUrl(nsIURI * aUrl,nsresult aExitCode)7116 nsImapFolderCopyState::OnStopRunningUrl(nsIURI* aUrl, nsresult aExitCode) {
7117 if (NS_FAILED(aExitCode)) {
7118 if (m_copySrvcListener) m_copySrvcListener->OnStopCopy(aExitCode);
7119 return aExitCode; // or NS_OK???
7120 }
7121 nsresult rv = NS_OK;
7122 if (aUrl) {
7123 nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(aUrl);
7124 if (imapUrl) {
7125 nsImapAction imapAction = nsIImapUrl::nsImapTest;
7126 imapUrl->GetImapAction(&imapAction);
7127
7128 switch (imapAction) {
7129 case nsIImapUrl::nsImapEnsureExistsFolder: {
7130 // Our EnsureFolderExists() call has completed successfully,
7131 // so our dest folder is ready.
7132 nsCOMPtr<nsIMsgFolder> newMsgFolder;
7133 nsString folderName;
7134 nsCString utfLeafName;
7135 m_curSrcFolder->GetName(folderName);
7136 bool utf8AcceptEnabled;
7137 nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
7138 do_QueryInterface(m_curDestParent);
7139 rv = imapFolder->GetShouldUseUtf8FolderName(&utf8AcceptEnabled);
7140 NS_ENSURE_SUCCESS(rv, rv);
7141 if (utf8AcceptEnabled) {
7142 CopyUTF16toUTF8(folderName, utfLeafName);
7143 } else {
7144 CopyUTF16toMUTF7(folderName, utfLeafName);
7145 }
7146 // Create the nsIMsgFolder object which represents the folder on
7147 // the IMAP server.
7148 rv = m_curDestParent->FindSubFolder(utfLeafName,
7149 getter_AddRefs(newMsgFolder));
7150 NS_ENSURE_SUCCESS(rv, rv);
7151 // Save the first new folder so we can send a notification to the
7152 // copy service when this whole process is done.
7153 if (!m_newDestFolder)
7154 m_newDestFolder =
7155 static_cast<nsImapMailFolder*>(newMsgFolder.get());
7156
7157 // Check if the source folder has children. If it does, list them
7158 // into m_srcChildFolders, and set m_destParents for the
7159 // corresponding indexes to the newly created folder.
7160 nsTArray<RefPtr<nsIMsgFolder>> subFolders;
7161 rv = m_curSrcFolder->GetSubFolders(subFolders);
7162 NS_ENSURE_SUCCESS(rv, rv);
7163 uint32_t childIndex = 0;
7164 for (nsIMsgFolder* folder : subFolders) {
7165 m_srcChildFolders.InsertElementAt(m_childIndex + childIndex + 1,
7166 folder);
7167 m_destParents.InsertElementAt(m_childIndex + childIndex + 1,
7168 newMsgFolder);
7169 ++childIndex;
7170 }
7171
7172 // Now kick off a copy (or move) of messages to the new folder.
7173 nsCOMPtr<nsIMsgEnumerator> enumerator;
7174 rv = m_curSrcFolder->GetMessages(getter_AddRefs(enumerator));
7175 nsTArray<RefPtr<nsIMsgDBHdr>> msgArray;
7176 bool hasMore = false;
7177
7178 if (enumerator) rv = enumerator->HasMoreElements(&hasMore);
7179
7180 // Early-out for empty folder.
7181 if (!hasMore) return AdvanceToNextFolder(NS_OK);
7182
7183 while (NS_SUCCEEDED(rv) && hasMore) {
7184 nsCOMPtr<nsIMsgDBHdr> hdr;
7185 rv = enumerator->GetNext(getter_AddRefs(hdr));
7186 NS_ENSURE_SUCCESS(rv, rv);
7187 msgArray.AppendElement(hdr);
7188 rv = enumerator->HasMoreElements(&hasMore);
7189 }
7190
7191 nsCOMPtr<nsIMsgCopyService> copyService =
7192 do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
7193 NS_ENSURE_SUCCESS(rv, rv);
7194 rv = copyService->CopyMessages(m_curSrcFolder, msgArray, newMsgFolder,
7195 m_isMoveFolder, this, m_msgWindow,
7196 false /* allowUndo */);
7197 } break;
7198 }
7199 }
7200 }
7201 return rv;
7202 }
7203
OnStartCopy()7204 NS_IMETHODIMP nsImapFolderCopyState::OnStartCopy() { return NS_OK; }
7205
7206 /* void OnProgress (in uint32_t aProgress, in uint32_t aProgressMax); */
OnProgress(uint32_t aProgress,uint32_t aProgressMax)7207 NS_IMETHODIMP nsImapFolderCopyState::OnProgress(uint32_t aProgress,
7208 uint32_t aProgressMax) {
7209 return NS_ERROR_NOT_IMPLEMENTED;
7210 }
7211
7212 /* void SetMessageKey (in nsMsgKey aKey); */
SetMessageKey(nsMsgKey aKey)7213 NS_IMETHODIMP nsImapFolderCopyState::SetMessageKey(nsMsgKey aKey) {
7214 return NS_ERROR_NOT_IMPLEMENTED;
7215 }
7216
7217 /* [noscript] void GetMessageId (in nsCString aMessageId); */
GetMessageId(nsACString & messageId)7218 NS_IMETHODIMP nsImapFolderCopyState::GetMessageId(nsACString& messageId) {
7219 return NS_ERROR_NOT_IMPLEMENTED;
7220 }
7221
7222 /* void OnStopCopy (in nsresult aStatus); */
OnStopCopy(nsresult aStatus)7223 NS_IMETHODIMP nsImapFolderCopyState::OnStopCopy(nsresult aStatus) {
7224 if (NS_SUCCEEDED(aStatus)) return AdvanceToNextFolder(aStatus);
7225 if (m_copySrvcListener) {
7226 (void)m_copySrvcListener->OnStopCopy(aStatus);
7227 m_copySrvcListener = nullptr;
7228 }
7229
7230 return NS_OK;
7231 }
7232
7233 // "this" is the parent of the copied folder.
7234 NS_IMETHODIMP
CopyFolder(nsIMsgFolder * srcFolder,bool isMoveFolder,nsIMsgWindow * msgWindow,nsIMsgCopyServiceListener * listener)7235 nsImapMailFolder::CopyFolder(nsIMsgFolder* srcFolder, bool isMoveFolder,
7236 nsIMsgWindow* msgWindow,
7237 nsIMsgCopyServiceListener* listener) {
7238 NS_ENSURE_ARG_POINTER(srcFolder);
7239
7240 nsresult rv = NS_OK;
7241
7242 if (isMoveFolder) // move folder permitted when dstFolder and the srcFolder
7243 // are on same server
7244 {
7245 uint32_t folderFlags = 0;
7246 if (srcFolder) srcFolder->GetFlags(&folderFlags);
7247
7248 // if our source folder is a virtual folder
7249 if (folderFlags & nsMsgFolderFlags::Virtual) {
7250 nsCOMPtr<nsIMsgFolder> newMsgFolder;
7251 nsString folderName;
7252 srcFolder->GetName(folderName);
7253
7254 nsAutoString safeFolderName(folderName);
7255 NS_MsgHashIfNecessary(safeFolderName);
7256
7257 srcFolder->ForceDBClosed();
7258
7259 nsCOMPtr<nsIFile> oldPathFile;
7260 rv = srcFolder->GetFilePath(getter_AddRefs(oldPathFile));
7261 NS_ENSURE_SUCCESS(rv, rv);
7262
7263 nsCOMPtr<nsIFile> summaryFile;
7264 GetSummaryFileLocation(oldPathFile, getter_AddRefs(summaryFile));
7265
7266 nsCOMPtr<nsIFile> newPathFile;
7267 rv = GetFilePath(getter_AddRefs(newPathFile));
7268 NS_ENSURE_SUCCESS(rv, rv);
7269
7270 bool isDirectory = false;
7271 newPathFile->IsDirectory(&isDirectory);
7272 if (!isDirectory) {
7273 AddDirectorySeparator(newPathFile);
7274 rv = newPathFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
7275 NS_ENSURE_SUCCESS(rv, rv);
7276 }
7277
7278 rv = CheckIfFolderExists(folderName, this, msgWindow);
7279 if (NS_FAILED(rv)) return rv;
7280
7281 rv = summaryFile->CopyTo(newPathFile, EmptyString());
7282 NS_ENSURE_SUCCESS(rv, rv);
7283
7284 rv = AddSubfolder(safeFolderName, getter_AddRefs(newMsgFolder));
7285 NS_ENSURE_SUCCESS(rv, rv);
7286
7287 newMsgFolder->SetPrettyName(folderName);
7288
7289 uint32_t flags;
7290 srcFolder->GetFlags(&flags);
7291 newMsgFolder->SetFlags(flags);
7292
7293 NotifyItemAdded(newMsgFolder);
7294
7295 // now remove the old folder
7296 nsCOMPtr<nsIMsgFolder> msgParent;
7297 srcFolder->GetParent(getter_AddRefs(msgParent));
7298 srcFolder->SetParent(nullptr);
7299 if (msgParent) {
7300 msgParent->PropagateDelete(
7301 srcFolder, false, msgWindow); // The files have already been moved,
7302 // so delete storage false
7303 oldPathFile->Remove(false); // berkeley mailbox
7304 srcFolder->DeleteStorage();
7305
7306 nsCOMPtr<nsIFile> parentPathFile;
7307 rv = msgParent->GetFilePath(getter_AddRefs(parentPathFile));
7308 NS_ENSURE_SUCCESS(rv, rv);
7309
7310 AddDirectorySeparator(parentPathFile);
7311 nsCOMPtr<nsIDirectoryEnumerator> children;
7312 parentPathFile->GetDirectoryEntries(getter_AddRefs(children));
7313 bool more;
7314 // checks if the directory is empty or not
7315 if (children && NS_SUCCEEDED(children->HasMoreElements(&more)) && !more)
7316 parentPathFile->Remove(true);
7317 }
7318 } else // non-virtual folder
7319 {
7320 nsCOMPtr<nsIImapService> imapService =
7321 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
7322 NS_ENSURE_SUCCESS(rv, rv);
7323 nsCOMPtr<nsISupports> srcSupport = do_QueryInterface(srcFolder);
7324 bool match = false;
7325 bool confirmed = false;
7326 if (mFlags & nsMsgFolderFlags::Trash) {
7327 rv = srcFolder->MatchOrChangeFilterDestination(nullptr, false, &match);
7328 if (match) {
7329 srcFolder->ConfirmFolderDeletionForFilter(msgWindow, &confirmed);
7330 // should we return an error to copy service?
7331 // or send a notification?
7332 if (!confirmed) return NS_OK;
7333 }
7334 }
7335 rv = InitCopyState(srcSupport, {}, false, false, false, 0, EmptyCString(),
7336 listener, msgWindow, false);
7337 if (NS_FAILED(rv)) return OnCopyCompleted(srcSupport, rv);
7338
7339 rv = imapService->MoveFolder(srcFolder, this, this, msgWindow, nullptr);
7340 }
7341 } else // copying folder (should only be across server?)
7342 {
7343 // NOTE: the copystate object must hold itself in existence until complete,
7344 // as we're not keeping hold of it here.
7345 RefPtr<nsImapFolderCopyState> folderCopier = new nsImapFolderCopyState(
7346 this, srcFolder, isMoveFolder, msgWindow, listener);
7347 return folderCopier->StartNextCopy();
7348 }
7349 return rv;
7350 }
7351
7352 NS_IMETHODIMP
CopyFileMessage(nsIFile * file,nsIMsgDBHdr * msgToReplace,bool isDraftOrTemplate,uint32_t aNewMsgFlags,const nsACString & aNewMsgKeywords,nsIMsgWindow * msgWindow,nsIMsgCopyServiceListener * listener)7353 nsImapMailFolder::CopyFileMessage(nsIFile* file, nsIMsgDBHdr* msgToReplace,
7354 bool isDraftOrTemplate, uint32_t aNewMsgFlags,
7355 const nsACString& aNewMsgKeywords,
7356 nsIMsgWindow* msgWindow,
7357 nsIMsgCopyServiceListener* listener) {
7358 nsresult rv = NS_ERROR_NULL_POINTER;
7359 nsMsgKey key = nsMsgKey_None;
7360 nsAutoCString messageId;
7361 nsTArray<RefPtr<nsIMsgDBHdr>> messages;
7362
7363 nsCOMPtr<nsIImapService> imapService =
7364 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
7365 if (NS_FAILED(rv)) return OnCopyCompleted(file, rv);
7366
7367 if (msgToReplace) {
7368 rv = msgToReplace->GetMessageKey(&key);
7369 if (NS_SUCCEEDED(rv)) {
7370 messageId.AppendInt((int32_t)key);
7371 // Perhaps we have the message offline, but even if we do it is
7372 // not valid, since the only time we do a file copy for an
7373 // existing message is when we are changing the message.
7374 // So set the offline size to 0 to force SetPendingAttributes to
7375 // clear the offline message flag.
7376 msgToReplace->SetOfflineMessageSize(0);
7377 messages.AppendElement(msgToReplace);
7378 SetPendingAttributes({msgToReplace}, false, false);
7379 }
7380 }
7381
7382 bool isMove = (msgToReplace ? true : false);
7383 rv = InitCopyState(file, messages, isMove, isDraftOrTemplate, false,
7384 aNewMsgFlags, aNewMsgKeywords, listener, msgWindow, false);
7385 if (NS_FAILED(rv)) return OnCopyCompleted(file, rv);
7386
7387 m_copyState->m_streamCopy = true;
7388 rv = imapService->AppendMessageFromFile(file, this, messageId, true,
7389 isDraftOrTemplate, this, nullptr,
7390 m_copyState, msgWindow);
7391 if (NS_FAILED(rv)) return OnCopyCompleted(file, rv);
7392
7393 return rv;
7394 }
7395
CopyStreamMessage(nsIMsgDBHdr * message,nsIMsgFolder * dstFolder,nsIMsgWindow * aMsgWindow,bool isMove)7396 nsresult nsImapMailFolder::CopyStreamMessage(
7397 nsIMsgDBHdr* message,
7398 nsIMsgFolder* dstFolder, // should be this
7399 nsIMsgWindow* aMsgWindow, bool isMove) {
7400 NS_ENSURE_ARG_POINTER(message);
7401 if (!m_copyState)
7402 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
7403 ("CopyStreamMessage failed with null m_copyState"));
7404 NS_ENSURE_TRUE(m_copyState, NS_ERROR_NULL_POINTER);
7405 nsresult rv;
7406 nsCOMPtr<nsICopyMessageStreamListener> copyStreamListener =
7407 do_CreateInstance(NS_COPYMESSAGESTREAMLISTENER_CONTRACTID, &rv);
7408 NS_ENSURE_SUCCESS(rv, rv);
7409
7410 nsCOMPtr<nsICopyMessageListener> copyListener(
7411 do_QueryInterface(dstFolder, &rv));
7412 NS_ENSURE_SUCCESS(rv, rv);
7413
7414 nsCOMPtr<nsIMsgFolder> srcFolder(
7415 do_QueryInterface(m_copyState->m_srcSupport, &rv));
7416 if (NS_FAILED(rv))
7417 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
7418 ("CopyStreaMessage failed with null m_copyState->m_srcSupport"));
7419 if (NS_FAILED(rv)) return rv;
7420 rv = copyStreamListener->Init(srcFolder, copyListener, nullptr);
7421 if (NS_FAILED(rv))
7422 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
7423 ("CopyStreaMessage failed in copyStreamListener->Init"));
7424 if (NS_FAILED(rv)) return rv;
7425
7426 nsCString uri;
7427 srcFolder->GetUriForMsg(message, uri);
7428
7429 if (!m_copyState->m_msgService)
7430 rv = GetMessageServiceFromURI(uri,
7431 getter_AddRefs(m_copyState->m_msgService));
7432
7433 if (NS_SUCCEEDED(rv) && m_copyState->m_msgService) {
7434 nsCOMPtr<nsIStreamListener> streamListener(
7435 do_QueryInterface(copyStreamListener, &rv));
7436 NS_ENSURE_SUCCESS(rv, rv);
7437
7438 // put up status message here, if copying more than one message.
7439 if (m_copyState->m_messages.Length() > 1) {
7440 nsString dstFolderName, progressText;
7441 GetName(dstFolderName);
7442 nsAutoString curMsgString;
7443 nsAutoString totalMsgString;
7444 totalMsgString.AppendInt((int32_t)m_copyState->m_messages.Length());
7445 curMsgString.AppendInt(m_copyState->m_curIndex + 1);
7446
7447 AutoTArray<nsString, 3> formatStrings = {curMsgString, totalMsgString,
7448 dstFolderName};
7449
7450 nsCOMPtr<nsIStringBundle> bundle;
7451 rv = IMAPGetStringBundle(getter_AddRefs(bundle));
7452 NS_ENSURE_SUCCESS(rv, rv);
7453 rv = bundle->FormatStringFromName("imapCopyingMessageOf2", formatStrings,
7454 progressText);
7455 nsCOMPtr<nsIMsgStatusFeedback> statusFeedback;
7456 if (m_copyState->m_msgWindow)
7457 m_copyState->m_msgWindow->GetStatusFeedback(
7458 getter_AddRefs(statusFeedback));
7459 if (statusFeedback) {
7460 statusFeedback->ShowStatusString(progressText);
7461 int32_t percent;
7462 percent = (100 * m_copyState->m_curIndex) /
7463 (int32_t)m_copyState->m_messages.Length();
7464 statusFeedback->ShowProgress(percent);
7465 }
7466 }
7467 nsCOMPtr<nsIURI> dummyNull;
7468 rv = m_copyState->m_msgService->CopyMessage(
7469 uri, streamListener, isMove && !m_copyState->m_isCrossServerOp, nullptr,
7470 aMsgWindow, getter_AddRefs(dummyNull));
7471 if (NS_FAILED(rv))
7472 MOZ_LOG(IMAP, mozilla::LogLevel::Info,
7473 ("CopyMessage failed: uri %s", uri.get()));
7474 }
7475 return rv;
7476 }
7477
nsImapMailCopyState()7478 nsImapMailCopyState::nsImapMailCopyState()
7479 : m_isMove(false),
7480 m_selectedState(false),
7481 m_isCrossServerOp(false),
7482 m_curIndex(0),
7483 m_streamCopy(false),
7484 m_dataBuffer(nullptr),
7485 m_dataBufferSize(0),
7486 m_leftOver(0),
7487 m_allowUndo(false),
7488 m_eatLF(false),
7489 m_newMsgFlags(0),
7490 m_appendUID(nsMsgKey_None) {}
7491
~nsImapMailCopyState()7492 nsImapMailCopyState::~nsImapMailCopyState() {
7493 PR_Free(m_dataBuffer);
7494 if (m_tmpFile) m_tmpFile->Remove(false);
7495 }
7496
NS_IMPL_ISUPPORTS(nsImapMailCopyState,nsImapMailCopyState)7497 NS_IMPL_ISUPPORTS(nsImapMailCopyState, nsImapMailCopyState)
7498
7499 nsresult nsImapMailFolder::InitCopyState(
7500 nsISupports* srcSupport, nsTArray<RefPtr<nsIMsgDBHdr>> const& messages,
7501 bool isMove, bool selectedState, bool acrossServers, uint32_t newMsgFlags,
7502 const nsACString& newMsgKeywords, nsIMsgCopyServiceListener* listener,
7503 nsIMsgWindow* msgWindow, bool allowUndo) {
7504 NS_ENSURE_ARG_POINTER(srcSupport);
7505
7506 NS_ENSURE_TRUE(!m_copyState, NS_ERROR_FAILURE);
7507
7508 m_copyState = new nsImapMailCopyState();
7509
7510 m_copyState->m_isCrossServerOp = acrossServers;
7511 m_copyState->m_srcSupport = srcSupport;
7512
7513 m_copyState->m_messages = messages.Clone();
7514 if (!m_copyState->m_isCrossServerOp) {
7515 uint32_t numUnread = 0;
7516 for (nsIMsgDBHdr* message : m_copyState->m_messages) {
7517 // if the message is not there, then assume what the caller tells us to.
7518 bool isRead = false;
7519 uint32_t flags;
7520 if (message) {
7521 message->GetFlags(&flags);
7522 isRead = flags & nsMsgMessageFlags::Read;
7523 }
7524 if (!isRead) numUnread++;
7525 }
7526 m_copyState->m_unreadCount = numUnread;
7527 } else {
7528 nsIMsgDBHdr* message = m_copyState->m_messages[m_copyState->m_curIndex];
7529 // if the key is not there, then assume what the caller tells us to.
7530 bool isRead = false;
7531 uint32_t flags;
7532 if (message) {
7533 message->GetFlags(&flags);
7534 isRead = flags & nsMsgMessageFlags::Read;
7535 }
7536 m_copyState->m_unreadCount = (isRead) ? 0 : 1;
7537 }
7538
7539 m_copyState->m_isMove = isMove;
7540 m_copyState->m_newMsgFlags = newMsgFlags;
7541 m_copyState->m_newMsgKeywords = newMsgKeywords;
7542 m_copyState->m_allowUndo = allowUndo;
7543 m_copyState->m_selectedState = selectedState;
7544 m_copyState->m_msgWindow = msgWindow;
7545 if (listener) m_copyState->m_listener = listener;
7546 return NS_OK;
7547 }
7548
CopyFileToOfflineStore(nsIFile * srcFile,nsMsgKey msgKey)7549 nsresult nsImapMailFolder::CopyFileToOfflineStore(nsIFile* srcFile,
7550 nsMsgKey msgKey) {
7551 nsresult rv = GetDatabase();
7552 NS_ENSURE_SUCCESS(rv, rv);
7553
7554 bool storeOffline = (mFlags & nsMsgFolderFlags::Offline) && !WeAreOffline();
7555
7556 if (msgKey == nsMsgKey_None) {
7557 // To support send filters, we need to store the message in the database
7558 // when it is copied to the FCC folder. In that case, we know the UID of the
7559 // message and therefore have the correct msgKey. In other cases, where
7560 // we don't need the offline message copied, don't add to db.
7561 if (!storeOffline) return NS_OK;
7562
7563 mDatabase->GetNextFakeOfflineMsgKey(&msgKey);
7564 }
7565
7566 nsCOMPtr<nsIMsgDBHdr> fakeHdr;
7567 rv = mDatabase->CreateNewHdr(msgKey, getter_AddRefs(fakeHdr));
7568 NS_ENSURE_SUCCESS(rv, rv);
7569 fakeHdr->SetUint32Property("pseudoHdr", 1);
7570
7571 // Should we add this to the offline store?
7572 nsCOMPtr<nsIOutputStream> offlineStore;
7573 if (storeOffline) {
7574 rv = GetOfflineStoreOutputStream(fakeHdr, getter_AddRefs(offlineStore));
7575 NS_ENSURE_SUCCESS(rv, rv);
7576 }
7577
7578 // We set an offline kMoveResult because in any case we want to update this
7579 // msgHdr with one downloaded from the server, with possible additional
7580 // headers added.
7581 nsCOMPtr<nsIMsgOfflineImapOperation> op;
7582 rv = mDatabase->GetOfflineOpForKey(msgKey, true, getter_AddRefs(op));
7583 if (NS_SUCCEEDED(rv) && op) {
7584 nsCString destFolderUri;
7585 GetURI(destFolderUri);
7586 op->SetOperation(nsIMsgOfflineImapOperation::kMoveResult);
7587 op->SetDestinationFolderURI(destFolderUri);
7588 SetFlag(nsMsgFolderFlags::OfflineEvents);
7589 }
7590
7591 nsCOMPtr<nsIInputStream> inputStream;
7592 nsCOMPtr<nsIMsgParseMailMsgState> msgParser =
7593 do_CreateInstance(NS_PARSEMAILMSGSTATE_CONTRACTID, &rv);
7594 NS_ENSURE_SUCCESS(rv, rv);
7595 msgParser->SetMailDB(mDatabase);
7596
7597 uint64_t offset = 0;
7598 if (offlineStore) {
7599 // Tell the parser to use the offset that will be in the dest stream,
7600 // not the temp file.
7601 fakeHdr->GetMessageOffset(&offset);
7602 }
7603 msgParser->SetEnvelopePos(offset);
7604
7605 rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), srcFile);
7606 if (NS_SUCCEEDED(rv) && inputStream) {
7607 // Now, parse the temp file to (optionally) copy to
7608 // the offline store for the cur folder.
7609 RefPtr<nsMsgLineStreamBuffer> inputStreamBuffer =
7610 new nsMsgLineStreamBuffer(FILE_IO_BUFFER_SIZE, true, false);
7611 int64_t fileSize;
7612 srcFile->GetFileSize(&fileSize);
7613 uint32_t bytesWritten;
7614 rv = NS_OK;
7615 msgParser->SetState(nsIMsgParseMailMsgState::ParseHeadersState);
7616 msgParser->SetNewMsgHdr(fakeHdr);
7617 bool needMoreData = false;
7618 char* newLine = nullptr;
7619 uint32_t numBytesInLine = 0;
7620 if (offlineStore) {
7621 const char* envelope = "From " CRLF;
7622 offlineStore->Write(envelope, strlen(envelope), &bytesWritten);
7623 fileSize += bytesWritten;
7624 }
7625 do {
7626 newLine = inputStreamBuffer->ReadNextLine(inputStream, numBytesInLine,
7627 needMoreData);
7628 if (newLine) {
7629 msgParser->ParseAFolderLine(newLine, numBytesInLine);
7630 if (offlineStore)
7631 rv = offlineStore->Write(newLine, numBytesInLine, &bytesWritten);
7632
7633 free(newLine);
7634 NS_ENSURE_SUCCESS(rv, rv);
7635 }
7636 } while (newLine);
7637
7638 msgParser->FinishHeader();
7639 uint32_t resultFlags;
7640 if (offlineStore)
7641 fakeHdr->OrFlags(nsMsgMessageFlags::Offline | nsMsgMessageFlags::Read,
7642 &resultFlags);
7643 else
7644 fakeHdr->OrFlags(nsMsgMessageFlags::Read, &resultFlags);
7645 if (offlineStore) fakeHdr->SetOfflineMessageSize(fileSize);
7646 mDatabase->AddNewHdrToDB(fakeHdr, true /* notify */);
7647
7648 // Call FinishNewMessage before setting pending attributes, as in
7649 // maildir it copies from tmp to cur and may change the storeToken
7650 // to get a unique filename.
7651 if (offlineStore) {
7652 nsCOMPtr<nsIMsgPluggableStore> msgStore;
7653 GetMsgStore(getter_AddRefs(msgStore));
7654 if (msgStore) msgStore->FinishNewMessage(offlineStore, fakeHdr);
7655 }
7656
7657 // We are copying from a file to offline store so set offline flag.
7658 SetPendingAttributes({&*fakeHdr}, false, true);
7659
7660 // Gloda needs this notification to index the fake message.
7661 nsCOMPtr<nsIMsgFolderNotificationService> notifier(
7662 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
7663 if (notifier) notifier->NotifyMsgsClassified({&*fakeHdr}, false, false);
7664 inputStream->Close();
7665 inputStream = nullptr;
7666 }
7667 if (offlineStore) offlineStore->Close();
7668 return rv;
7669 }
7670
OnCopyCompleted(nsISupports * srcSupport,nsresult rv)7671 nsresult nsImapMailFolder::OnCopyCompleted(nsISupports* srcSupport,
7672 nsresult rv) {
7673 // if it's a file, and the copy succeeded, then fcc the offline
7674 // store, and add a kMoveResult offline op.
7675 if (NS_SUCCEEDED(rv) && m_copyState) {
7676 nsCOMPtr<nsIFile> srcFile(do_QueryInterface(srcSupport));
7677 if (srcFile)
7678 (void)CopyFileToOfflineStore(srcFile, m_copyState->m_appendUID);
7679 }
7680 m_copyState = nullptr;
7681 nsresult result;
7682 nsCOMPtr<nsIMsgCopyService> copyService =
7683 do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &result);
7684 NS_ENSURE_SUCCESS(result, result);
7685 return copyService->NotifyCompletion(srcSupport, this, rv);
7686 }
7687
CreateBaseMessageURI(const nsACString & aURI)7688 nsresult nsImapMailFolder::CreateBaseMessageURI(const nsACString& aURI) {
7689 return nsCreateImapBaseMessageURI(aURI, mBaseMessageURI);
7690 }
7691
GetFolderURL(nsACString & aFolderURL)7692 NS_IMETHODIMP nsImapMailFolder::GetFolderURL(nsACString& aFolderURL) {
7693 nsCOMPtr<nsIMsgFolder> rootFolder;
7694 nsresult rv = GetRootFolder(getter_AddRefs(rootFolder));
7695 NS_ENSURE_SUCCESS(rv, rv);
7696 rootFolder->GetURI(aFolderURL);
7697 if (rootFolder == this) return NS_OK;
7698
7699 NS_ASSERTION(mURI.Length() > aFolderURL.Length(),
7700 "Should match with a folder name!");
7701 nsCString escapedName;
7702 MsgEscapeString(Substring(mURI, aFolderURL.Length()),
7703 nsINetUtil::ESCAPE_URL_PATH, escapedName);
7704 if (escapedName.IsEmpty()) return NS_ERROR_OUT_OF_MEMORY;
7705 aFolderURL.Append(escapedName);
7706 return NS_OK;
7707 }
7708
GetFolderNeedsSubscribing(bool * bVal)7709 NS_IMETHODIMP nsImapMailFolder::GetFolderNeedsSubscribing(bool* bVal) {
7710 NS_ENSURE_ARG_POINTER(bVal);
7711 *bVal = m_folderNeedsSubscribing;
7712 return NS_OK;
7713 }
7714
SetFolderNeedsSubscribing(bool bVal)7715 NS_IMETHODIMP nsImapMailFolder::SetFolderNeedsSubscribing(bool bVal) {
7716 m_folderNeedsSubscribing = bVal;
7717 return NS_OK;
7718 }
7719
GetFolderACL()7720 nsMsgIMAPFolderACL* nsImapMailFolder::GetFolderACL() {
7721 if (!m_folderACL) m_folderACL = new nsMsgIMAPFolderACL(this);
7722 return m_folderACL;
7723 }
7724
CreateACLRightsStringForFolder(nsAString & rightsString)7725 nsresult nsImapMailFolder::CreateACLRightsStringForFolder(
7726 nsAString& rightsString) {
7727 GetFolderACL(); // lazy create
7728 NS_ENSURE_TRUE(m_folderACL, NS_ERROR_NULL_POINTER);
7729 return m_folderACL->CreateACLRightsString(rightsString);
7730 }
7731
GetFolderNeedsACLListed(bool * bVal)7732 NS_IMETHODIMP nsImapMailFolder::GetFolderNeedsACLListed(bool* bVal) {
7733 NS_ENSURE_ARG_POINTER(bVal);
7734 bool dontNeedACLListed = !m_folderNeedsACLListed;
7735 // if we haven't acl listed, and it's not a no select folder or the inbox,
7736 // then we'll list the acl if it's not a namespace.
7737 if (m_folderNeedsACLListed &&
7738 !(mFlags & (nsMsgFolderFlags::ImapNoselect | nsMsgFolderFlags::Inbox)))
7739 GetIsNamespace(&dontNeedACLListed);
7740 *bVal = !dontNeedACLListed;
7741 return NS_OK;
7742 }
7743
SetFolderNeedsACLListed(bool bVal)7744 NS_IMETHODIMP nsImapMailFolder::SetFolderNeedsACLListed(bool bVal) {
7745 m_folderNeedsACLListed = bVal;
7746 return NS_OK;
7747 }
7748
GetIsNamespace(bool * aResult)7749 NS_IMETHODIMP nsImapMailFolder::GetIsNamespace(bool* aResult) {
7750 NS_ENSURE_ARG_POINTER(aResult);
7751 nsresult rv = NS_OK;
7752 if (!m_namespace) {
7753 #ifdef DEBUG_bienvenu
7754 // Make sure this isn't causing us to open the database
7755 NS_ASSERTION(m_hierarchyDelimiter != kOnlineHierarchySeparatorUnknown,
7756 "hierarchy delimiter not set");
7757 #endif
7758
7759 nsCString onlineName, serverKey;
7760 GetServerKey(serverKey);
7761 GetOnlineName(onlineName);
7762 char hierarchyDelimiter;
7763 GetHierarchyDelimiter(&hierarchyDelimiter);
7764
7765 nsCOMPtr<nsIImapHostSessionList> hostSession =
7766 do_GetService(kCImapHostSessionList, &rv);
7767 NS_ENSURE_SUCCESS(rv, rv);
7768 m_namespace = nsImapNamespaceList::GetNamespaceForFolder(
7769 serverKey.get(), onlineName.get(), hierarchyDelimiter);
7770 if (m_namespace == nullptr) {
7771 if (mFlags & nsMsgFolderFlags::ImapOtherUser)
7772 rv = hostSession->GetDefaultNamespaceOfTypeForHost(
7773 serverKey.get(), kOtherUsersNamespace, m_namespace);
7774 else if (mFlags & nsMsgFolderFlags::ImapPublic)
7775 rv = hostSession->GetDefaultNamespaceOfTypeForHost(
7776 serverKey.get(), kPublicNamespace, m_namespace);
7777 else
7778 rv = hostSession->GetDefaultNamespaceOfTypeForHost(
7779 serverKey.get(), kPersonalNamespace, m_namespace);
7780 }
7781 NS_ASSERTION(m_namespace, "failed to get namespace");
7782 if (m_namespace) {
7783 nsImapNamespaceList::SuggestHierarchySeparatorForNamespace(
7784 m_namespace, hierarchyDelimiter);
7785 m_folderIsNamespace = nsImapNamespaceList::GetFolderIsNamespace(
7786 serverKey.get(), onlineName.get(), hierarchyDelimiter, m_namespace);
7787 }
7788 }
7789 *aResult = m_folderIsNamespace;
7790 return rv;
7791 }
7792
SetIsNamespace(bool isNamespace)7793 NS_IMETHODIMP nsImapMailFolder::SetIsNamespace(bool isNamespace) {
7794 m_folderIsNamespace = isNamespace;
7795 return NS_OK;
7796 }
7797
ResetNamespaceReferences()7798 NS_IMETHODIMP nsImapMailFolder::ResetNamespaceReferences() {
7799 nsCString serverKey;
7800 nsCString onlineName;
7801 GetServerKey(serverKey);
7802 GetOnlineName(onlineName);
7803 char hierarchyDelimiter;
7804 GetHierarchyDelimiter(&hierarchyDelimiter);
7805 m_namespace = nsImapNamespaceList::GetNamespaceForFolder(
7806 serverKey.get(), onlineName.get(), hierarchyDelimiter);
7807 m_folderIsNamespace = m_namespace ? nsImapNamespaceList::GetFolderIsNamespace(
7808 serverKey.get(), onlineName.get(),
7809 hierarchyDelimiter, m_namespace)
7810 : false;
7811
7812 nsTArray<RefPtr<nsIMsgFolder>> subFolders;
7813 nsresult rv = GetSubFolders(subFolders);
7814 NS_ENSURE_SUCCESS(rv, rv);
7815
7816 for (nsIMsgFolder* f : subFolders) {
7817 nsCOMPtr<nsIMsgImapMailFolder> imapFolder(do_QueryInterface(f, &rv));
7818 NS_ENSURE_SUCCESS(rv, rv);
7819 rv = imapFolder->ResetNamespaceReferences();
7820 NS_ENSURE_SUCCESS(rv, rv);
7821 }
7822 return NS_OK;
7823 }
7824
FindOnlineSubFolder(const nsACString & targetOnlineName,nsIMsgImapMailFolder ** aResultFolder)7825 NS_IMETHODIMP nsImapMailFolder::FindOnlineSubFolder(
7826 const nsACString& targetOnlineName, nsIMsgImapMailFolder** aResultFolder) {
7827 *aResultFolder = nullptr;
7828 nsresult rv = NS_OK;
7829
7830 nsCString onlineName;
7831 GetOnlineName(onlineName);
7832
7833 if (onlineName.Equals(targetOnlineName)) {
7834 return QueryInterface(NS_GET_IID(nsIMsgImapMailFolder),
7835 (void**)aResultFolder);
7836 }
7837
7838 nsTArray<RefPtr<nsIMsgFolder>> subFolders;
7839 rv = GetSubFolders(subFolders);
7840 NS_ENSURE_SUCCESS(rv, rv);
7841 for (nsIMsgFolder* f : subFolders) {
7842 nsCOMPtr<nsIMsgImapMailFolder> imapFolder(do_QueryInterface(f, &rv));
7843 NS_ENSURE_SUCCESS(rv, rv);
7844 rv = imapFolder->FindOnlineSubFolder(targetOnlineName, aResultFolder);
7845 NS_ENSURE_SUCCESS(rv, rv);
7846 if (*aResultFolder) {
7847 return NS_OK; // Found it!
7848 }
7849 }
7850 return NS_OK; // Not found.
7851 }
7852
GetFolderNeedsAdded(bool * bVal)7853 NS_IMETHODIMP nsImapMailFolder::GetFolderNeedsAdded(bool* bVal) {
7854 NS_ENSURE_ARG_POINTER(bVal);
7855 *bVal = m_folderNeedsAdded;
7856 return NS_OK;
7857 }
7858
SetFolderNeedsAdded(bool bVal)7859 NS_IMETHODIMP nsImapMailFolder::SetFolderNeedsAdded(bool bVal) {
7860 m_folderNeedsAdded = bVal;
7861 return NS_OK;
7862 }
7863
GetFolderQuotaCommandIssued(bool * aCmdIssued)7864 NS_IMETHODIMP nsImapMailFolder::GetFolderQuotaCommandIssued(bool* aCmdIssued) {
7865 NS_ENSURE_ARG_POINTER(aCmdIssued);
7866 *aCmdIssued = m_folderQuotaCommandIssued;
7867 return NS_OK;
7868 }
7869
SetFolderQuotaCommandIssued(bool aCmdIssued)7870 NS_IMETHODIMP nsImapMailFolder::SetFolderQuotaCommandIssued(bool aCmdIssued) {
7871 m_folderQuotaCommandIssued = aCmdIssued;
7872 return NS_OK;
7873 }
7874
SetFolderQuotaData(uint32_t aAction,const nsACString & aFolderQuotaRoot,uint64_t aFolderQuotaUsage,uint64_t aFolderQuotaLimit)7875 NS_IMETHODIMP nsImapMailFolder::SetFolderQuotaData(
7876 uint32_t aAction, const nsACString& aFolderQuotaRoot,
7877 uint64_t aFolderQuotaUsage, uint64_t aFolderQuotaLimit) {
7878 switch (aAction) {
7879 case kInvalidateQuota:
7880 // Reset to initialize evaluation of a new quotaroot imap response. This
7881 // clears any previous array data and marks the quota data for this folder
7882 // invalid.
7883 m_folderQuotaDataIsValid = false;
7884 m_folderQuota.Clear();
7885 break;
7886 case kStoreQuota:
7887 // Store folder's quota data to an array. This will occur zero or more
7888 // times for a folder.
7889 m_folderQuota.AppendElement(new nsMsgQuota(
7890 aFolderQuotaRoot, aFolderQuotaUsage, aFolderQuotaLimit));
7891 break;
7892 case kValidateQuota:
7893 // GETQUOTAROOT command was successful and OK response has occurred. This
7894 // indicates that all the untagged QUOTA responses have occurred so mark
7895 // as valid.
7896 m_folderQuotaDataIsValid = true;
7897 break;
7898 default:
7899 // Called with undefined aAction parameter.
7900 NS_ASSERTION(false, "undefined action");
7901 }
7902 return NS_OK;
7903 }
7904
7905 // Provide the quota array for status bar notification.
GetQuota(nsTArray<RefPtr<nsIMsgQuota>> & aArray)7906 NS_IMETHODIMP nsImapMailFolder::GetQuota(
7907 nsTArray<RefPtr<nsIMsgQuota>>& aArray) {
7908 if (m_folderQuotaDataIsValid) {
7909 aArray = m_folderQuota.Clone();
7910 }
7911 return NS_OK;
7912 }
7913
PerformExpand(nsIMsgWindow * aMsgWindow)7914 NS_IMETHODIMP nsImapMailFolder::PerformExpand(nsIMsgWindow* aMsgWindow) {
7915 nsresult rv;
7916 bool usingSubscription = false;
7917 nsCOMPtr<nsIImapIncomingServer> imapServer;
7918 rv = GetImapIncomingServer(getter_AddRefs(imapServer));
7919 NS_ENSURE_SUCCESS(rv, rv);
7920 rv = imapServer->GetUsingSubscription(&usingSubscription);
7921 if (NS_SUCCEEDED(rv) && !usingSubscription) {
7922 nsCOMPtr<nsIImapService> imapService =
7923 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
7924 NS_ENSURE_SUCCESS(rv, rv);
7925 rv = imapService->DiscoverChildren(this, this, m_onlineFolderName, nullptr);
7926 }
7927 return rv;
7928 }
7929
RenameClient(nsIMsgWindow * msgWindow,nsIMsgFolder * msgFolder,const nsACString & oldName,const nsACString & newName)7930 NS_IMETHODIMP nsImapMailFolder::RenameClient(nsIMsgWindow* msgWindow,
7931 nsIMsgFolder* msgFolder,
7932 const nsACString& oldName,
7933 const nsACString& newName) {
7934 nsresult rv;
7935 nsCOMPtr<nsIFile> pathFile;
7936 rv = GetFilePath(getter_AddRefs(pathFile));
7937 if (NS_FAILED(rv)) return rv;
7938
7939 nsCOMPtr<nsIMsgImapMailFolder> oldImapFolder =
7940 do_QueryInterface(msgFolder, &rv);
7941 if (NS_FAILED(rv)) return rv;
7942
7943 char hierarchyDelimiter = '/';
7944 oldImapFolder->GetHierarchyDelimiter(&hierarchyDelimiter);
7945 int32_t boxflags = 0;
7946 oldImapFolder->GetBoxFlags(&boxflags);
7947
7948 nsAutoString newLeafName;
7949 NS_ConvertUTF8toUTF16 newNameString(newName);
7950 NS_ENSURE_SUCCESS(rv, rv);
7951 newLeafName = newNameString;
7952 nsAutoString folderNameStr;
7953 int32_t folderStart = newLeafName.RFindChar(
7954 '/'); // internal use of hierarchyDelimiter is always '/'
7955 if (folderStart > 0) {
7956 newLeafName = Substring(newNameString, folderStart + 1);
7957 CreateDirectoryForFolder(
7958 getter_AddRefs(pathFile)); // needed when we move a folder to a folder
7959 // with no subfolders.
7960 }
7961
7962 // if we get here, it's really a leaf, and "this" is the parent.
7963 folderNameStr = newLeafName;
7964
7965 // Create an empty database for this mail folder, set its name from the user
7966 nsCOMPtr<nsIMsgDatabase> mailDBFactory;
7967 nsCOMPtr<nsIMsgFolder> child;
7968 nsCOMPtr<nsIMsgImapMailFolder> imapFolder;
7969
7970 nsCOMPtr<nsIMsgDBService> msgDBService =
7971 do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
7972 NS_ENSURE_SUCCESS(rv, rv);
7973
7974 nsCOMPtr<nsIMsgDatabase> unusedDB;
7975 nsCOMPtr<nsIFile> dbFile;
7976
7977 // warning, path will be changed
7978 rv = CreateFileForDB(folderNameStr, pathFile, getter_AddRefs(dbFile));
7979 NS_ENSURE_SUCCESS(rv, rv);
7980
7981 // Use openMailDBFromFile() and not OpenFolderDB() here, since we don't use
7982 // the DB.
7983 rv = msgDBService->OpenMailDBFromFile(dbFile, nullptr, true, true,
7984 getter_AddRefs(unusedDB));
7985 if (NS_SUCCEEDED(rv) && unusedDB) {
7986 // need to set the folder name
7987 nsCOMPtr<nsIDBFolderInfo> folderInfo;
7988 rv = unusedDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
7989
7990 // Now let's create the actual new folder
7991 rv = AddSubfolderWithPath(folderNameStr, dbFile, getter_AddRefs(child));
7992 if (!child || NS_FAILED(rv)) return rv;
7993 nsAutoString unicodeName;
7994 rv = CopyFolderNameToUTF16(NS_ConvertUTF16toUTF8(folderNameStr),
7995 unicodeName);
7996 if (NS_SUCCEEDED(rv)) child->SetPrettyName(unicodeName);
7997 imapFolder = do_QueryInterface(child);
7998 if (imapFolder) {
7999 nsAutoCString onlineName(m_onlineFolderName);
8000
8001 if (!onlineName.IsEmpty()) onlineName.Append(hierarchyDelimiter);
8002 onlineName.Append(NS_ConvertUTF16toUTF8(folderNameStr));
8003 imapFolder->SetVerifiedAsOnlineFolder(true);
8004 imapFolder->SetOnlineName(onlineName);
8005 imapFolder->SetHierarchyDelimiter(hierarchyDelimiter);
8006 imapFolder->SetBoxFlags(boxflags);
8007 // store the online name as the mailbox name in the db folder info
8008 // I don't think anyone uses the mailbox name, so we'll use it
8009 // to restore the online name when blowing away an imap db.
8010 if (folderInfo) {
8011 nsAutoString unicodeOnlineName;
8012 CopyUTF8toUTF16(onlineName, unicodeOnlineName);
8013 folderInfo->SetMailboxName(unicodeOnlineName);
8014 }
8015 bool changed = false;
8016 msgFolder->MatchOrChangeFilterDestination(
8017 child, false /*caseInsensitive*/, &changed);
8018 if (changed) msgFolder->AlertFilterChanged(msgWindow);
8019 }
8020 unusedDB->SetSummaryValid(true);
8021 unusedDB->Commit(nsMsgDBCommitType::kLargeCommit);
8022 unusedDB->Close(true);
8023 child->RenameSubFolders(msgWindow, msgFolder);
8024 nsCOMPtr<nsIMsgFolder> msgParent;
8025 msgFolder->GetParent(getter_AddRefs(msgParent));
8026 msgFolder->SetParent(nullptr);
8027 // Reset online status now that the folder is renamed.
8028 nsCOMPtr<nsIMsgImapMailFolder> oldImapFolder = do_QueryInterface(msgFolder);
8029 if (oldImapFolder) oldImapFolder->SetVerifiedAsOnlineFolder(false);
8030 nsCOMPtr<nsIMsgFolderNotificationService> notifier(
8031 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID));
8032 if (notifier) notifier->NotifyFolderRenamed(msgFolder, child);
8033
8034 // Do not propagate the deletion until after we have (synchronously)
8035 // notified all listeners about the rename. This allows them to access
8036 // properties on the source folder without experiencing failures.
8037 if (msgParent) msgParent->PropagateDelete(msgFolder, true, nullptr);
8038 NotifyItemAdded(child);
8039 }
8040 return rv;
8041 }
8042
RenameSubFolders(nsIMsgWindow * msgWindow,nsIMsgFolder * oldFolder)8043 NS_IMETHODIMP nsImapMailFolder::RenameSubFolders(nsIMsgWindow* msgWindow,
8044 nsIMsgFolder* oldFolder) {
8045 m_initialized = true;
8046 nsTArray<RefPtr<nsIMsgFolder>> subFolders;
8047 nsresult rv = oldFolder->GetSubFolders(subFolders);
8048 NS_ENSURE_SUCCESS(rv, rv);
8049
8050 for (nsIMsgFolder* msgFolder : subFolders) {
8051 nsCOMPtr<nsIMsgImapMailFolder> folder(do_QueryInterface(msgFolder, &rv));
8052 NS_ENSURE_SUCCESS(rv, rv);
8053
8054 char hierarchyDelimiter = '/';
8055 folder->GetHierarchyDelimiter(&hierarchyDelimiter);
8056
8057 int32_t boxflags;
8058 folder->GetBoxFlags(&boxflags);
8059
8060 bool verified;
8061 folder->GetVerifiedAsOnlineFolder(&verified);
8062
8063 nsCOMPtr<nsIFile> oldPathFile;
8064 rv = msgFolder->GetFilePath(getter_AddRefs(oldPathFile));
8065 if (NS_FAILED(rv)) return rv;
8066
8067 nsCOMPtr<nsIFile> newParentPathFile;
8068 rv = GetFilePath(getter_AddRefs(newParentPathFile));
8069 if (NS_FAILED(rv)) return rv;
8070
8071 rv = AddDirectorySeparator(newParentPathFile);
8072 nsAutoCString oldLeafName;
8073 oldPathFile->GetNativeLeafName(oldLeafName);
8074 newParentPathFile->AppendNative(oldLeafName);
8075
8076 nsCOMPtr<nsIFile> newPathFile =
8077 do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
8078 NS_ENSURE_SUCCESS(rv, rv);
8079 newPathFile->InitWithFile(newParentPathFile);
8080
8081 nsCOMPtr<nsIFile> dbFilePath = newPathFile;
8082
8083 nsCOMPtr<nsIMsgFolder> child;
8084
8085 nsString folderName;
8086 rv = msgFolder->GetName(folderName);
8087 if (folderName.IsEmpty() || NS_FAILED(rv)) return rv;
8088
8089 nsCString utfLeafName;
8090 bool utf8AcceptEnabled;
8091 nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(msgFolder);
8092 rv = imapFolder->GetShouldUseUtf8FolderName(&utf8AcceptEnabled);
8093 NS_ENSURE_SUCCESS(rv, rv);
8094 if (utf8AcceptEnabled) {
8095 CopyUTF16toUTF8(folderName, utfLeafName);
8096 } else {
8097 CopyUTF16toMUTF7(folderName, utfLeafName);
8098 }
8099
8100 // XXX : Fix this non-sense by fixing AddSubfolderWithPath
8101 nsAutoString unicodeLeafName;
8102 CopyUTF8toUTF16(utfLeafName, unicodeLeafName);
8103
8104 rv = AddSubfolderWithPath(unicodeLeafName, dbFilePath,
8105 getter_AddRefs(child));
8106 if (!child || NS_FAILED(rv)) return rv;
8107
8108 child->SetName(folderName);
8109 imapFolder = do_QueryInterface(child);
8110 nsCString onlineName;
8111 GetOnlineName(onlineName);
8112 nsAutoCString onlineCName(onlineName);
8113 onlineCName.Append(hierarchyDelimiter);
8114 onlineCName.Append(utfLeafName);
8115 if (imapFolder) {
8116 imapFolder->SetVerifiedAsOnlineFolder(verified);
8117 imapFolder->SetOnlineName(onlineCName);
8118 imapFolder->SetHierarchyDelimiter(hierarchyDelimiter);
8119 imapFolder->SetBoxFlags(boxflags);
8120
8121 bool changed = false;
8122 msgFolder->MatchOrChangeFilterDestination(
8123 child, false /*caseInsensitive*/, &changed);
8124 if (changed) msgFolder->AlertFilterChanged(msgWindow);
8125 child->RenameSubFolders(msgWindow, msgFolder);
8126 }
8127 }
8128 return rv;
8129 }
8130
IsCommandEnabled(const nsACString & command,bool * result)8131 NS_IMETHODIMP nsImapMailFolder::IsCommandEnabled(const nsACString& command,
8132 bool* result) {
8133 NS_ENSURE_ARG_POINTER(result);
8134 *result = !(WeAreOffline() && (command.EqualsLiteral("cmd_renameFolder") ||
8135 command.EqualsLiteral("cmd_compactFolder") ||
8136 command.EqualsLiteral("button_compact") ||
8137 command.EqualsLiteral("cmd_delete") ||
8138 command.EqualsLiteral("button_delete")));
8139 return NS_OK;
8140 }
8141
8142 NS_IMETHODIMP
GetCanFileMessages(bool * aCanFileMessages)8143 nsImapMailFolder::GetCanFileMessages(bool* aCanFileMessages) {
8144 nsresult rv;
8145 *aCanFileMessages = true;
8146
8147 nsCOMPtr<nsIMsgIncomingServer> server;
8148 rv = GetServer(getter_AddRefs(server));
8149 if (NS_SUCCEEDED(rv) && server)
8150 rv = server->GetCanFileMessagesOnServer(aCanFileMessages);
8151
8152 if (*aCanFileMessages)
8153 rv = nsMsgDBFolder::GetCanFileMessages(aCanFileMessages);
8154
8155 if (*aCanFileMessages) {
8156 bool noSelect;
8157 GetFlag(nsMsgFolderFlags::ImapNoselect, &noSelect);
8158 *aCanFileMessages =
8159 (noSelect) ? false : GetFolderACL()->GetCanIInsertInFolder();
8160 return NS_OK;
8161 }
8162 return rv;
8163 }
8164
8165 NS_IMETHODIMP
GetCanDeleteMessages(bool * aCanDeleteMessages)8166 nsImapMailFolder::GetCanDeleteMessages(bool* aCanDeleteMessages) {
8167 NS_ENSURE_ARG_POINTER(aCanDeleteMessages);
8168 *aCanDeleteMessages = GetFolderACL()->GetCanIDeleteInFolder();
8169 return NS_OK;
8170 }
8171
8172 NS_IMETHODIMP
GetPerformingBiff(bool * aPerformingBiff)8173 nsImapMailFolder::GetPerformingBiff(bool* aPerformingBiff) {
8174 NS_ENSURE_ARG_POINTER(aPerformingBiff);
8175 *aPerformingBiff = m_performingBiff;
8176 return NS_OK;
8177 }
8178
8179 NS_IMETHODIMP
SetPerformingBiff(bool aPerformingBiff)8180 nsImapMailFolder::SetPerformingBiff(bool aPerformingBiff) {
8181 m_performingBiff = aPerformingBiff;
8182 return NS_OK;
8183 }
8184
8185 NS_IMETHODIMP
SetFilterList(nsIMsgFilterList * aMsgFilterList)8186 nsImapMailFolder::SetFilterList(nsIMsgFilterList* aMsgFilterList) {
8187 m_filterList = aMsgFilterList;
8188 return nsMsgDBFolder::SetFilterList(aMsgFilterList);
8189 }
8190
GetMoveCoalescer()8191 nsresult nsImapMailFolder::GetMoveCoalescer() {
8192 if (!m_moveCoalescer)
8193 m_moveCoalescer = new nsImapMoveCoalescer(this, nullptr /* msgWindow */);
8194 return NS_OK;
8195 }
8196
8197 NS_IMETHODIMP
StoreCustomKeywords(nsIMsgWindow * aMsgWindow,const nsACString & aFlagsToAdd,const nsACString & aFlagsToSubtract,const nsTArray<nsMsgKey> & aKeysToStore,nsIURI ** _retval)8198 nsImapMailFolder::StoreCustomKeywords(nsIMsgWindow* aMsgWindow,
8199 const nsACString& aFlagsToAdd,
8200 const nsACString& aFlagsToSubtract,
8201 const nsTArray<nsMsgKey>& aKeysToStore,
8202 nsIURI** _retval) {
8203 if (aKeysToStore.IsEmpty()) return NS_OK;
8204 nsresult rv = NS_OK;
8205 if (WeAreOffline()) {
8206 GetDatabase();
8207 if (!mDatabase) return NS_ERROR_UNEXPECTED;
8208 for (auto key : aKeysToStore) {
8209 nsCOMPtr<nsIMsgOfflineImapOperation> op;
8210 nsresult rv2 =
8211 mDatabase->GetOfflineOpForKey(key, true, getter_AddRefs(op));
8212 if (NS_FAILED(rv2)) rv = rv2;
8213 SetFlag(nsMsgFolderFlags::OfflineEvents);
8214 if (NS_SUCCEEDED(rv2) && op) {
8215 if (!aFlagsToAdd.IsEmpty())
8216 op->AddKeywordToAdd(PromiseFlatCString(aFlagsToAdd).get());
8217 if (!aFlagsToSubtract.IsEmpty())
8218 op->AddKeywordToRemove(PromiseFlatCString(aFlagsToSubtract).get());
8219 }
8220 }
8221 mDatabase->Commit(nsMsgDBCommitType::kLargeCommit); // flush offline ops
8222 return rv;
8223 }
8224
8225 nsCOMPtr<nsIImapService> imapService(
8226 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv));
8227 NS_ENSURE_SUCCESS(rv, rv);
8228 nsAutoCString msgIds;
8229 AllocateUidStringFromKeys(aKeysToStore, msgIds);
8230 return imapService->StoreCustomKeywords(this, aMsgWindow, aFlagsToAdd,
8231 aFlagsToSubtract, msgIds, _retval);
8232 }
8233
NotifyIfNewMail()8234 NS_IMETHODIMP nsImapMailFolder::NotifyIfNewMail() {
8235 return PerformBiffNotifications();
8236 }
8237
ShowPreviewText()8238 bool nsImapMailFolder::ShowPreviewText() {
8239 bool showPreviewText = false;
8240 nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
8241 if (prefBranch)
8242 prefBranch->GetBoolPref("mail.biff.alert.show_preview", &showPreviewText);
8243 return showPreviewText;
8244 }
8245
PlaybackCoalescedOperations()8246 nsresult nsImapMailFolder::PlaybackCoalescedOperations() {
8247 if (m_moveCoalescer) {
8248 nsTArray<nsMsgKey>* junkKeysToClassify = m_moveCoalescer->GetKeyBucket(0);
8249 if (junkKeysToClassify && !junkKeysToClassify->IsEmpty())
8250 StoreCustomKeywords(m_moveCoalescer->GetMsgWindow(), "Junk"_ns,
8251 EmptyCString(), *junkKeysToClassify, nullptr);
8252 junkKeysToClassify->Clear();
8253 nsTArray<nsMsgKey>* nonJunkKeysToClassify =
8254 m_moveCoalescer->GetKeyBucket(1);
8255 if (nonJunkKeysToClassify && !nonJunkKeysToClassify->IsEmpty())
8256 StoreCustomKeywords(m_moveCoalescer->GetMsgWindow(), "NonJunk"_ns,
8257 EmptyCString(), *nonJunkKeysToClassify, nullptr);
8258 nonJunkKeysToClassify->Clear();
8259 return m_moveCoalescer->PlaybackMoves(ShowPreviewText());
8260 }
8261 return NS_OK; // must not be any coalesced operations
8262 }
8263
8264 NS_IMETHODIMP
SetJunkScoreForMessages(const nsTArray<RefPtr<nsIMsgDBHdr>> & aMessages,const nsACString & aJunkScore)8265 nsImapMailFolder::SetJunkScoreForMessages(
8266 const nsTArray<RefPtr<nsIMsgDBHdr>>& aMessages,
8267 const nsACString& aJunkScore) {
8268 nsresult rv = nsMsgDBFolder::SetJunkScoreForMessages(aMessages, aJunkScore);
8269 if (NS_SUCCEEDED(rv)) {
8270 nsAutoCString messageIds;
8271 nsTArray<nsMsgKey> keys;
8272 nsresult rv = BuildIdsAndKeyArray(aMessages, messageIds, keys);
8273 NS_ENSURE_SUCCESS(rv, rv);
8274 StoreCustomKeywords(
8275 nullptr, aJunkScore.EqualsLiteral("0") ? "NonJunk"_ns : "Junk"_ns,
8276 aJunkScore.EqualsLiteral("0") ? "Junk"_ns : "NonJunk"_ns, keys,
8277 nullptr);
8278 if (mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
8279 }
8280 return rv;
8281 }
8282
8283 NS_IMETHODIMP
OnMessageClassified(const nsACString & aMsgURI,nsMsgJunkStatus aClassification,uint32_t aJunkPercent)8284 nsImapMailFolder::OnMessageClassified(const nsACString& aMsgURI,
8285 nsMsgJunkStatus aClassification,
8286 uint32_t aJunkPercent) {
8287 nsCOMPtr<nsIMsgIncomingServer> server;
8288 nsresult rv = GetServer(getter_AddRefs(server));
8289 NS_ENSURE_SUCCESS(rv, rv);
8290
8291 if (!aMsgURI.IsEmpty()) // not end of batch
8292 {
8293 nsCOMPtr<nsIMsgDBHdr> msgHdr;
8294 rv = GetMsgDBHdrFromURI(aMsgURI, getter_AddRefs(msgHdr));
8295 NS_ENSURE_SUCCESS(rv, rv);
8296
8297 nsMsgKey msgKey;
8298 rv = msgHdr->GetMessageKey(&msgKey);
8299 NS_ENSURE_SUCCESS(rv, rv);
8300
8301 // check if this message needs junk classification
8302
8303 uint32_t processingFlags;
8304 GetProcessingFlags(msgKey, &processingFlags);
8305
8306 if (processingFlags & nsMsgProcessingFlags::ClassifyJunk) {
8307 nsMsgDBFolder::OnMessageClassified(aMsgURI, aClassification,
8308 aJunkPercent);
8309
8310 GetMoveCoalescer();
8311 if (m_moveCoalescer) {
8312 nsTArray<nsMsgKey>* keysToClassify = m_moveCoalescer->GetKeyBucket(
8313 (aClassification == nsIJunkMailPlugin::JUNK) ? 0 : 1);
8314 NS_ASSERTION(keysToClassify, "error getting key bucket");
8315 if (keysToClassify) keysToClassify->AppendElement(msgKey);
8316 }
8317 if (aClassification == nsIJunkMailPlugin::JUNK) {
8318 nsCOMPtr<nsISpamSettings> spamSettings;
8319 rv = server->GetSpamSettings(getter_AddRefs(spamSettings));
8320 NS_ENSURE_SUCCESS(rv, rv);
8321
8322 bool markAsReadOnSpam;
8323 (void)spamSettings->GetMarkAsReadOnSpam(&markAsReadOnSpam);
8324 if (markAsReadOnSpam) {
8325 m_junkMessagesToMarkAsRead.AppendElement(msgHdr);
8326 }
8327
8328 bool willMoveMessage = false;
8329
8330 // don't do the move when we are opening up
8331 // the junk mail folder or the trash folder
8332 // or when manually classifying messages in those folders
8333 if (!(mFlags & nsMsgFolderFlags::Junk ||
8334 mFlags & nsMsgFolderFlags::Trash)) {
8335 bool moveOnSpam;
8336 (void)spamSettings->GetMoveOnSpam(&moveOnSpam);
8337 if (moveOnSpam) {
8338 nsCString spamFolderURI;
8339 rv = spamSettings->GetSpamFolderURI(spamFolderURI);
8340 NS_ENSURE_SUCCESS(rv, rv);
8341
8342 if (!spamFolderURI.IsEmpty()) {
8343 rv = FindFolder(spamFolderURI, getter_AddRefs(mSpamFolder));
8344 NS_ENSURE_SUCCESS(rv, rv);
8345 if (mSpamFolder) {
8346 rv = mSpamFolder->SetFlag(nsMsgFolderFlags::Junk);
8347 NS_ENSURE_SUCCESS(rv, rv);
8348 mSpamKeysToMove.AppendElement(msgKey);
8349 willMoveMessage = true;
8350 } else {
8351 // XXX TODO
8352 // JUNK MAIL RELATED
8353 // the listener should do
8354 // rv = folder->SetFlag(nsMsgFolderFlags::Junk);
8355 // NS_ENSURE_SUCCESS(rv,rv);
8356 // if (NS_SUCCEEDED(GetMoveCoalescer())) {
8357 // m_moveCoalescer->AddMove(folder, msgKey);
8358 // willMoveMessage = true;
8359 // }
8360 rv = GetOrCreateJunkFolder(spamFolderURI,
8361 nullptr /* aListener */);
8362 NS_ASSERTION(NS_SUCCEEDED(rv), "GetOrCreateJunkFolder failed");
8363 }
8364 }
8365 }
8366 }
8367 rv = spamSettings->LogJunkHit(msgHdr, willMoveMessage);
8368 NS_ENSURE_SUCCESS(rv, rv);
8369 }
8370 }
8371 }
8372
8373 else // end of batch
8374 {
8375 // Parent will apply post bayes filters.
8376 nsMsgDBFolder::OnMessageClassified(EmptyCString(),
8377 nsIJunkMailPlugin::UNCLASSIFIED, 0);
8378
8379 if (!m_junkMessagesToMarkAsRead.IsEmpty()) {
8380 rv = MarkMessagesRead(m_junkMessagesToMarkAsRead, true);
8381 NS_ENSURE_SUCCESS(rv, rv);
8382 m_junkMessagesToMarkAsRead.Clear();
8383 }
8384 if (!mSpamKeysToMove.IsEmpty()) {
8385 GetMoveCoalescer();
8386 for (uint32_t keyIndex = 0; keyIndex < mSpamKeysToMove.Length();
8387 keyIndex++) {
8388 // If an upstream filter moved this message, don't move it here.
8389 nsMsgKey msgKey = mSpamKeysToMove.ElementAt(keyIndex);
8390 nsMsgProcessingFlagType processingFlags;
8391 GetProcessingFlags(msgKey, &processingFlags);
8392 if (!(processingFlags & nsMsgProcessingFlags::FilterToMove)) {
8393 if (m_moveCoalescer && mSpamFolder)
8394 m_moveCoalescer->AddMove(mSpamFolder, msgKey);
8395 } else {
8396 // We don't need the FilterToMove flag anymore.
8397 AndProcessingFlags(msgKey, ~nsMsgProcessingFlags::FilterToMove);
8398 }
8399 }
8400 mSpamKeysToMove.Clear();
8401 }
8402
8403 // Let's not hold onto the spam folder reference longer than necessary.
8404 mSpamFolder = nullptr;
8405
8406 bool pendingMoves = m_moveCoalescer && m_moveCoalescer->HasPendingMoves();
8407 PlaybackCoalescedOperations();
8408 // If we are performing biff for this folder, tell the server object
8409 if ((!pendingMoves || !ShowPreviewText()) && m_performingBiff) {
8410 // we don't need to adjust the num new messages in this folder because
8411 // the playback moves code already did that.
8412 (void)PerformBiffNotifications();
8413 server->SetPerformingBiff(false);
8414 m_performingBiff = false;
8415 }
8416 }
8417 return NS_OK;
8418 }
8419
8420 NS_IMETHODIMP
GetShouldDownloadAllHeaders(bool * aResult)8421 nsImapMailFolder::GetShouldDownloadAllHeaders(bool* aResult) {
8422 NS_ENSURE_ARG_POINTER(aResult);
8423 *aResult = false;
8424 // for just the inbox, we check if the filter list has arbitrary headers.
8425 // for all folders, check if we have a spam plugin that requires all headers
8426 if (mFlags & nsMsgFolderFlags::Inbox) {
8427 nsCOMPtr<nsIMsgFilterList> filterList;
8428 nsresult rv = GetFilterList(nullptr, getter_AddRefs(filterList));
8429 NS_ENSURE_SUCCESS(rv, rv);
8430
8431 rv = filterList->GetShouldDownloadAllHeaders(aResult);
8432 if (*aResult) return rv;
8433 }
8434 nsCOMPtr<nsIMsgFilterPlugin> filterPlugin;
8435 nsCOMPtr<nsIMsgIncomingServer> server;
8436
8437 if (NS_SUCCEEDED(GetServer(getter_AddRefs(server))))
8438 server->GetSpamFilterPlugin(getter_AddRefs(filterPlugin));
8439
8440 return (filterPlugin) ? filterPlugin->GetShouldDownloadAllHeaders(aResult)
8441 : NS_OK;
8442 }
8443
GetTrashFolderName(nsAString & aFolderName)8444 void nsImapMailFolder::GetTrashFolderName(nsAString& aFolderName) {
8445 nsCOMPtr<nsIMsgIncomingServer> server;
8446 nsCOMPtr<nsIImapIncomingServer> imapServer;
8447 nsresult rv;
8448 rv = GetServer(getter_AddRefs(server));
8449 if (NS_FAILED(rv)) return;
8450 imapServer = do_QueryInterface(server, &rv);
8451 if (NS_FAILED(rv)) return;
8452 imapServer->GetTrashFolderName(aFolderName);
8453 return;
8454 }
FetchMsgPreviewText(nsTArray<nsMsgKey> const & aKeysToFetch,bool aLocalOnly,nsIUrlListener * aUrlListener,bool * aAsyncResults)8455 NS_IMETHODIMP nsImapMailFolder::FetchMsgPreviewText(
8456 nsTArray<nsMsgKey> const& aKeysToFetch, bool aLocalOnly,
8457 nsIUrlListener* aUrlListener, bool* aAsyncResults) {
8458 NS_ENSURE_ARG_POINTER(aAsyncResults);
8459
8460 nsTArray<nsMsgKey> keysToFetchFromServer;
8461
8462 *aAsyncResults = false;
8463 nsresult rv = NS_OK;
8464
8465 nsCOMPtr<nsIImapService> imapService =
8466 do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv);
8467 NS_ENSURE_SUCCESS(rv, rv);
8468 nsCOMPtr<nsIMsgMessageService> msgService =
8469 do_QueryInterface(imapService, &rv);
8470 NS_ENSURE_SUCCESS(rv, rv);
8471
8472 for (uint32_t i = 0; i < aKeysToFetch.Length(); i++) {
8473 nsCOMPtr<nsIMsgDBHdr> msgHdr;
8474 nsCString prevBody;
8475 rv = GetMessageHeader(aKeysToFetch[i], getter_AddRefs(msgHdr));
8476 NS_ENSURE_SUCCESS(rv, rv);
8477 // ignore messages that already have a preview body.
8478 msgHdr->GetStringProperty("preview", getter_Copies(prevBody));
8479 if (!prevBody.IsEmpty()) continue;
8480
8481 /* check if message is in memory cache or offline store. */
8482 nsCOMPtr<nsIURI> url;
8483 nsCOMPtr<nsIInputStream> inputStream;
8484 nsCString messageUri;
8485 rv = GetUriForMsg(msgHdr, messageUri);
8486 NS_ENSURE_SUCCESS(rv, rv);
8487 rv = msgService->GetUrlForUri(messageUri, nullptr, getter_AddRefs(url));
8488 NS_ENSURE_SUCCESS(rv, rv);
8489
8490 // Lets look in the offline store.
8491 uint32_t msgFlags;
8492 msgHdr->GetFlags(&msgFlags);
8493 nsMsgKey msgKey;
8494 msgHdr->GetMessageKey(&msgKey);
8495 if (msgFlags & nsMsgMessageFlags::Offline) {
8496 int64_t messageOffset;
8497 uint32_t messageSize;
8498 rv = GetOfflineFileStream(msgKey, &messageOffset, &messageSize,
8499 getter_AddRefs(inputStream));
8500 NS_ENSURE_SUCCESS(rv, rv);
8501 rv = GetMsgPreviewTextFromStream(msgHdr, inputStream);
8502 NS_ENSURE_SUCCESS(rv, rv);
8503 } else if (!aLocalOnly) {
8504 keysToFetchFromServer.AppendElement(msgKey);
8505 }
8506 }
8507 if (!keysToFetchFromServer.IsEmpty()) {
8508 uint32_t msgCount = keysToFetchFromServer.Length();
8509 nsAutoCString messageIds;
8510 AllocateImapUidString(keysToFetchFromServer.Elements(), msgCount, nullptr,
8511 messageIds);
8512 rv = imapService->GetBodyStart(this, aUrlListener, messageIds, 2048,
8513 nullptr);
8514 *aAsyncResults = true; // the preview text will be available async...
8515 }
8516 return NS_OK;
8517 }
8518
AddKeywordsToMessages(const nsTArray<RefPtr<nsIMsgDBHdr>> & aMessages,const nsACString & aKeywords)8519 NS_IMETHODIMP nsImapMailFolder::AddKeywordsToMessages(
8520 const nsTArray<RefPtr<nsIMsgDBHdr>>& aMessages,
8521 const nsACString& aKeywords) {
8522 nsresult rv = nsMsgDBFolder::AddKeywordsToMessages(aMessages, aKeywords);
8523 if (NS_SUCCEEDED(rv)) {
8524 nsAutoCString messageIds;
8525 nsTArray<nsMsgKey> keys;
8526 rv = BuildIdsAndKeyArray(aMessages, messageIds, keys);
8527 NS_ENSURE_SUCCESS(rv, rv);
8528 rv = StoreCustomKeywords(nullptr, aKeywords, EmptyCString(), keys, nullptr);
8529 if (mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
8530 }
8531 return rv;
8532 }
8533
RemoveKeywordsFromMessages(const nsTArray<RefPtr<nsIMsgDBHdr>> & aMessages,const nsACString & aKeywords)8534 NS_IMETHODIMP nsImapMailFolder::RemoveKeywordsFromMessages(
8535 const nsTArray<RefPtr<nsIMsgDBHdr>>& aMessages,
8536 const nsACString& aKeywords) {
8537 nsresult rv = nsMsgDBFolder::RemoveKeywordsFromMessages(aMessages, aKeywords);
8538 if (NS_SUCCEEDED(rv)) {
8539 nsAutoCString messageIds;
8540 nsTArray<nsMsgKey> keys;
8541 nsresult rv = BuildIdsAndKeyArray(aMessages, messageIds, keys);
8542 NS_ENSURE_SUCCESS(rv, rv);
8543 rv = StoreCustomKeywords(nullptr, EmptyCString(), aKeywords, keys, nullptr);
8544 if (mDatabase) mDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
8545 }
8546 return rv;
8547 }
8548
GetCustomIdentity(nsIMsgIdentity ** aIdentity)8549 NS_IMETHODIMP nsImapMailFolder::GetCustomIdentity(nsIMsgIdentity** aIdentity) {
8550 NS_ENSURE_ARG_POINTER(aIdentity);
8551 if (mFlags & nsMsgFolderFlags::ImapOtherUser) {
8552 nsresult rv;
8553 bool delegateOtherUsersFolders = false;
8554 nsCOMPtr<nsIPrefBranch> prefBranch(
8555 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
8556 NS_ENSURE_SUCCESS(rv, rv);
8557 prefBranch->GetBoolPref("mail.imap.delegateOtherUsersFolders",
8558 &delegateOtherUsersFolders);
8559 // if we're automatically delegating other user's folders, we need to
8560 // cons up an e-mail address for the other user. We do that by
8561 // taking the other user's name and the current user's domain name,
8562 // assuming they'll be the same. So, <otherUsersName>@<ourDomain>
8563 if (delegateOtherUsersFolders) {
8564 nsCOMPtr<nsIMsgIncomingServer> server = do_QueryReferent(mServer, &rv);
8565 NS_ENSURE_SUCCESS(rv, rv);
8566 nsCOMPtr<nsIMsgAccountManager> accountManager =
8567 do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
8568 NS_ENSURE_SUCCESS(rv, rv);
8569 nsCOMPtr<nsIMsgIdentity> ourIdentity;
8570 nsCOMPtr<nsIMsgIdentity> retIdentity;
8571 nsCOMPtr<nsIMsgAccount> account;
8572 nsCString foldersUserName;
8573 nsCString ourEmailAddress;
8574
8575 accountManager->FindAccountForServer(server, getter_AddRefs(account));
8576 NS_ENSURE_SUCCESS(rv, rv);
8577 account->GetDefaultIdentity(getter_AddRefs(ourIdentity));
8578 NS_ENSURE_SUCCESS(rv, rv);
8579 ourIdentity->GetEmail(ourEmailAddress);
8580 int32_t atPos = ourEmailAddress.FindChar('@');
8581 if (atPos != kNotFound) {
8582 nsCString otherUsersEmailAddress;
8583 GetFolderOwnerUserName(otherUsersEmailAddress);
8584 otherUsersEmailAddress.Append(
8585 Substring(ourEmailAddress, atPos, ourEmailAddress.Length()));
8586 nsTArray<RefPtr<nsIMsgIdentity>> identities;
8587 rv = accountManager->GetIdentitiesForServer(server, identities);
8588 NS_ENSURE_SUCCESS(rv, rv);
8589
8590 for (auto identity : identities) {
8591 if (!identity) continue;
8592 nsCString identityEmail;
8593 identity->GetEmail(identityEmail);
8594 if (identityEmail.Equals(otherUsersEmailAddress)) {
8595 retIdentity = identity;
8596 break;
8597 }
8598 }
8599 if (!retIdentity) {
8600 // create the identity
8601 rv = accountManager->CreateIdentity(getter_AddRefs(retIdentity));
8602 NS_ENSURE_SUCCESS(rv, rv);
8603 retIdentity->SetEmail(otherUsersEmailAddress);
8604 nsCOMPtr<nsIMsgAccount> account;
8605 accountManager->FindAccountForServer(server, getter_AddRefs(account));
8606 NS_ENSURE_SUCCESS(rv, rv);
8607 account->AddIdentity(retIdentity);
8608 }
8609 }
8610 if (retIdentity) {
8611 retIdentity.forget(aIdentity);
8612 return NS_OK;
8613 }
8614 }
8615 }
8616 return nsMsgDBFolder::GetCustomIdentity(aIdentity);
8617 }
8618
ChangePendingTotal(int32_t aDelta)8619 NS_IMETHODIMP nsImapMailFolder::ChangePendingTotal(int32_t aDelta) {
8620 ChangeNumPendingTotalMessages(aDelta);
8621 if (aDelta > 0) NotifyHasPendingMsgs();
8622 return NS_OK;
8623 }
8624
NotifyHasPendingMsgs()8625 void nsImapMailFolder::NotifyHasPendingMsgs() {
8626 InitAutoSyncState();
8627 nsresult rv;
8628 nsCOMPtr<nsIAutoSyncManager> autoSyncMgr =
8629 do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
8630 if (NS_SUCCEEDED(rv)) autoSyncMgr->OnFolderHasPendingMsgs(m_autoSyncStateObj);
8631 }
8632
8633 /* void changePendingUnread (in long aDelta); */
ChangePendingUnread(int32_t aDelta)8634 NS_IMETHODIMP nsImapMailFolder::ChangePendingUnread(int32_t aDelta) {
8635 ChangeNumPendingUnread(aDelta);
8636 return NS_OK;
8637 }
8638
GetServerRecent(int32_t * aServerRecent)8639 NS_IMETHODIMP nsImapMailFolder::GetServerRecent(int32_t* aServerRecent) {
8640 NS_ENSURE_ARG_POINTER(aServerRecent);
8641 *aServerRecent = m_numServerRecentMessages;
8642 return NS_OK;
8643 }
8644
GetServerTotal(int32_t * aServerTotal)8645 NS_IMETHODIMP nsImapMailFolder::GetServerTotal(int32_t* aServerTotal) {
8646 NS_ENSURE_ARG_POINTER(aServerTotal);
8647 *aServerTotal = m_numServerTotalMessages;
8648 return NS_OK;
8649 }
8650
GetServerUnseen(int32_t * aServerUnseen)8651 NS_IMETHODIMP nsImapMailFolder::GetServerUnseen(int32_t* aServerUnseen) {
8652 NS_ENSURE_ARG_POINTER(aServerUnseen);
8653 *aServerUnseen = m_numServerUnseenMessages;
8654 return NS_OK;
8655 }
8656
GetServerNextUID(int32_t * aNextUID)8657 NS_IMETHODIMP nsImapMailFolder::GetServerNextUID(int32_t* aNextUID) {
8658 NS_ENSURE_ARG_POINTER(aNextUID);
8659 *aNextUID = m_nextUID;
8660 return NS_OK;
8661 }
8662
GetAutoSyncStateObj(nsIAutoSyncState ** autoSyncStateObj)8663 NS_IMETHODIMP nsImapMailFolder::GetAutoSyncStateObj(
8664 nsIAutoSyncState** autoSyncStateObj) {
8665 NS_ENSURE_ARG_POINTER(autoSyncStateObj);
8666
8667 // create auto-sync state object lazily
8668 InitAutoSyncState();
8669
8670 NS_IF_ADDREF(*autoSyncStateObj = m_autoSyncStateObj);
8671 return NS_OK;
8672 }
8673
InitiateAutoSync(nsIUrlListener * aUrlListener)8674 NS_IMETHODIMP nsImapMailFolder::InitiateAutoSync(nsIUrlListener* aUrlListener) {
8675 nsCString folderName;
8676 GetURI(folderName);
8677 MOZ_LOG(gAutoSyncLog, mozilla::LogLevel::Debug,
8678 ("Updating folder: %s", folderName.get()));
8679
8680 // HACK: if UpdateFolder finds out that it can't open
8681 // the folder, it doesn't set the url listener and returns
8682 // no error. In this case, we return success from this call
8683 // but the caller never gets a notification on its url listener.
8684 bool canOpenThisFolder = true;
8685 GetCanOpenFolder(&canOpenThisFolder);
8686
8687 if (!canOpenThisFolder) {
8688 MOZ_LOG(gAutoSyncLog, mozilla::LogLevel::Debug,
8689 ("Cannot update folder: %s", folderName.get()));
8690 return NS_ERROR_FAILURE;
8691 }
8692
8693 // create auto-sync state object lazily
8694 InitAutoSyncState();
8695
8696 // make sure we get the counts from the folder cache.
8697 ReadDBFolderInfo(false);
8698
8699 nsresult rv = m_autoSyncStateObj->ManageStorageSpace();
8700 NS_ENSURE_SUCCESS(rv, rv);
8701
8702 int32_t syncState;
8703 m_autoSyncStateObj->GetState(&syncState);
8704 if (syncState == nsAutoSyncState::stUpdateNeeded)
8705 return m_autoSyncStateObj->UpdateFolder();
8706
8707 // We only want to init the autosyncStateObj server counts the first time
8708 // we update, and update it when the STATUS call finishes. This deals with
8709 // the case where biff is doing a STATUS on a non-inbox folder, which
8710 // can make autosync think the counts aren't changing.
8711 PRTime lastUpdateTime;
8712 m_autoSyncStateObj->GetLastUpdateTime(&lastUpdateTime);
8713 if (!lastUpdateTime)
8714 m_autoSyncStateObj->SetServerCounts(m_numServerTotalMessages,
8715 m_numServerRecentMessages,
8716 m_numServerUnseenMessages, m_nextUID);
8717 // Issue a STATUS command and see if any counts changed.
8718 m_autoSyncStateObj->SetState(nsAutoSyncState::stStatusIssued);
8719 // The OnStopRunningUrl method of the autosync state obj
8720 // will check if the counts or next uid have changed,
8721 // and if so, will issue an UpdateFolder().
8722 rv = UpdateStatus(m_autoSyncStateObj, nullptr);
8723 NS_ENSURE_SUCCESS(rv, rv);
8724
8725 // record the last update time
8726 m_autoSyncStateObj->SetLastUpdateTime(PR_Now());
8727
8728 return NS_OK;
8729 }
8730
CreatePlaybackTimer()8731 nsresult nsImapMailFolder::CreatePlaybackTimer() {
8732 nsresult rv = NS_OK;
8733 if (!m_playbackTimer) {
8734 m_playbackTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
8735 NS_ASSERTION(
8736 NS_SUCCEEDED(rv),
8737 "failed to create pseudo-offline operation timer in nsImapMailFolder");
8738 }
8739 return rv;
8740 }
8741
PlaybackTimerCallback(nsITimer * aTimer,void * aClosure)8742 void nsImapMailFolder::PlaybackTimerCallback(nsITimer* aTimer, void* aClosure) {
8743 nsPlaybackRequest* request = static_cast<nsPlaybackRequest*>(aClosure);
8744
8745 NS_ASSERTION(request->SrcFolder->m_pendingPlaybackReq == request,
8746 "wrong playback request pointer");
8747
8748 RefPtr<nsImapOfflineSync> offlineSync = new nsImapOfflineSync(
8749 request->MsgWindow, nullptr, request->SrcFolder, true);
8750 if (offlineSync) {
8751 mozilla::DebugOnly<nsresult> rv = offlineSync->ProcessNextOperation();
8752 NS_ASSERTION(NS_SUCCEEDED(rv), "pseudo-offline playback is not successful");
8753 }
8754
8755 // release request struct
8756 request->SrcFolder->m_pendingPlaybackReq = nullptr;
8757 delete request;
8758 }
8759
InitAutoSyncState()8760 void nsImapMailFolder::InitAutoSyncState() {
8761 if (!m_autoSyncStateObj) m_autoSyncStateObj = new nsAutoSyncState(this);
8762 }
8763
HasMsgOffline(nsMsgKey msgKey,bool * _retval)8764 NS_IMETHODIMP nsImapMailFolder::HasMsgOffline(nsMsgKey msgKey, bool* _retval) {
8765 NS_ENSURE_ARG_POINTER(_retval);
8766 *_retval = false;
8767 nsCOMPtr<nsIMsgFolder> msgFolder;
8768 nsresult rv = GetOfflineMsgFolder(msgKey, getter_AddRefs(msgFolder));
8769 if (NS_SUCCEEDED(rv) && msgFolder) *_retval = true;
8770 return NS_OK;
8771 }
8772
GetOfflineMsgFolder(nsMsgKey msgKey,nsIMsgFolder ** aMsgFolder)8773 NS_IMETHODIMP nsImapMailFolder::GetOfflineMsgFolder(nsMsgKey msgKey,
8774 nsIMsgFolder** aMsgFolder) {
8775 // Check if we have the message in the current folder.
8776 NS_ENSURE_ARG_POINTER(aMsgFolder);
8777 nsCOMPtr<nsIMsgFolder> subMsgFolder;
8778 nsresult rv = GetDatabase();
8779 NS_ENSURE_SUCCESS(rv, rv);
8780
8781 nsCOMPtr<nsIMsgDBHdr> hdr;
8782 rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(hdr));
8783 if (NS_FAILED(rv)) return rv;
8784
8785 if (hdr) {
8786 uint32_t msgFlags = 0;
8787 hdr->GetFlags(&msgFlags);
8788 // Check if we already have this message body offline
8789 if ((msgFlags & nsMsgMessageFlags::Offline)) {
8790 NS_IF_ADDREF(*aMsgFolder = this);
8791 return NS_OK;
8792 }
8793 }
8794
8795 if (!*aMsgFolder) {
8796 // Checking the existence of message in other folders in case of GMail
8797 // Server
8798 bool isGMail;
8799 nsCOMPtr<nsIImapIncomingServer> imapServer;
8800 rv = GetImapIncomingServer(getter_AddRefs(imapServer));
8801 NS_ENSURE_SUCCESS(rv, rv);
8802 rv = imapServer->GetIsGMailServer(&isGMail);
8803 NS_ENSURE_SUCCESS(rv, rv);
8804
8805 if (isGMail) {
8806 nsCString labels;
8807 nsTArray<nsCString> labelNames;
8808 hdr->GetStringProperty("X-GM-LABELS", getter_Copies(labels));
8809 ParseString(labels, ' ', labelNames);
8810 nsCOMPtr<nsIMsgFolder> rootFolder;
8811 nsCOMPtr<nsIMsgImapMailFolder> subFolder;
8812 for (uint32_t i = 0; i < labelNames.Length(); i++) {
8813 rv = GetRootFolder(getter_AddRefs(rootFolder));
8814 if (NS_SUCCEEDED(rv) && (rootFolder)) {
8815 nsCOMPtr<nsIMsgImapMailFolder> imapRootFolder =
8816 do_QueryInterface(rootFolder);
8817 if (labelNames[i].EqualsLiteral("\"\\\\Draft\""))
8818 rv = rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Drafts,
8819 getter_AddRefs(subMsgFolder));
8820 if (labelNames[i].EqualsLiteral("\"\\\\Inbox\""))
8821 rv = rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox,
8822 getter_AddRefs(subMsgFolder));
8823 if (labelNames[i].EqualsLiteral("\"\\\\All Mail\""))
8824 rv = rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Archive,
8825 getter_AddRefs(subMsgFolder));
8826 if (labelNames[i].EqualsLiteral("\"\\\\Trash\""))
8827 rv = rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Trash,
8828 getter_AddRefs(subMsgFolder));
8829 if (labelNames[i].EqualsLiteral("\"\\\\Spam\""))
8830 rv = rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Junk,
8831 getter_AddRefs(subMsgFolder));
8832 if (labelNames[i].EqualsLiteral("\"\\\\Sent\""))
8833 rv = rootFolder->GetFolderWithFlags(nsMsgFolderFlags::SentMail,
8834 getter_AddRefs(subMsgFolder));
8835 if (labelNames[i].Find("[Imap]/", /* ignoreCase = */ true) !=
8836 kNotFound) {
8837 labelNames[i].ReplaceSubstring("[Imap]/", "");
8838 imapRootFolder->FindOnlineSubFolder(labelNames[i],
8839 getter_AddRefs(subFolder));
8840 subMsgFolder = do_QueryInterface(subFolder);
8841 }
8842 if (!subMsgFolder) {
8843 imapRootFolder->FindOnlineSubFolder(labelNames[i],
8844 getter_AddRefs(subFolder));
8845 subMsgFolder = do_QueryInterface(subFolder);
8846 }
8847 if (subMsgFolder) {
8848 nsCOMPtr<nsIMsgDatabase> db;
8849 subMsgFolder->GetMsgDatabase(getter_AddRefs(db));
8850 if (db) {
8851 nsCOMPtr<nsIMsgDBHdr> retHdr;
8852 nsCString gmMsgID;
8853 hdr->GetStringProperty("X-GM-MSGID", getter_Copies(gmMsgID));
8854 rv = db->GetMsgHdrForGMMsgID(gmMsgID.get(),
8855 getter_AddRefs(retHdr));
8856 if (NS_FAILED(rv)) return rv;
8857 if (retHdr) {
8858 uint32_t gmFlags = 0;
8859 retHdr->GetFlags(&gmFlags);
8860 if ((gmFlags & nsMsgMessageFlags::Offline)) {
8861 subMsgFolder.forget(aMsgFolder);
8862 // Focus on first positive result.
8863 return NS_OK;
8864 }
8865 }
8866 }
8867 }
8868 }
8869 }
8870 }
8871 }
8872 return NS_OK;
8873 }
8874
GetOfflineFileStream(nsMsgKey msgKey,int64_t * offset,uint32_t * size,nsIInputStream ** aFileStream)8875 NS_IMETHODIMP nsImapMailFolder::GetOfflineFileStream(
8876 nsMsgKey msgKey, int64_t* offset, uint32_t* size,
8877 nsIInputStream** aFileStream) {
8878 NS_ENSURE_ARG(aFileStream);
8879 nsCOMPtr<nsIMsgFolder> offlineFolder;
8880 nsresult rv = GetOfflineMsgFolder(msgKey, getter_AddRefs(offlineFolder));
8881 NS_ENSURE_SUCCESS(rv, rv);
8882 if (!offlineFolder) return NS_ERROR_FAILURE;
8883
8884 rv = GetDatabase();
8885 NS_ENSURE_SUCCESS(rv, rv);
8886
8887 if (offlineFolder == this)
8888 return nsMsgDBFolder::GetOfflineFileStream(msgKey, offset, size,
8889 aFileStream);
8890
8891 nsCOMPtr<nsIMsgDBHdr> hdr;
8892 rv = mDatabase->GetMsgHdrForKey(msgKey, getter_AddRefs(hdr));
8893 NS_ENSURE_SUCCESS(rv, rv);
8894
8895 nsCString gmMsgID;
8896 hdr->GetStringProperty("X-GM-MSGID", getter_Copies(gmMsgID));
8897 nsCOMPtr<nsIMsgDatabase> db;
8898 offlineFolder->GetMsgDatabase(getter_AddRefs(db));
8899 rv = db->GetMsgHdrForGMMsgID(gmMsgID.get(), getter_AddRefs(hdr));
8900 NS_ENSURE_SUCCESS(rv, rv);
8901 if (!hdr) {
8902 return NS_ERROR_FAILURE;
8903 }
8904
8905 nsMsgKey newMsgKey;
8906 hdr->GetMessageKey(&newMsgKey);
8907 return offlineFolder->GetOfflineFileStream(newMsgKey, offset, size,
8908 aFileStream);
8909 }
8910
GetIncomingServerType(nsACString & serverType)8911 NS_IMETHODIMP nsImapMailFolder::GetIncomingServerType(nsACString& serverType) {
8912 serverType.AssignLiteral("imap");
8913 return NS_OK;
8914 }
8915
GetShouldUseUtf8FolderName(bool * aUseUTF8)8916 NS_IMETHODIMP nsImapMailFolder::GetShouldUseUtf8FolderName(bool* aUseUTF8) {
8917 *aUseUTF8 = false;
8918 nsCOMPtr<nsIMsgIncomingServer> server;
8919 nsresult rv = GetServer(getter_AddRefs(server));
8920 NS_ENSURE_SUCCESS(rv, rv);
8921 nsCOMPtr<nsIImapIncomingServer> imapServer = do_QueryInterface(server, &rv);
8922 NS_ENSURE_SUCCESS(rv, rv);
8923 imapServer->GetUtf8AcceptEnabled(aUseUTF8);
8924 return NS_OK;
8925 }
8926
DeleteStoreMessages(const nsTArray<RefPtr<nsIMsgDBHdr>> & aMessages)8927 void nsImapMailFolder::DeleteStoreMessages(
8928 const nsTArray<RefPtr<nsIMsgDBHdr>>& aMessages) {
8929 // Delete messages for pluggable stores that do not support compaction.
8930 nsCOMPtr<nsIMsgPluggableStore> offlineStore;
8931 (void)GetMsgStore(getter_AddRefs(offlineStore));
8932
8933 if (offlineStore) {
8934 bool supportsCompaction;
8935 offlineStore->GetSupportsCompaction(&supportsCompaction);
8936 if (!supportsCompaction) offlineStore->DeleteMessages(aMessages);
8937 }
8938 }
8939
DeleteStoreMessages(const nsTArray<nsMsgKey> & aMessages)8940 void nsImapMailFolder::DeleteStoreMessages(
8941 const nsTArray<nsMsgKey>& aMessages) {
8942 DeleteStoreMessages(aMessages, this);
8943 }
8944
DeleteStoreMessages(const nsTArray<nsMsgKey> & aMessages,nsIMsgFolder * aFolder)8945 void nsImapMailFolder::DeleteStoreMessages(const nsTArray<nsMsgKey>& aMessages,
8946 nsIMsgFolder* aFolder) {
8947 // Delete messages for pluggable stores that do not support compaction.
8948 NS_ASSERTION(aFolder, "Missing Source Folder");
8949 nsCOMPtr<nsIMsgPluggableStore> offlineStore;
8950 (void)aFolder->GetMsgStore(getter_AddRefs(offlineStore));
8951 if (offlineStore) {
8952 bool supportsCompaction;
8953 offlineStore->GetSupportsCompaction(&supportsCompaction);
8954 if (!supportsCompaction) {
8955 nsCOMPtr<nsIMsgDatabase> db;
8956 aFolder->GetMsgDatabase(getter_AddRefs(db));
8957 nsresult rv = NS_ERROR_FAILURE;
8958 nsTArray<RefPtr<nsIMsgDBHdr>> messages;
8959 if (db) rv = MsgGetHeadersFromKeys(db, aMessages, messages);
8960 if (NS_SUCCEEDED(rv))
8961 offlineStore->DeleteMessages(messages);
8962 else
8963 NS_WARNING("Failed to get database");
8964 }
8965 }
8966 }
8967