1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /**
7 * The account manager service - manages all accounts, servers, and identities
8 */
9
10 #include "nsCOMPtr.h"
11 #include "nsISupports.h"
12 #include "nsIThread.h"
13 #include "nscore.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Likely.h"
16 #include "mozilla/MemoryReporting.h"
17 #include "mozilla/RefCountType.h"
18 #include "mozilla/RefPtr.h"
19 #include "nsIComponentManager.h"
20 #include "nsIServiceManager.h"
21 #include "nsMsgAccountManager.h"
22 #include "nsMsgBaseCID.h"
23 #include "nsMsgCompCID.h"
24 #include "nsMsgDBCID.h"
25 #include "prmem.h"
26 #include "prcmon.h"
27 #include "prthread.h"
28 #include "plstr.h"
29 #include "nsString.h"
30 #include "nsMemory.h"
31 #include "nsUnicharUtils.h"
32 #include "nscore.h"
33 #include "prprf.h"
34 #include "nsIMsgFolderCache.h"
35 #include "nsMsgUtils.h"
36 #include "nsMsgDBFolder.h"
37 #include "nsIFile.h"
38 #include "nsIURL.h"
39 #include "nsNetCID.h"
40 #include "nsIPrefService.h"
41 #include "nsIPrefBranch.h"
42 #include "nsISmtpService.h"
43 #include "nsIMsgBiffManager.h"
44 #include "nsIMsgPurgeService.h"
45 #include "nsIObserverService.h"
46 #include "nsINoIncomingServer.h"
47 #include "nsIMsgMailSession.h"
48 #include "nsIDirectoryService.h"
49 #include "nsAppDirectoryServiceDefs.h"
50 #include "nsMailDirServiceDefs.h"
51 #include "nsMsgFolderFlags.h"
52 #include "nsIMsgFolderNotificationService.h"
53 #include "nsIImapIncomingServer.h"
54 #include "nsIImapUrl.h"
55 #include "nsICategoryManager.h"
56 #include "nsISupportsPrimitives.h"
57 #include "nsIMsgFilterService.h"
58 #include "nsIMsgFilter.h"
59 #include "nsIMsgSearchSession.h"
60 #include "nsIMsgSearchTerm.h"
61 #include "nsIDBFolderInfo.h"
62 #include "nsIMsgHdr.h"
63 #include "nsILineInputStream.h"
64 #include "nsThreadUtils.h"
65 #include "nsNetUtil.h"
66 #include "nsIStringBundle.h"
67 #include "nsMsgMessageFlags.h"
68 #include "nsIMsgFilterList.h"
69 #include "nsDirectoryServiceUtils.h"
70 #include "mozilla/Services.h"
71 #include "nsIFileStreams.h"
72 #include "nsIOutputStream.h"
73 #include "nsISafeOutputStream.h"
74 #include "nsXULAppAPI.h"
75
76 #define PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS "mail.accountmanager.accounts"
77 #define PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT \
78 "mail.accountmanager.defaultaccount"
79 #define PREF_MAIL_ACCOUNTMANAGER_LOCALFOLDERSSERVER \
80 "mail.accountmanager.localfoldersserver"
81 #define PREF_MAIL_SERVER_PREFIX "mail.server."
82 #define ACCOUNT_PREFIX "account"
83 #define SERVER_PREFIX "server"
84 #define ID_PREFIX "id"
85 #define ABOUT_TO_GO_OFFLINE_TOPIC "network:offline-about-to-go-offline"
86 #define ACCOUNT_DELIMITER ','
87 #define APPEND_ACCOUNTS_VERSION_PREF_NAME "append_preconfig_accounts.version"
88 #define MAILNEWS_ROOT_PREF "mailnews."
89 #define PREF_MAIL_ACCOUNTMANAGER_APPEND_ACCOUNTS \
90 "mail.accountmanager.appendaccounts"
91
92 static NS_DEFINE_CID(kMsgAccountCID, NS_MSGACCOUNT_CID);
93 static NS_DEFINE_CID(kMsgFolderCacheCID, NS_MSGFOLDERCACHE_CID);
94
95 #define SEARCH_FOLDER_FLAG "searchFolderFlag"
96 #define SEARCH_FOLDER_FLAG_LEN (sizeof(SEARCH_FOLDER_FLAG) - 1)
97
98 const char* kSearchFolderUriProp = "searchFolderUri";
99
100 bool nsMsgAccountManager::m_haveShutdown = false;
101 bool nsMsgAccountManager::m_shutdownInProgress = false;
102
NS_IMPL_ISUPPORTS(nsMsgAccountManager,nsIMsgAccountManager,nsIObserver,nsISupportsWeakReference,nsIUrlListener,nsIFolderListener)103 NS_IMPL_ISUPPORTS(nsMsgAccountManager, nsIMsgAccountManager, nsIObserver,
104 nsISupportsWeakReference, nsIUrlListener, nsIFolderListener)
105
106 nsMsgAccountManager::nsMsgAccountManager()
107 : m_accountsLoaded(false),
108 m_emptyTrashInProgress(false),
109 m_cleanupInboxInProgress(false),
110 m_userAuthenticated(false),
111 m_loadingVirtualFolders(false),
112 m_virtualFoldersLoaded(false) {}
113
~nsMsgAccountManager()114 nsMsgAccountManager::~nsMsgAccountManager() {
115 if (!m_haveShutdown) {
116 Shutdown();
117 // Don't remove from Observer service in Shutdown because Shutdown also gets
118 // called from xpcom shutdown observer. And we don't want to remove from
119 // the service in that case.
120 nsCOMPtr<nsIObserverService> observerService =
121 mozilla::services::GetObserverService();
122 if (observerService) {
123 observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
124 observerService->RemoveObserver(this, "quit-application-granted");
125 observerService->RemoveObserver(this, ABOUT_TO_GO_OFFLINE_TOPIC);
126 observerService->RemoveObserver(this, "sleep_notification");
127 }
128 }
129 }
130
Init()131 nsresult nsMsgAccountManager::Init() {
132 if (!XRE_IsParentProcess()) {
133 return NS_ERROR_NOT_AVAILABLE;
134 }
135
136 nsresult rv;
137
138 m_prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
139 NS_ENSURE_SUCCESS(rv, rv);
140
141 nsCOMPtr<nsIObserverService> observerService =
142 mozilla::services::GetObserverService();
143 if (observerService) {
144 observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
145 observerService->AddObserver(this, "quit-application-granted", true);
146 observerService->AddObserver(this, ABOUT_TO_GO_OFFLINE_TOPIC, true);
147 observerService->AddObserver(this, "profile-before-change", true);
148 observerService->AddObserver(this, "sleep_notification", true);
149 }
150
151 // Make sure PSM gets initialized before any accounts use certificates.
152 net_EnsurePSMInit();
153
154 return NS_OK;
155 }
156
Shutdown()157 nsresult nsMsgAccountManager::Shutdown() {
158 if (m_haveShutdown) // do not shutdown twice
159 return NS_OK;
160
161 nsresult rv;
162
163 SaveVirtualFolders();
164
165 nsCOMPtr<nsIMsgDBService> msgDBService =
166 do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
167 if (msgDBService) {
168 nsTObserverArray<RefPtr<VirtualFolderChangeListener>>::ForwardIterator iter(
169 m_virtualFolderListeners);
170 RefPtr<VirtualFolderChangeListener> listener;
171
172 while (iter.HasMore()) {
173 listener = iter.GetNext();
174 msgDBService->UnregisterPendingListener(listener);
175 }
176 }
177 if (m_msgFolderCache) WriteToFolderCache(m_msgFolderCache);
178 (void)ShutdownServers();
179 (void)UnloadAccounts();
180
181 // shutdown removes nsIIncomingServer listener from biff manager, so do it
182 // after accounts have been unloaded
183 nsCOMPtr<nsIMsgBiffManager> biffService =
184 do_GetService(NS_MSGBIFFMANAGER_CONTRACTID, &rv);
185 if (NS_SUCCEEDED(rv) && biffService) biffService->Shutdown();
186
187 // shutdown removes nsIIncomingServer listener from purge service, so do it
188 // after accounts have been unloaded
189 nsCOMPtr<nsIMsgPurgeService> purgeService =
190 do_GetService(NS_MSGPURGESERVICE_CONTRACTID, &rv);
191 if (NS_SUCCEEDED(rv) && purgeService) purgeService->Shutdown();
192
193 m_msgFolderCache = nullptr;
194 m_haveShutdown = true;
195 return NS_OK;
196 }
197
198 NS_IMETHODIMP
GetShutdownInProgress(bool * _retval)199 nsMsgAccountManager::GetShutdownInProgress(bool* _retval) {
200 NS_ENSURE_ARG_POINTER(_retval);
201 *_retval = m_shutdownInProgress;
202 return NS_OK;
203 }
204
205 NS_IMETHODIMP
GetUserNeedsToAuthenticate(bool * aRetval)206 nsMsgAccountManager::GetUserNeedsToAuthenticate(bool* aRetval) {
207 NS_ENSURE_ARG_POINTER(aRetval);
208 if (!m_userAuthenticated)
209 return m_prefs->GetBoolPref("mail.password_protect_local_cache", aRetval);
210 *aRetval = !m_userAuthenticated;
211 return NS_OK;
212 }
213
214 NS_IMETHODIMP
SetUserNeedsToAuthenticate(bool aUserNeedsToAuthenticate)215 nsMsgAccountManager::SetUserNeedsToAuthenticate(bool aUserNeedsToAuthenticate) {
216 m_userAuthenticated = !aUserNeedsToAuthenticate;
217 return NS_OK;
218 }
219
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * someData)220 NS_IMETHODIMP nsMsgAccountManager::Observe(nsISupports* aSubject,
221 const char* aTopic,
222 const char16_t* someData) {
223 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
224 Shutdown();
225 return NS_OK;
226 }
227 if (!strcmp(aTopic, "quit-application-granted")) {
228 // CleanupOnExit will set m_shutdownInProgress to true.
229 CleanupOnExit();
230 return NS_OK;
231 }
232 if (!strcmp(aTopic, ABOUT_TO_GO_OFFLINE_TOPIC)) {
233 nsAutoString dataString(u"offline"_ns);
234 if (someData) {
235 nsAutoString someDataString(someData);
236 if (dataString.Equals(someDataString)) CloseCachedConnections();
237 }
238 return NS_OK;
239 }
240 if (!strcmp(aTopic, "sleep_notification")) return CloseCachedConnections();
241
242 if (!strcmp(aTopic, "profile-before-change")) {
243 Shutdown();
244 return NS_OK;
245 }
246
247 return NS_OK;
248 }
249
250 NS_IMETHODIMP
GetUniqueAccountKey(nsACString & aResult)251 nsMsgAccountManager::GetUniqueAccountKey(nsACString& aResult) {
252 int32_t lastKey = 0;
253 nsresult rv;
254 nsCOMPtr<nsIPrefService> prefservice(
255 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
256 if (NS_SUCCEEDED(rv)) {
257 nsCOMPtr<nsIPrefBranch> prefBranch;
258 prefservice->GetBranch("", getter_AddRefs(prefBranch));
259
260 rv = prefBranch->GetIntPref("mail.account.lastKey", &lastKey);
261 if (NS_FAILED(rv) || lastKey == 0) {
262 // If lastKey pref does not contain a valid value, loop over existing
263 // pref names mail.account.* .
264 nsCOMPtr<nsIPrefBranch> prefBranchAccount;
265 rv = prefservice->GetBranch("mail.account.",
266 getter_AddRefs(prefBranchAccount));
267 if (NS_SUCCEEDED(rv)) {
268 nsTArray<nsCString> prefList;
269 rv = prefBranchAccount->GetChildList("", prefList);
270 if (NS_SUCCEEDED(rv)) {
271 // Pref names are of the format accountX.
272 // Find the maximum value of 'X' used so far.
273 for (auto& prefName : prefList) {
274 if (StringBeginsWith(prefName, nsLiteralCString(ACCOUNT_PREFIX))) {
275 int32_t dotPos = prefName.FindChar('.');
276 if (dotPos != kNotFound) {
277 nsCString keyString(Substring(prefName, strlen(ACCOUNT_PREFIX),
278 dotPos - strlen(ACCOUNT_PREFIX)));
279 int32_t thisKey = keyString.ToInteger(&rv);
280 if (NS_SUCCEEDED(rv)) lastKey = std::max(lastKey, thisKey);
281 }
282 }
283 }
284 }
285 }
286 }
287
288 // Use next available key and store the value in the pref.
289 aResult.Assign(ACCOUNT_PREFIX);
290 aResult.AppendInt(++lastKey);
291 rv = prefBranch->SetIntPref("mail.account.lastKey", lastKey);
292 } else {
293 // If pref service is not working, try to find a free accountX key
294 // by checking which keys exist.
295 int32_t i = 1;
296 nsCOMPtr<nsIMsgAccount> account;
297
298 do {
299 aResult = ACCOUNT_PREFIX;
300 aResult.AppendInt(i++);
301 GetAccount(aResult, getter_AddRefs(account));
302 } while (account);
303 }
304 return NS_OK;
305 }
306
307 NS_IMETHODIMP
GetUniqueServerKey(nsACString & aResult)308 nsMsgAccountManager::GetUniqueServerKey(nsACString& aResult) {
309 nsAutoCString prefResult;
310 bool usePrefsScan = true;
311 nsresult rv;
312 nsCOMPtr<nsIPrefService> prefService(
313 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
314 if (NS_FAILED(rv)) usePrefsScan = false;
315
316 // Loop over existing pref names mail.server.server(lastKey).type
317 nsCOMPtr<nsIPrefBranch> prefBranchServer;
318 if (prefService) {
319 rv = prefService->GetBranch(PREF_MAIL_SERVER_PREFIX,
320 getter_AddRefs(prefBranchServer));
321 if (NS_FAILED(rv)) usePrefsScan = false;
322 }
323
324 if (usePrefsScan) {
325 nsAutoCString type;
326 nsAutoCString typeKey;
327 for (uint32_t lastKey = 1;; lastKey++) {
328 aResult.AssignLiteral(SERVER_PREFIX);
329 aResult.AppendInt(lastKey);
330 typeKey.Assign(aResult);
331 typeKey.AppendLiteral(".type");
332 prefBranchServer->GetCharPref(typeKey.get(), type);
333 if (type.IsEmpty()) // a server slot with no type is considered empty
334 return NS_OK;
335 }
336 } else {
337 // If pref service fails, try to find a free serverX key
338 // by checking which keys exist.
339 nsAutoCString internalResult;
340 nsCOMPtr<nsIMsgIncomingServer> server;
341 uint32_t i = 1;
342 do {
343 aResult.AssignLiteral(SERVER_PREFIX);
344 aResult.AppendInt(i++);
345 m_incomingServers.Get(aResult, getter_AddRefs(server));
346 } while (server);
347 return NS_OK;
348 }
349 }
350
CreateIdentity(nsIMsgIdentity ** _retval)351 nsresult nsMsgAccountManager::CreateIdentity(nsIMsgIdentity** _retval) {
352 NS_ENSURE_ARG_POINTER(_retval);
353 nsresult rv;
354 nsAutoCString key;
355 nsCOMPtr<nsIMsgIdentity> identity;
356 int32_t i = 1;
357 do {
358 key.AssignLiteral(ID_PREFIX);
359 key.AppendInt(i++);
360 m_identities.Get(key, getter_AddRefs(identity));
361 } while (identity);
362
363 rv = createKeyedIdentity(key, _retval);
364 return rv;
365 }
366
367 NS_IMETHODIMP
GetIdentity(const nsACString & key,nsIMsgIdentity ** _retval)368 nsMsgAccountManager::GetIdentity(const nsACString& key,
369 nsIMsgIdentity** _retval) {
370 NS_ENSURE_ARG_POINTER(_retval);
371 nsresult rv = NS_OK;
372 *_retval = nullptr;
373
374 if (!key.IsEmpty()) {
375 nsCOMPtr<nsIMsgIdentity> identity;
376 m_identities.Get(key, getter_AddRefs(identity));
377 if (identity)
378 identity.forget(_retval);
379 else // identity doesn't exist. create it.
380 rv = createKeyedIdentity(key, _retval);
381 }
382
383 return rv;
384 }
385
386 /*
387 * the shared identity-creation code
388 * create an identity and add it to the accountmanager's list.
389 */
createKeyedIdentity(const nsACString & key,nsIMsgIdentity ** aIdentity)390 nsresult nsMsgAccountManager::createKeyedIdentity(const nsACString& key,
391 nsIMsgIdentity** aIdentity) {
392 nsresult rv;
393 nsCOMPtr<nsIMsgIdentity> identity =
394 do_CreateInstance(NS_MSGIDENTITY_CONTRACTID, &rv);
395 NS_ENSURE_SUCCESS(rv, rv);
396
397 identity->SetKey(key);
398 m_identities.InsertOrUpdate(key, identity);
399 identity.forget(aIdentity);
400 return NS_OK;
401 }
402
403 NS_IMETHODIMP
CreateIncomingServer(const nsACString & username,const nsACString & hostname,const nsACString & type,nsIMsgIncomingServer ** _retval)404 nsMsgAccountManager::CreateIncomingServer(const nsACString& username,
405 const nsACString& hostname,
406 const nsACString& type,
407 nsIMsgIncomingServer** _retval) {
408 NS_ENSURE_ARG_POINTER(_retval);
409
410 nsresult rv = LoadAccounts();
411 NS_ENSURE_SUCCESS(rv, rv);
412
413 nsAutoCString key;
414 GetUniqueServerKey(key);
415 rv = createKeyedServer(key, username, hostname, type, _retval);
416 if (*_retval) {
417 nsCString defaultStore;
418 m_prefs->GetCharPref("mail.serverDefaultStoreContractID", defaultStore);
419 (*_retval)->SetCharValue("storeContractID", defaultStore);
420
421 // From when we first create the account until we have created some folders,
422 // we can change the store type.
423 (*_retval)->SetBoolValue("canChangeStoreType", true);
424 }
425 return rv;
426 }
427
428 NS_IMETHODIMP
GetIncomingServer(const nsACString & key,nsIMsgIncomingServer ** _retval)429 nsMsgAccountManager::GetIncomingServer(const nsACString& key,
430 nsIMsgIncomingServer** _retval) {
431 NS_ENSURE_ARG_POINTER(_retval);
432 nsresult rv;
433
434 if (m_incomingServers.Get(key, _retval)) return NS_OK;
435
436 // server doesn't exist, so create it
437 // this is really horrible because we are doing our own prefname munging
438 // instead of leaving it up to the incoming server.
439 // this should be fixed somehow so that we can create the incoming server
440 // and then read from the incoming server's attributes
441
442 // in order to create the right kind of server, we have to look
443 // at the pref for this server to get the username, hostname, and type
444 nsAutoCString serverPrefPrefix(PREF_MAIL_SERVER_PREFIX);
445 serverPrefPrefix.Append(key);
446
447 nsCString serverType;
448 nsAutoCString serverPref(serverPrefPrefix);
449 serverPref.AppendLiteral(".type");
450 rv = m_prefs->GetCharPref(serverPref.get(), serverType);
451 NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_INITIALIZED);
452
453 //
454 // .userName
455 serverPref = serverPrefPrefix;
456 serverPref.AppendLiteral(".userName");
457 nsCString username;
458 rv = m_prefs->GetCharPref(serverPref.get(), username);
459
460 // .hostname
461 serverPref = serverPrefPrefix;
462 serverPref.AppendLiteral(".hostname");
463 nsCString hostname;
464 rv = m_prefs->GetCharPref(serverPref.get(), hostname);
465 NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_INITIALIZED);
466
467 return createKeyedServer(key, username, hostname, serverType, _retval);
468 }
469
470 NS_IMETHODIMP
RemoveIncomingServer(nsIMsgIncomingServer * aServer,bool aRemoveFiles)471 nsMsgAccountManager::RemoveIncomingServer(nsIMsgIncomingServer* aServer,
472 bool aRemoveFiles) {
473 NS_ENSURE_ARG_POINTER(aServer);
474
475 nsCString serverKey;
476 nsresult rv = aServer->GetKey(serverKey);
477 NS_ENSURE_SUCCESS(rv, rv);
478
479 // close cached connections and forget session password
480 LogoutOfServer(aServer);
481
482 // invalidate the FindServer() cache if we are removing the cached server
483 if (m_lastFindServerResult == aServer)
484 SetLastServerFound(nullptr, EmptyCString(), EmptyCString(), 0,
485 EmptyCString());
486
487 m_incomingServers.Remove(serverKey);
488
489 nsCOMPtr<nsIMsgFolder> rootFolder;
490 rv = aServer->GetRootFolder(getter_AddRefs(rootFolder));
491 NS_ENSURE_SUCCESS(rv, rv);
492
493 nsTArray<RefPtr<nsIMsgFolder>> allDescendants;
494 rv = rootFolder->GetDescendants(allDescendants);
495 NS_ENSURE_SUCCESS(rv, rv);
496
497 nsCOMPtr<nsIMsgFolderNotificationService> notifier =
498 do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID);
499 nsCOMPtr<nsIFolderListener> mailSession =
500 do_GetService(NS_MSGMAILSESSION_CONTRACTID);
501
502 for (auto folder : allDescendants) {
503 folder->ForceDBClosed();
504 if (notifier) notifier->NotifyFolderDeleted(folder);
505 if (mailSession) {
506 nsCOMPtr<nsIMsgFolder> parentFolder;
507 folder->GetParent(getter_AddRefs(parentFolder));
508 mailSession->OnItemRemoved(parentFolder, folder);
509 }
510 }
511 if (notifier) notifier->NotifyFolderDeleted(rootFolder);
512 if (mailSession) mailSession->OnItemRemoved(nullptr, rootFolder);
513
514 removeListenersFromFolder(rootFolder);
515 NotifyServerUnloaded(aServer);
516 if (aRemoveFiles) {
517 rv = aServer->RemoveFiles();
518 NS_ENSURE_SUCCESS(rv, rv);
519 }
520
521 // now clear out the server once and for all.
522 // watch out! could be scary
523 aServer->ClearAllValues();
524 rootFolder->Shutdown(true);
525 return rv;
526 }
527
528 /*
529 * create a server when you know the key and the type
530 */
createKeyedServer(const nsACString & key,const nsACString & username,const nsACString & hostname,const nsACString & type,nsIMsgIncomingServer ** aServer)531 nsresult nsMsgAccountManager::createKeyedServer(
532 const nsACString& key, const nsACString& username,
533 const nsACString& hostname, const nsACString& type,
534 nsIMsgIncomingServer** aServer) {
535 nsresult rv;
536 *aServer = nullptr;
537
538 // construct the contractid
539 nsAutoCString serverContractID(NS_MSGINCOMINGSERVER_CONTRACTID_PREFIX);
540 serverContractID += type;
541
542 // finally, create the server
543 // (This will fail if type is from an extension that has been removed)
544 nsCOMPtr<nsIMsgIncomingServer> server =
545 do_CreateInstance(serverContractID.get(), &rv);
546 NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_AVAILABLE);
547
548 int32_t port;
549 nsCOMPtr<nsIMsgIncomingServer> existingServer;
550 server->SetKey(key);
551 server->SetType(type);
552 server->SetUsername(username);
553 server->SetHostName(hostname);
554 server->GetPort(&port);
555 FindRealServer(username, hostname, type, port,
556 getter_AddRefs(existingServer));
557 // don't allow duplicate servers.
558 if (existingServer) return NS_ERROR_FAILURE;
559
560 m_incomingServers.InsertOrUpdate(key, server);
561
562 // now add all listeners that are supposed to be
563 // waiting on root folders
564 nsCOMPtr<nsIMsgFolder> rootFolder;
565 rv = server->GetRootFolder(getter_AddRefs(rootFolder));
566 NS_ENSURE_SUCCESS(rv, rv);
567
568 nsTObserverArray<nsCOMPtr<nsIFolderListener>>::ForwardIterator iter(
569 mFolderListeners);
570 while (iter.HasMore()) {
571 rootFolder->AddFolderListener(iter.GetNext());
572 }
573
574 server.forget(aServer);
575 return NS_OK;
576 }
577
removeListenersFromFolder(nsIMsgFolder * aFolder)578 void nsMsgAccountManager::removeListenersFromFolder(nsIMsgFolder* aFolder) {
579 nsTObserverArray<nsCOMPtr<nsIFolderListener>>::ForwardIterator iter(
580 mFolderListeners);
581 while (iter.HasMore()) {
582 aFolder->RemoveFolderListener(iter.GetNext());
583 }
584 }
585
586 NS_IMETHODIMP
RemoveAccount(nsIMsgAccount * aAccount,bool aRemoveFiles=false)587 nsMsgAccountManager::RemoveAccount(nsIMsgAccount* aAccount,
588 bool aRemoveFiles = false) {
589 NS_ENSURE_ARG_POINTER(aAccount);
590 // Hold account in scope while we tidy up potentially-shared identities.
591 nsresult rv = LoadAccounts();
592 NS_ENSURE_SUCCESS(rv, rv);
593
594 if (!m_accounts.RemoveElement(aAccount)) {
595 return NS_ERROR_INVALID_ARG;
596 }
597
598 rv = OutputAccountsPref();
599 // If we couldn't write out the pref, restore the account.
600 if (NS_FAILED(rv)) {
601 m_accounts.AppendElement(aAccount);
602 return rv;
603 }
604
605 // If it's the default, choose a new default account.
606 if (m_defaultAccount == aAccount) AutosetDefaultAccount();
607
608 // XXX - need to figure out if this is the last time this server is
609 // being used, and only send notification then.
610 // (and only remove from hashtable then too!)
611 nsCOMPtr<nsIMsgIncomingServer> server;
612 rv = aAccount->GetIncomingServer(getter_AddRefs(server));
613 if (NS_SUCCEEDED(rv) && server) RemoveIncomingServer(server, aRemoveFiles);
614
615 nsTArray<RefPtr<nsIMsgIdentity>> identities;
616 rv = aAccount->GetIdentities(identities);
617 if (NS_SUCCEEDED(rv)) {
618 for (auto identity : identities) {
619 bool identityStillUsed = false;
620 // for each identity, see if any remaining account still uses it,
621 // and if not, clear it.
622 // Note that we are also searching here accounts with missing servers from
623 // unloaded extension types.
624 for (auto account : m_accounts) {
625 nsTArray<RefPtr<nsIMsgIdentity>> existingIdentities;
626 account->GetIdentities(existingIdentities);
627 auto pos = existingIdentities.IndexOf(identity);
628 if (pos != existingIdentities.NoIndex) {
629 identityStillUsed = true;
630 break;
631 }
632 }
633 // clear out all identity information if no other account uses it.
634 if (!identityStillUsed) identity->ClearAllValues();
635 }
636 }
637
638 // It is not a critical problem if this fails as the account was already
639 // removed from the list of accounts so should not ever be referenced.
640 // Just print it out for debugging.
641 rv = aAccount->ClearAllValues();
642 NS_ASSERTION(NS_SUCCEEDED(rv), "removing of account prefs failed");
643 return NS_OK;
644 }
645
OutputAccountsPref()646 nsresult nsMsgAccountManager::OutputAccountsPref() {
647 nsCString accountKey;
648 mAccountKeyList.Truncate();
649
650 for (uint32_t index = 0; index < m_accounts.Length(); index++) {
651 m_accounts[index]->GetKey(accountKey);
652 if (index) mAccountKeyList.Append(ACCOUNT_DELIMITER);
653 mAccountKeyList.Append(accountKey);
654 }
655 return m_prefs->SetCharPref(PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS,
656 mAccountKeyList);
657 }
658
659 /**
660 * Get the default account. If no default account, return null.
661 */
662 NS_IMETHODIMP
GetDefaultAccount(nsIMsgAccount ** aDefaultAccount)663 nsMsgAccountManager::GetDefaultAccount(nsIMsgAccount** aDefaultAccount) {
664 NS_ENSURE_ARG_POINTER(aDefaultAccount);
665
666 nsresult rv = LoadAccounts();
667 NS_ENSURE_SUCCESS(rv, rv);
668
669 if (!m_defaultAccount) {
670 nsCString defaultKey;
671 rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT,
672 defaultKey);
673 if (NS_SUCCEEDED(rv)) {
674 rv = GetAccount(defaultKey, getter_AddRefs(m_defaultAccount));
675 if (NS_SUCCEEDED(rv) && m_defaultAccount) {
676 bool canBeDefault = false;
677 rv = CheckDefaultAccount(m_defaultAccount, canBeDefault);
678 if (NS_FAILED(rv) || !canBeDefault) m_defaultAccount = nullptr;
679 }
680 }
681 }
682
683 NS_IF_ADDREF(*aDefaultAccount = m_defaultAccount);
684 return NS_OK;
685 }
686
687 /**
688 * Check if the given account can be default.
689 */
CheckDefaultAccount(nsIMsgAccount * aAccount,bool & aCanBeDefault)690 nsresult nsMsgAccountManager::CheckDefaultAccount(nsIMsgAccount* aAccount,
691 bool& aCanBeDefault) {
692 aCanBeDefault = false;
693 nsCOMPtr<nsIMsgIncomingServer> server;
694 // Server could be null if created by an unloaded extension.
695 nsresult rv = aAccount->GetIncomingServer(getter_AddRefs(server));
696 NS_ENSURE_SUCCESS(rv, rv);
697 if (server) {
698 // Check if server can be default.
699 rv = server->GetCanBeDefaultServer(&aCanBeDefault);
700 }
701 return rv;
702 }
703
704 /**
705 * Pick the first account that can be default and make it the default.
706 */
AutosetDefaultAccount()707 nsresult nsMsgAccountManager::AutosetDefaultAccount() {
708 for (nsIMsgAccount* account : m_accounts) {
709 bool canBeDefault = false;
710 nsresult rv = CheckDefaultAccount(account, canBeDefault);
711 if (NS_SUCCEEDED(rv) && canBeDefault) {
712 return SetDefaultAccount(account);
713 }
714 }
715
716 // No accounts can be the default. Clear it.
717 if (m_defaultAccount) {
718 nsCOMPtr<nsIMsgAccount> oldAccount = m_defaultAccount;
719 m_defaultAccount = nullptr;
720 (void)setDefaultAccountPref(nullptr);
721 (void)notifyDefaultServerChange(oldAccount, nullptr);
722 }
723 return NS_OK;
724 }
725
726 NS_IMETHODIMP
SetDefaultAccount(nsIMsgAccount * aDefaultAccount)727 nsMsgAccountManager::SetDefaultAccount(nsIMsgAccount* aDefaultAccount) {
728 if (!aDefaultAccount) return NS_ERROR_INVALID_ARG;
729
730 if (m_defaultAccount != aDefaultAccount) {
731 bool canBeDefault = false;
732 nsresult rv = CheckDefaultAccount(aDefaultAccount, canBeDefault);
733 if (NS_FAILED(rv) || !canBeDefault) {
734 // Report failure if we were explicitly asked to use an unusable server.
735 return NS_ERROR_INVALID_ARG;
736 }
737 nsCOMPtr<nsIMsgAccount> oldAccount = m_defaultAccount;
738 m_defaultAccount = aDefaultAccount;
739 (void)setDefaultAccountPref(aDefaultAccount);
740 (void)notifyDefaultServerChange(oldAccount, aDefaultAccount);
741 }
742 return NS_OK;
743 }
744
745 // fire notifications
notifyDefaultServerChange(nsIMsgAccount * aOldAccount,nsIMsgAccount * aNewAccount)746 nsresult nsMsgAccountManager::notifyDefaultServerChange(
747 nsIMsgAccount* aOldAccount, nsIMsgAccount* aNewAccount) {
748 nsresult rv;
749
750 nsCOMPtr<nsIMsgIncomingServer> server;
751 nsCOMPtr<nsIMsgFolder> rootFolder;
752
753 // first tell old server it's no longer the default
754 if (aOldAccount) {
755 rv = aOldAccount->GetIncomingServer(getter_AddRefs(server));
756 if (NS_SUCCEEDED(rv) && server) {
757 rv = server->GetRootFolder(getter_AddRefs(rootFolder));
758 if (NS_SUCCEEDED(rv) && rootFolder)
759 rootFolder->NotifyBoolPropertyChanged(kDefaultServer, true, false);
760 }
761 }
762
763 // now tell new server it is.
764 if (aNewAccount) {
765 rv = aNewAccount->GetIncomingServer(getter_AddRefs(server));
766 if (NS_SUCCEEDED(rv) && server) {
767 rv = server->GetRootFolder(getter_AddRefs(rootFolder));
768 if (NS_SUCCEEDED(rv) && rootFolder)
769 rootFolder->NotifyBoolPropertyChanged(kDefaultServer, false, true);
770 }
771 }
772
773 // only notify if the user goes and changes default account
774 if (aOldAccount && aNewAccount) {
775 nsCOMPtr<nsIObserverService> observerService =
776 mozilla::services::GetObserverService();
777
778 if (observerService)
779 observerService->NotifyObservers(nullptr, "mailDefaultAccountChanged",
780 nullptr);
781 }
782
783 return NS_OK;
784 }
785
setDefaultAccountPref(nsIMsgAccount * aDefaultAccount)786 nsresult nsMsgAccountManager::setDefaultAccountPref(
787 nsIMsgAccount* aDefaultAccount) {
788 nsresult rv;
789
790 if (aDefaultAccount) {
791 nsCString key;
792 rv = aDefaultAccount->GetKey(key);
793 NS_ENSURE_SUCCESS(rv, rv);
794
795 rv = m_prefs->SetCharPref(PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT, key);
796 NS_ENSURE_SUCCESS(rv, rv);
797 } else
798 m_prefs->ClearUserPref(PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT);
799
800 return NS_OK;
801 }
802
LogoutOfServer(nsIMsgIncomingServer * aServer)803 void nsMsgAccountManager::LogoutOfServer(nsIMsgIncomingServer* aServer) {
804 if (!aServer) return;
805 mozilla::DebugOnly<nsresult> rv = aServer->Shutdown();
806 NS_ASSERTION(NS_SUCCEEDED(rv), "Shutdown of server failed");
807 rv = aServer->ForgetSessionPassword();
808 NS_ASSERTION(NS_SUCCEEDED(rv),
809 "failed to remove the password associated with server");
810 }
811
GetFolderCache(nsIMsgFolderCache ** aFolderCache)812 NS_IMETHODIMP nsMsgAccountManager::GetFolderCache(
813 nsIMsgFolderCache** aFolderCache) {
814 NS_ENSURE_ARG_POINTER(aFolderCache);
815 nsresult rv = NS_OK;
816
817 if (!m_msgFolderCache) {
818 m_msgFolderCache = do_CreateInstance(kMsgFolderCacheCID, &rv);
819 NS_ENSURE_SUCCESS(rv, rv);
820
821 nsCOMPtr<nsIFile> cacheFile;
822 rv = NS_GetSpecialDirectory(NS_APP_MESSENGER_FOLDER_CACHE_50_FILE,
823 getter_AddRefs(cacheFile));
824 NS_ENSURE_SUCCESS(rv, rv);
825 m_msgFolderCache->Init(cacheFile);
826 }
827
828 NS_IF_ADDREF(*aFolderCache = m_msgFolderCache);
829 return rv;
830 }
831
832 NS_IMETHODIMP
GetAccounts(nsTArray<RefPtr<nsIMsgAccount>> & accounts)833 nsMsgAccountManager::GetAccounts(nsTArray<RefPtr<nsIMsgAccount>>& accounts) {
834 nsresult rv = LoadAccounts();
835 NS_ENSURE_SUCCESS(rv, rv);
836
837 accounts.Clear();
838 accounts.SetCapacity(m_accounts.Length());
839 for (auto existingAccount : m_accounts) {
840 nsCOMPtr<nsIMsgIncomingServer> server;
841 existingAccount->GetIncomingServer(getter_AddRefs(server));
842 if (!server) continue;
843 if (server) {
844 bool hidden = false;
845 server->GetHidden(&hidden);
846 if (hidden) continue;
847 }
848 accounts.AppendElement(existingAccount);
849 }
850 return NS_OK;
851 }
852
853 NS_IMETHODIMP
GetAllIdentities(nsTArray<RefPtr<nsIMsgIdentity>> & result)854 nsMsgAccountManager::GetAllIdentities(
855 nsTArray<RefPtr<nsIMsgIdentity>>& result) {
856 nsresult rv = LoadAccounts();
857 NS_ENSURE_SUCCESS(rv, rv);
858
859 result.Clear();
860
861 for (auto account : m_accounts) {
862 nsTArray<RefPtr<nsIMsgIdentity>> identities;
863 rv = account->GetIdentities(identities);
864 if (NS_FAILED(rv)) continue;
865
866 for (auto identity : identities) {
867 // Have we already got this identity?
868 nsAutoCString key;
869 rv = identity->GetKey(key);
870 if (NS_FAILED(rv)) continue;
871
872 bool found = false;
873 for (auto thisIdentity : result) {
874 nsAutoCString thisKey;
875 rv = thisIdentity->GetKey(thisKey);
876 if (NS_FAILED(rv)) continue;
877
878 if (key == thisKey) {
879 found = true;
880 break;
881 }
882 }
883
884 if (!found) result.AppendElement(identity);
885 }
886 }
887 return rv;
888 }
889
890 NS_IMETHODIMP
GetAllServers(nsTArray<RefPtr<nsIMsgIncomingServer>> & servers)891 nsMsgAccountManager::GetAllServers(
892 nsTArray<RefPtr<nsIMsgIncomingServer>>& servers) {
893 servers.Clear();
894 nsresult rv = LoadAccounts();
895 NS_ENSURE_SUCCESS(rv, rv);
896
897 for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
898 nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
899 if (!server) continue;
900
901 bool hidden = false;
902 server->GetHidden(&hidden);
903 if (hidden) continue;
904
905 nsCString type;
906 if (NS_FAILED(server->GetType(type))) {
907 NS_WARNING("server->GetType() failed");
908 continue;
909 }
910
911 if (!type.EqualsLiteral("im")) {
912 servers.AppendElement(server);
913 }
914 }
915 return NS_OK;
916 }
917
LoadAccounts()918 nsresult nsMsgAccountManager::LoadAccounts() {
919 nsresult rv;
920
921 // for now safeguard multiple calls to this function
922 if (m_accountsLoaded) return NS_OK;
923
924 nsCOMPtr<nsIMsgMailSession> mailSession =
925 do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
926
927 if (NS_SUCCEEDED(rv))
928 mailSession->AddFolderListener(
929 this, nsIFolderListener::added | nsIFolderListener::removed |
930 nsIFolderListener::intPropertyChanged);
931 // If we have code trying to do things after we've unloaded accounts,
932 // ignore it.
933 if (m_shutdownInProgress || m_haveShutdown) return NS_ERROR_FAILURE;
934
935 // Ensure biff service has started
936 nsCOMPtr<nsIMsgBiffManager> biffService =
937 do_GetService(NS_MSGBIFFMANAGER_CONTRACTID, &rv);
938
939 if (NS_SUCCEEDED(rv)) biffService->Init();
940
941 // Ensure purge service has started
942 nsCOMPtr<nsIMsgPurgeService> purgeService =
943 do_GetService(NS_MSGPURGESERVICE_CONTRACTID, &rv);
944
945 if (NS_SUCCEEDED(rv)) purgeService->Init();
946
947 nsCOMPtr<nsIPrefService> prefservice(
948 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
949 NS_ENSURE_SUCCESS(rv, rv);
950
951 // mail.accountmanager.accounts is the main entry point for all accounts
952 nsCString accountList;
953 rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS, accountList);
954
955 /**
956 * Check to see if we need to add pre-configured accounts.
957 * Following prefs are important to note in understanding the procedure here.
958 *
959 * 1. pref("mailnews.append_preconfig_accounts.version", version number);
960 * This pref registers the current version in the user prefs file. A default
961 * value is stored in mailnews.js file. If a given vendor needs to add more
962 * preconfigured accounts, the default version number can be increased.
963 * Comparing version number from user's prefs file and the default one from
964 * mailnews.js, we can add new accounts and any other version level changes
965 * that need to be done.
966 *
967 * 2. pref("mail.accountmanager.appendaccounts", <comma sep. account list>);
968 * This pref contains the list of pre-configured accounts that ISP/Vendor
969 * wants to add to the existing accounts list.
970 */
971 nsCOMPtr<nsIPrefBranch> defaultsPrefBranch;
972 rv = prefservice->GetDefaultBranch(MAILNEWS_ROOT_PREF,
973 getter_AddRefs(defaultsPrefBranch));
974 NS_ENSURE_SUCCESS(rv, rv);
975
976 nsCOMPtr<nsIPrefBranch> prefBranch;
977 rv = prefservice->GetBranch(MAILNEWS_ROOT_PREF, getter_AddRefs(prefBranch));
978 NS_ENSURE_SUCCESS(rv, rv);
979
980 int32_t appendAccountsCurrentVersion = 0;
981 int32_t appendAccountsDefaultVersion = 0;
982 rv = prefBranch->GetIntPref(APPEND_ACCOUNTS_VERSION_PREF_NAME,
983 &appendAccountsCurrentVersion);
984 NS_ENSURE_SUCCESS(rv, rv);
985
986 rv = defaultsPrefBranch->GetIntPref(APPEND_ACCOUNTS_VERSION_PREF_NAME,
987 &appendAccountsDefaultVersion);
988 NS_ENSURE_SUCCESS(rv, rv);
989
990 // Update the account list if needed
991 if ((appendAccountsCurrentVersion <= appendAccountsDefaultVersion)) {
992 // Get a list of pre-configured accounts
993 nsCString appendAccountList;
994 rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_APPEND_ACCOUNTS,
995 appendAccountList);
996 appendAccountList.StripWhitespace();
997
998 // If there are pre-configured accounts, we need to add them to the
999 // existing list.
1000 if (!appendAccountList.IsEmpty()) {
1001 if (!accountList.IsEmpty()) {
1002 // Tokenize the data and add each account
1003 // in the user's current mailnews account list
1004 nsTArray<nsCString> accountsArray;
1005 ParseString(accountList, ACCOUNT_DELIMITER, accountsArray);
1006 uint32_t i = accountsArray.Length();
1007
1008 // Append each account in the pre-configured account list
1009 ParseString(appendAccountList, ACCOUNT_DELIMITER, accountsArray);
1010
1011 // Now add each account that does not already appear in the list
1012 for (; i < accountsArray.Length(); i++) {
1013 if (accountsArray.IndexOf(accountsArray[i]) == i) {
1014 accountList.Append(ACCOUNT_DELIMITER);
1015 accountList.Append(accountsArray[i]);
1016 }
1017 }
1018 } else {
1019 accountList = appendAccountList;
1020 }
1021 // Increase the version number so that updates will happen as and when
1022 // needed
1023 rv = prefBranch->SetIntPref(APPEND_ACCOUNTS_VERSION_PREF_NAME,
1024 appendAccountsCurrentVersion + 1);
1025 }
1026 }
1027
1028 // It is ok to return null accounts like when we create new profile.
1029 m_accountsLoaded = true;
1030 m_haveShutdown = false;
1031
1032 if (accountList.IsEmpty()) return NS_OK;
1033
1034 /* parse accountList and run loadAccount on each string, comma-separated */
1035 nsCOMPtr<nsIMsgAccount> account;
1036 // Tokenize the data and add each account
1037 // in the user's current mailnews account list
1038 nsTArray<nsCString> accountsArray;
1039 ParseString(accountList, ACCOUNT_DELIMITER, accountsArray);
1040
1041 // These are the duplicate accounts we found. We keep track of these
1042 // because if any other server defers to one of these accounts, we need
1043 // to defer to the correct account.
1044 nsCOMArray<nsIMsgAccount> dupAccounts;
1045
1046 // Now add each account that does not already appear in the list
1047 for (uint32_t i = 0; i < accountsArray.Length(); i++) {
1048 // if we've already seen this exact account, advance to the next account.
1049 // After the loop, we'll notice that we don't have as many actual accounts
1050 // as there were accounts in the pref, and rewrite the pref.
1051 if (accountsArray.IndexOf(accountsArray[i]) != i) continue;
1052
1053 // get the "server" pref to see if we already have an account with this
1054 // server. If we do, we ignore this account.
1055 nsAutoCString serverKeyPref("mail.account.");
1056 serverKeyPref += accountsArray[i];
1057
1058 nsCOMPtr<nsIPrefBranch> accountPrefBranch;
1059 rv = prefservice->GetBranch(serverKeyPref.get(),
1060 getter_AddRefs(accountPrefBranch));
1061 NS_ENSURE_SUCCESS(rv, rv);
1062
1063 serverKeyPref += ".server";
1064 nsCString serverKey;
1065 rv = m_prefs->GetCharPref(serverKeyPref.get(), serverKey);
1066 if (NS_FAILED(rv)) continue;
1067
1068 nsCOMPtr<nsIMsgAccount> serverAccount;
1069 findAccountByServerKey(serverKey, getter_AddRefs(serverAccount));
1070 // If we have an existing account with the same server, ignore this account
1071 if (serverAccount) continue;
1072
1073 if (NS_FAILED(createKeyedAccount(accountsArray[i], true,
1074 getter_AddRefs(account))) ||
1075 !account) {
1076 NS_WARNING("unexpected entry in account list; prefs corrupt?");
1077 continue;
1078 }
1079
1080 // See nsIMsgAccount.idl for a description of the secondsToLeaveUnavailable
1081 // and timeFoundUnavailable preferences
1082 nsAutoCString toLeavePref(PREF_MAIL_SERVER_PREFIX);
1083 toLeavePref.Append(serverKey);
1084 nsAutoCString unavailablePref(
1085 toLeavePref); // this is the server-specific prefix
1086 unavailablePref.AppendLiteral(".timeFoundUnavailable");
1087 toLeavePref.AppendLiteral(".secondsToLeaveUnavailable");
1088 int32_t secondsToLeave = 0;
1089 int32_t timeUnavailable = 0;
1090
1091 m_prefs->GetIntPref(toLeavePref.get(), &secondsToLeave);
1092
1093 // force load of accounts (need to find a better way to do this)
1094 nsTArray<RefPtr<nsIMsgIdentity>> unused;
1095 account->GetIdentities(unused);
1096
1097 rv = account->CreateServer();
1098 bool deleteAccount = NS_FAILED(rv);
1099
1100 if (secondsToLeave) { // we need to process timeUnavailable
1101 if (NS_SUCCEEDED(rv)) // clear the time if server is available
1102 {
1103 m_prefs->ClearUserPref(unavailablePref.get());
1104 }
1105 // NS_ERROR_NOT_AVAILABLE signifies a server that could not be
1106 // instantiated, presumably because of an invalid type.
1107 else if (rv == NS_ERROR_NOT_AVAILABLE) {
1108 m_prefs->GetIntPref(unavailablePref.get(), &timeUnavailable);
1109 if (!timeUnavailable) { // we need to set it, this must be the first
1110 // time unavailable
1111 uint32_t nowSeconds;
1112 PRTime2Seconds(PR_Now(), &nowSeconds);
1113 m_prefs->SetIntPref(unavailablePref.get(), nowSeconds);
1114 deleteAccount = false;
1115 }
1116 }
1117 }
1118
1119 // Our server is still unavailable. Have we timed out yet?
1120 if (rv == NS_ERROR_NOT_AVAILABLE && timeUnavailable != 0) {
1121 uint32_t nowSeconds;
1122 PRTime2Seconds(PR_Now(), &nowSeconds);
1123 if ((int32_t)nowSeconds < timeUnavailable + secondsToLeave)
1124 deleteAccount = false;
1125 }
1126
1127 if (deleteAccount) {
1128 dupAccounts.AppendObject(account);
1129 m_accounts.RemoveElement(account);
1130 }
1131 }
1132
1133 // Check if we removed one or more of the accounts in the pref string.
1134 // If so, rewrite the pref string.
1135 if (accountsArray.Length() != m_accounts.Length()) OutputAccountsPref();
1136
1137 int32_t cnt = dupAccounts.Count();
1138 nsCOMPtr<nsIMsgAccount> dupAccount;
1139
1140 // Go through the accounts seeing if any existing server is deferred to
1141 // an account we removed. If so, fix the deferral. Then clean up the prefs
1142 // for the removed account.
1143 for (int32_t i = 0; i < cnt; i++) {
1144 dupAccount = dupAccounts[i];
1145 for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
1146 /*
1147 * This loop gets run for every incoming server, and is passed a
1148 * duplicate account. It checks that the server is not deferred to the
1149 * duplicate account. If it is, then it looks up the information for the
1150 * duplicate account's server (username, hostName, type), and finds an
1151 * account with a server with the same username, hostname, and type, and
1152 * if it finds one, defers to that account instead. Generally, this will
1153 * be a Local Folders account, since 2.0 has a bug where duplicate Local
1154 * Folders accounts are created.
1155 */
1156 nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
1157 nsCString type;
1158 server->GetType(type);
1159 if (type.EqualsLiteral("pop3")) {
1160 nsCString deferredToAccount;
1161 // Get the pref directly, because the GetDeferredToAccount accessor
1162 // attempts to fix broken deferrals, but we know more about what the
1163 // deferred to account was.
1164 server->GetCharValue("deferred_to_account", deferredToAccount);
1165 if (!deferredToAccount.IsEmpty()) {
1166 nsCString dupAccountKey;
1167 dupAccount->GetKey(dupAccountKey);
1168 if (deferredToAccount.Equals(dupAccountKey)) {
1169 nsresult rv;
1170 nsCString accountPref("mail.account.");
1171 nsCString dupAccountServerKey;
1172 accountPref.Append(dupAccountKey);
1173 accountPref.AppendLiteral(".server");
1174 nsCOMPtr<nsIPrefService> prefservice(
1175 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
1176 if (NS_FAILED(rv)) {
1177 continue;
1178 }
1179 nsCOMPtr<nsIPrefBranch> prefBranch(
1180 do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
1181 if (NS_FAILED(rv)) {
1182 continue;
1183 }
1184 rv =
1185 prefBranch->GetCharPref(accountPref.get(), dupAccountServerKey);
1186 if (NS_FAILED(rv)) {
1187 continue;
1188 }
1189 nsCOMPtr<nsIPrefBranch> serverPrefBranch;
1190 nsCString serverKeyPref(PREF_MAIL_SERVER_PREFIX);
1191 serverKeyPref.Append(dupAccountServerKey);
1192 serverKeyPref.Append('.');
1193 rv = prefservice->GetBranch(serverKeyPref.get(),
1194 getter_AddRefs(serverPrefBranch));
1195 if (NS_FAILED(rv)) {
1196 continue;
1197 }
1198 nsCString userName;
1199 nsCString hostName;
1200 nsCString type;
1201 serverPrefBranch->GetCharPref("userName", userName);
1202 serverPrefBranch->GetCharPref("hostname", hostName);
1203 serverPrefBranch->GetCharPref("type", type);
1204 // Find a server with the same info.
1205 nsCOMPtr<nsIMsgAccountManager> accountManager =
1206 do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
1207 if (NS_FAILED(rv)) {
1208 continue;
1209 }
1210 nsCOMPtr<nsIMsgIncomingServer> server;
1211 accountManager->FindServer(userName, hostName, type,
1212 getter_AddRefs(server));
1213 if (server) {
1214 nsCOMPtr<nsIMsgAccount> replacement;
1215 accountManager->FindAccountForServer(server,
1216 getter_AddRefs(replacement));
1217 if (replacement) {
1218 nsCString accountKey;
1219 replacement->GetKey(accountKey);
1220 if (!accountKey.IsEmpty())
1221 server->SetCharValue("deferred_to_account", accountKey);
1222 }
1223 }
1224 }
1225 }
1226 }
1227 }
1228
1229 nsAutoCString accountKeyPref("mail.account.");
1230 nsCString dupAccountKey;
1231 dupAccount->GetKey(dupAccountKey);
1232 if (dupAccountKey.IsEmpty()) continue;
1233 accountKeyPref.Append(dupAccountKey);
1234 accountKeyPref.Append('.');
1235
1236 nsCOMPtr<nsIPrefBranch> accountPrefBranch;
1237 rv = prefservice->GetBranch(accountKeyPref.get(),
1238 getter_AddRefs(accountPrefBranch));
1239 if (accountPrefBranch) {
1240 nsTArray<nsCString> prefNames;
1241 nsresult rv = accountPrefBranch->GetChildList("", prefNames);
1242 NS_ENSURE_SUCCESS(rv, rv);
1243
1244 for (auto& prefName : prefNames) {
1245 accountPrefBranch->ClearUserPref(prefName.get());
1246 }
1247 }
1248 }
1249
1250 // Make sure we have an account that points at the local folders server
1251 nsCString localFoldersServerKey;
1252 rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_LOCALFOLDERSSERVER,
1253 localFoldersServerKey);
1254
1255 if (!localFoldersServerKey.IsEmpty()) {
1256 nsCOMPtr<nsIMsgIncomingServer> server;
1257 rv = GetIncomingServer(localFoldersServerKey, getter_AddRefs(server));
1258 if (server) {
1259 nsCOMPtr<nsIMsgAccount> localFoldersAccount;
1260 findAccountByServerKey(localFoldersServerKey,
1261 getter_AddRefs(localFoldersAccount));
1262 // If we don't have an existing account pointing at the local folders
1263 // server, we're going to add one.
1264 if (!localFoldersAccount) {
1265 nsCOMPtr<nsIMsgAccount> account;
1266 (void)CreateAccount(getter_AddRefs(account));
1267 if (account) account->SetIncomingServer(server);
1268 }
1269 }
1270 }
1271 return NS_OK;
1272 }
1273
1274 NS_IMETHODIMP
ReactivateAccounts()1275 nsMsgAccountManager::ReactivateAccounts() {
1276 for (nsIMsgAccount* account : m_accounts) {
1277 // This will error out if the account already has its server, or
1278 // if this isn't the account that the extension is trying to reactivate.
1279 if (NS_SUCCEEDED(account->CreateServer())) {
1280 nsCOMPtr<nsIMsgIncomingServer> server;
1281 account->GetIncomingServer(getter_AddRefs(server));
1282 // This triggers all of the notifications required by the UI.
1283 account->SetIncomingServer(server);
1284 }
1285 }
1286 return NS_OK;
1287 }
1288
1289 // this routine goes through all the identities and makes sure
1290 // that the special folders for each identity have the
1291 // correct special folder flags set, e.g, the Sent folder has
1292 // the sent flag set.
1293 //
1294 // it also goes through all the spam settings for each account
1295 // and makes sure the folder flags are set there, too
1296 NS_IMETHODIMP
SetSpecialFolders()1297 nsMsgAccountManager::SetSpecialFolders() {
1298 nsTArray<RefPtr<nsIMsgIdentity>> identities;
1299 GetAllIdentities(identities);
1300
1301 for (auto identity : identities) {
1302 nsresult rv;
1303 nsCString folderUri;
1304 nsCOMPtr<nsIMsgFolder> folder;
1305
1306 identity->GetFccFolder(folderUri);
1307 if (!folderUri.IsEmpty() &&
1308 NS_SUCCEEDED(GetOrCreateFolder(folderUri, getter_AddRefs(folder)))) {
1309 nsCOMPtr<nsIMsgFolder> parent;
1310 rv = folder->GetParent(getter_AddRefs(parent));
1311 if (NS_SUCCEEDED(rv) && parent) {
1312 rv = folder->SetFlag(nsMsgFolderFlags::SentMail);
1313 NS_ENSURE_SUCCESS(rv, rv);
1314 }
1315 }
1316
1317 identity->GetDraftFolder(folderUri);
1318 if (!folderUri.IsEmpty() &&
1319 NS_SUCCEEDED(GetOrCreateFolder(folderUri, getter_AddRefs(folder)))) {
1320 nsCOMPtr<nsIMsgFolder> parent;
1321 rv = folder->GetParent(getter_AddRefs(parent));
1322 if (NS_SUCCEEDED(rv) && parent) {
1323 rv = folder->SetFlag(nsMsgFolderFlags::Drafts);
1324 NS_ENSURE_SUCCESS(rv, rv);
1325 }
1326 }
1327
1328 identity->GetArchiveFolder(folderUri);
1329 if (!folderUri.IsEmpty() &&
1330 NS_SUCCEEDED(GetOrCreateFolder(folderUri, getter_AddRefs(folder)))) {
1331 nsCOMPtr<nsIMsgFolder> parent;
1332 rv = folder->GetParent(getter_AddRefs(parent));
1333 if (NS_SUCCEEDED(rv) && parent) {
1334 bool archiveEnabled;
1335 identity->GetArchiveEnabled(&archiveEnabled);
1336 if (archiveEnabled)
1337 rv = folder->SetFlag(nsMsgFolderFlags::Archive);
1338 else
1339 rv = folder->ClearFlag(nsMsgFolderFlags::Archive);
1340 NS_ENSURE_SUCCESS(rv, rv);
1341 }
1342 }
1343
1344 identity->GetStationeryFolder(folderUri);
1345 if (!folderUri.IsEmpty() &&
1346 NS_SUCCEEDED(GetOrCreateFolder(folderUri, getter_AddRefs(folder)))) {
1347 nsCOMPtr<nsIMsgFolder> parent;
1348 rv = folder->GetParent(getter_AddRefs(parent));
1349 if (NS_SUCCEEDED(rv) && parent) {
1350 folder->SetFlag(nsMsgFolderFlags::Templates);
1351 NS_ENSURE_SUCCESS(rv, rv);
1352 }
1353 }
1354 }
1355
1356 // XXX todo
1357 // get all servers
1358 // get all spam settings for each server
1359 // set the JUNK folder flag on the spam folders, right?
1360 return NS_OK;
1361 }
1362
1363 NS_IMETHODIMP
UnloadAccounts()1364 nsMsgAccountManager::UnloadAccounts() {
1365 // release the default account
1366 m_defaultAccount = nullptr;
1367 for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
1368 nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
1369 if (!server) continue;
1370 nsresult rv;
1371 NotifyServerUnloaded(server);
1372
1373 nsCOMPtr<nsIMsgFolder> rootFolder;
1374 rv = server->GetRootFolder(getter_AddRefs(rootFolder));
1375 if (NS_SUCCEEDED(rv)) {
1376 removeListenersFromFolder(rootFolder);
1377
1378 rootFolder->Shutdown(true);
1379 }
1380 }
1381
1382 m_accounts.Clear(); // will release all elements
1383 m_identities.Clear();
1384 m_incomingServers.Clear();
1385 mAccountKeyList.Truncate();
1386 SetLastServerFound(nullptr, EmptyCString(), EmptyCString(), 0,
1387 EmptyCString());
1388
1389 if (m_accountsLoaded) {
1390 nsCOMPtr<nsIMsgMailSession> mailSession =
1391 do_GetService(NS_MSGMAILSESSION_CONTRACTID);
1392 if (mailSession) mailSession->RemoveFolderListener(this);
1393 m_accountsLoaded = false;
1394 }
1395
1396 return NS_OK;
1397 }
1398
1399 NS_IMETHODIMP
ShutdownServers()1400 nsMsgAccountManager::ShutdownServers() {
1401 for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
1402 nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
1403 if (server) server->Shutdown();
1404 }
1405 return NS_OK;
1406 }
1407
1408 NS_IMETHODIMP
CloseCachedConnections()1409 nsMsgAccountManager::CloseCachedConnections() {
1410 for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
1411 nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
1412 if (server) server->CloseCachedConnections();
1413 }
1414 return NS_OK;
1415 }
1416
1417 NS_IMETHODIMP
CleanupOnExit()1418 nsMsgAccountManager::CleanupOnExit() {
1419 // This can get called multiple times, and potentially re-entrantly.
1420 // So add some protection against that.
1421 if (m_shutdownInProgress) return NS_OK;
1422
1423 m_shutdownInProgress = true;
1424
1425 for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
1426 nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
1427
1428 bool emptyTrashOnExit = false;
1429 bool cleanupInboxOnExit = false;
1430 nsresult rv;
1431
1432 if (WeAreOffline()) break;
1433
1434 if (!server) continue;
1435
1436 server->GetEmptyTrashOnExit(&emptyTrashOnExit);
1437 nsCOMPtr<nsIImapIncomingServer> imapserver = do_QueryInterface(server);
1438 if (imapserver) {
1439 imapserver->GetCleanupInboxOnExit(&cleanupInboxOnExit);
1440 imapserver->SetShuttingDown(true);
1441 }
1442 if (emptyTrashOnExit || cleanupInboxOnExit) {
1443 nsCOMPtr<nsIMsgFolder> root;
1444 server->GetRootFolder(getter_AddRefs(root));
1445 nsCString type;
1446 server->GetType(type);
1447 if (root) {
1448 nsString passwd;
1449 bool serverRequiresPasswordForAuthentication = true;
1450 bool isImap = type.EqualsLiteral("imap");
1451 if (isImap) {
1452 server->GetServerRequiresPasswordForBiff(
1453 &serverRequiresPasswordForAuthentication);
1454 server->GetPassword(passwd);
1455 }
1456 if (!isImap || (isImap && (!serverRequiresPasswordForAuthentication ||
1457 !passwd.IsEmpty()))) {
1458 nsCOMPtr<nsIUrlListener> urlListener;
1459 nsCOMPtr<nsIMsgAccountManager> accountManager =
1460 do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
1461 if (NS_FAILED(rv)) continue;
1462
1463 if (isImap) urlListener = do_QueryInterface(accountManager, &rv);
1464
1465 if (isImap && cleanupInboxOnExit) {
1466 nsTArray<RefPtr<nsIMsgFolder>> subFolders;
1467 rv = root->GetSubFolders(subFolders);
1468 if (NS_SUCCEEDED(rv)) {
1469 for (nsIMsgFolder* folder : subFolders) {
1470 uint32_t flags;
1471 folder->GetFlags(&flags);
1472 if (flags & nsMsgFolderFlags::Inbox) {
1473 rv = folder->Compact(urlListener, nullptr /* msgwindow */);
1474 if (NS_SUCCEEDED(rv))
1475 accountManager->SetFolderDoingCleanupInbox(folder);
1476 break;
1477 }
1478 }
1479 }
1480 }
1481
1482 if (emptyTrashOnExit) {
1483 rv = root->EmptyTrash(nullptr, urlListener);
1484 if (isImap && NS_SUCCEEDED(rv))
1485 accountManager->SetFolderDoingEmptyTrash(root);
1486 }
1487
1488 if (isImap && urlListener) {
1489 nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
1490
1491 bool inProgress = false;
1492 if (cleanupInboxOnExit) {
1493 int32_t loopCount = 0; // used to break out after 5 seconds
1494 accountManager->GetCleanupInboxInProgress(&inProgress);
1495 while (inProgress && loopCount++ < 5000) {
1496 accountManager->GetCleanupInboxInProgress(&inProgress);
1497 PR_CEnterMonitor(root);
1498 PR_CWait(root, PR_MicrosecondsToInterval(1000UL));
1499 PR_CExitMonitor(root);
1500 NS_ProcessPendingEvents(thread,
1501 PR_MicrosecondsToInterval(1000UL));
1502 }
1503 }
1504 if (emptyTrashOnExit) {
1505 accountManager->GetEmptyTrashInProgress(&inProgress);
1506 int32_t loopCount = 0;
1507 while (inProgress && loopCount++ < 5000) {
1508 accountManager->GetEmptyTrashInProgress(&inProgress);
1509 PR_CEnterMonitor(root);
1510 PR_CWait(root, PR_MicrosecondsToInterval(1000UL));
1511 PR_CExitMonitor(root);
1512 NS_ProcessPendingEvents(thread,
1513 PR_MicrosecondsToInterval(1000UL));
1514 }
1515 }
1516 }
1517 }
1518 }
1519 }
1520 }
1521
1522 // Try to do this early on in the shutdown process before
1523 // necko shuts itself down.
1524 CloseCachedConnections();
1525 return NS_OK;
1526 }
1527
1528 NS_IMETHODIMP
WriteToFolderCache(nsIMsgFolderCache * folderCache)1529 nsMsgAccountManager::WriteToFolderCache(nsIMsgFolderCache* folderCache) {
1530 for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
1531 iter.Data()->WriteToFolderCache(folderCache);
1532 }
1533 return folderCache ? folderCache->Close() : NS_ERROR_FAILURE;
1534 }
1535
createKeyedAccount(const nsCString & key,bool forcePositionToEnd,nsIMsgAccount ** aAccount)1536 nsresult nsMsgAccountManager::createKeyedAccount(const nsCString& key,
1537 bool forcePositionToEnd,
1538 nsIMsgAccount** aAccount) {
1539 nsresult rv;
1540 nsCOMPtr<nsIMsgAccount> account = do_CreateInstance(kMsgAccountCID, &rv);
1541 NS_ENSURE_SUCCESS(rv, rv);
1542
1543 account->SetKey(key);
1544
1545 nsCString localFoldersAccountKey;
1546 nsCString lastFolderAccountKey;
1547 if (!forcePositionToEnd) {
1548 nsCOMPtr<nsIMsgIncomingServer> localFoldersServer;
1549 rv = GetLocalFoldersServer(getter_AddRefs(localFoldersServer));
1550 if (NS_SUCCEEDED(rv)) {
1551 for (auto account : m_accounts) {
1552 nsCOMPtr<nsIMsgIncomingServer> server;
1553 rv = account->GetIncomingServer(getter_AddRefs(server));
1554 if (NS_SUCCEEDED(rv) && server == localFoldersServer) {
1555 account->GetKey(localFoldersAccountKey);
1556 break;
1557 }
1558 }
1559 }
1560
1561 // Extracting the account key of the last mail acoount.
1562 for (int32_t index = m_accounts.Length() - 1; index >= 0; index--) {
1563 nsCOMPtr<nsIMsgIncomingServer> server;
1564 rv = m_accounts[index]->GetIncomingServer(getter_AddRefs(server));
1565 if (NS_SUCCEEDED(rv) && server) {
1566 nsCString accountType;
1567 rv = server->GetType(accountType);
1568 if (NS_SUCCEEDED(rv) && !accountType.EqualsLiteral("im")) {
1569 m_accounts[index]->GetKey(lastFolderAccountKey);
1570 break;
1571 }
1572 }
1573 }
1574 }
1575
1576 if (!forcePositionToEnd && !localFoldersAccountKey.IsEmpty() &&
1577 !lastFolderAccountKey.IsEmpty() &&
1578 lastFolderAccountKey == localFoldersAccountKey) {
1579 // Insert account before Local Folders if that is the last account.
1580 m_accounts.InsertElementAt(m_accounts.Length() - 1, account);
1581 } else {
1582 m_accounts.AppendElement(account);
1583 }
1584
1585 nsCString newAccountKeyList;
1586 nsCString accountKey;
1587 for (uint32_t index = 0; index < m_accounts.Length(); index++) {
1588 m_accounts[index]->GetKey(accountKey);
1589 if (index) newAccountKeyList.Append(ACCOUNT_DELIMITER);
1590 newAccountKeyList.Append(accountKey);
1591 }
1592 mAccountKeyList = newAccountKeyList;
1593
1594 m_prefs->SetCharPref(PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS, mAccountKeyList);
1595 account.forget(aAccount);
1596 return NS_OK;
1597 }
1598
1599 NS_IMETHODIMP
CreateAccount(nsIMsgAccount ** _retval)1600 nsMsgAccountManager::CreateAccount(nsIMsgAccount** _retval) {
1601 NS_ENSURE_ARG_POINTER(_retval);
1602
1603 nsAutoCString key;
1604 GetUniqueAccountKey(key);
1605
1606 return createKeyedAccount(key, false, _retval);
1607 }
1608
1609 NS_IMETHODIMP
GetAccount(const nsACString & aKey,nsIMsgAccount ** aAccount)1610 nsMsgAccountManager::GetAccount(const nsACString& aKey,
1611 nsIMsgAccount** aAccount) {
1612 NS_ENSURE_ARG_POINTER(aAccount);
1613 *aAccount = nullptr;
1614
1615 for (uint32_t i = 0; i < m_accounts.Length(); ++i) {
1616 nsCOMPtr<nsIMsgAccount> account(m_accounts[i]);
1617 nsCString key;
1618 account->GetKey(key);
1619 if (key.Equals(aKey)) {
1620 account.forget(aAccount);
1621 break;
1622 }
1623 }
1624
1625 // If not found, create on demand.
1626 return NS_OK;
1627 }
1628
1629 NS_IMETHODIMP
FindServerIndex(nsIMsgIncomingServer * server,int32_t * result)1630 nsMsgAccountManager::FindServerIndex(nsIMsgIncomingServer* server,
1631 int32_t* result) {
1632 NS_ENSURE_ARG_POINTER(server);
1633 NS_ENSURE_ARG_POINTER(result);
1634
1635 nsCString key;
1636 nsresult rv = server->GetKey(key);
1637 NS_ENSURE_SUCCESS(rv, rv);
1638
1639 // do this by account because the account list is in order
1640 uint32_t i;
1641 for (i = 0; i < m_accounts.Length(); ++i) {
1642 nsCOMPtr<nsIMsgIncomingServer> server;
1643 rv = m_accounts[i]->GetIncomingServer(getter_AddRefs(server));
1644 if (!server || NS_FAILED(rv)) continue;
1645
1646 nsCString serverKey;
1647 rv = server->GetKey(serverKey);
1648 if (NS_FAILED(rv)) continue;
1649
1650 // stop when found,
1651 // index will be set to the current index
1652 if (serverKey.Equals(key)) break;
1653 }
1654
1655 // Even if the search failed, we can return index.
1656 // This means that all servers not in the array return an index higher
1657 // than all "registered" servers.
1658 *result = i;
1659 return NS_OK;
1660 }
1661
AddIncomingServerListener(nsIIncomingServerListener * serverListener)1662 NS_IMETHODIMP nsMsgAccountManager::AddIncomingServerListener(
1663 nsIIncomingServerListener* serverListener) {
1664 m_incomingServerListeners.AppendObject(serverListener);
1665 return NS_OK;
1666 }
1667
RemoveIncomingServerListener(nsIIncomingServerListener * serverListener)1668 NS_IMETHODIMP nsMsgAccountManager::RemoveIncomingServerListener(
1669 nsIIncomingServerListener* serverListener) {
1670 m_incomingServerListeners.RemoveObject(serverListener);
1671 return NS_OK;
1672 }
1673
NotifyServerLoaded(nsIMsgIncomingServer * server)1674 NS_IMETHODIMP nsMsgAccountManager::NotifyServerLoaded(
1675 nsIMsgIncomingServer* server) {
1676 int32_t count = m_incomingServerListeners.Count();
1677 for (int32_t i = 0; i < count; i++) {
1678 nsIIncomingServerListener* listener = m_incomingServerListeners[i];
1679 listener->OnServerLoaded(server);
1680 }
1681
1682 return NS_OK;
1683 }
1684
NotifyServerUnloaded(nsIMsgIncomingServer * server)1685 NS_IMETHODIMP nsMsgAccountManager::NotifyServerUnloaded(
1686 nsIMsgIncomingServer* server) {
1687 NS_ENSURE_ARG_POINTER(server);
1688
1689 int32_t count = m_incomingServerListeners.Count();
1690 // Clear this to cut shutdown leaks. We are always passing valid non-null
1691 // server here.
1692 server->SetFilterList(nullptr);
1693
1694 for (int32_t i = 0; i < count; i++) {
1695 nsIIncomingServerListener* listener = m_incomingServerListeners[i];
1696 listener->OnServerUnloaded(server);
1697 }
1698
1699 return NS_OK;
1700 }
1701
NotifyServerChanged(nsIMsgIncomingServer * server)1702 NS_IMETHODIMP nsMsgAccountManager::NotifyServerChanged(
1703 nsIMsgIncomingServer* server) {
1704 int32_t count = m_incomingServerListeners.Count();
1705 for (int32_t i = 0; i < count; i++) {
1706 nsIIncomingServerListener* listener = m_incomingServerListeners[i];
1707 listener->OnServerChanged(server);
1708 }
1709
1710 return NS_OK;
1711 }
1712
1713 NS_IMETHODIMP
FindServerByURI(nsIURI * aURI,bool aRealFlag,nsIMsgIncomingServer ** aResult)1714 nsMsgAccountManager::FindServerByURI(nsIURI* aURI, bool aRealFlag,
1715 nsIMsgIncomingServer** aResult) {
1716 NS_ENSURE_ARG_POINTER(aURI);
1717
1718 nsresult rv = LoadAccounts();
1719 NS_ENSURE_SUCCESS(rv, rv);
1720
1721 // Get username and hostname and port so we can get the server
1722 nsAutoCString username;
1723 nsAutoCString escapedUsername;
1724 rv = aURI->GetUserPass(escapedUsername);
1725 if (NS_SUCCEEDED(rv) && !escapedUsername.IsEmpty())
1726 MsgUnescapeString(escapedUsername, 0, username);
1727
1728 nsAutoCString hostname;
1729 nsAutoCString escapedHostname;
1730 rv = aURI->GetHost(escapedHostname);
1731 if (NS_SUCCEEDED(rv) && !escapedHostname.IsEmpty())
1732 MsgUnescapeString(escapedHostname, 0, hostname);
1733
1734 nsAutoCString type;
1735 rv = aURI->GetScheme(type);
1736 if (NS_SUCCEEDED(rv) && !type.IsEmpty()) {
1737 // Remove "-message" from the scheme in case we get called with
1738 // "imap-message", "mailbox-message", or friends.
1739 if (StringEndsWith(type, nsDependentCString("-message")))
1740 type.SetLength(type.Length() - 8);
1741 // now modify type if pop or news
1742 if (type.EqualsLiteral("pop")) type.AssignLiteral("pop3");
1743 // we use "nntp" in the server list so translate it here.
1744 else if (type.EqualsLiteral("news"))
1745 type.AssignLiteral("nntp");
1746 // we use "any" as the wildcard type.
1747 else if (type.EqualsLiteral("any"))
1748 type.Truncate();
1749 }
1750
1751 int32_t port = 0;
1752 // check the port of the scheme is not 'none' or blank
1753 if (!(type.EqualsLiteral("none") || type.IsEmpty())) {
1754 rv = aURI->GetPort(&port);
1755 // Set the port to zero if we got a -1 (use default)
1756 if (NS_SUCCEEDED(rv) && (port == -1)) port = 0;
1757 }
1758
1759 return findServerInternal(username, hostname, type, port, aRealFlag, aResult);
1760 }
1761
findServerInternal(const nsACString & username,const nsACString & hostname,const nsACString & type,int32_t port,bool aRealFlag,nsIMsgIncomingServer ** aResult)1762 nsresult nsMsgAccountManager::findServerInternal(
1763 const nsACString& username, const nsACString& hostname,
1764 const nsACString& type, int32_t port, bool aRealFlag,
1765 nsIMsgIncomingServer** aResult) {
1766 // If 'aRealFlag' is set then we want to scan all existing accounts
1767 // to make sure there's no duplicate including those whose host and/or
1768 // user names have been changed.
1769 if (!aRealFlag && (m_lastFindServerUserName.Equals(username)) &&
1770 (m_lastFindServerHostName.Equals(hostname)) &&
1771 (m_lastFindServerType.Equals(type)) && (m_lastFindServerPort == port) &&
1772 m_lastFindServerResult) {
1773 NS_ADDREF(*aResult = m_lastFindServerResult);
1774 return NS_OK;
1775 }
1776
1777 for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
1778 // Find matching server by user+host+type+port.
1779 nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
1780
1781 if (!server) continue;
1782
1783 nsresult rv;
1784 nsCString thisHostname;
1785 if (aRealFlag)
1786 rv = server->GetRealHostName(thisHostname);
1787 else
1788 rv = server->GetHostName(thisHostname);
1789 if (NS_FAILED(rv)) continue;
1790
1791 nsCString thisUsername;
1792 if (aRealFlag)
1793 rv = server->GetRealUsername(thisUsername);
1794 else
1795 rv = server->GetUsername(thisUsername);
1796 if (NS_FAILED(rv)) continue;
1797
1798 nsCString thisType;
1799 rv = server->GetType(thisType);
1800 if (NS_FAILED(rv)) continue;
1801
1802 int32_t thisPort = -1; // use the default port identifier
1803 // Don't try and get a port for the 'none' scheme
1804 if (!thisType.EqualsLiteral("none")) {
1805 rv = server->GetPort(&thisPort);
1806 if (NS_FAILED(rv)) {
1807 continue;
1808 }
1809 }
1810
1811 // treat "" as a wild card, so if the caller passed in "" for the desired
1812 // attribute treat it as a match
1813 if ((type.IsEmpty() || thisType.Equals(type)) &&
1814 (hostname.IsEmpty() ||
1815 thisHostname.Equals(hostname, nsCaseInsensitiveCStringComparator)) &&
1816 (!(port != 0) || (port == thisPort)) &&
1817 (username.IsEmpty() || thisUsername.Equals(username))) {
1818 // stop on first find; cache for next time
1819 if (!aRealFlag)
1820 SetLastServerFound(server, hostname, username, port, type);
1821
1822 NS_ADDREF(*aResult = server); // Was populated from member variable.
1823 return NS_OK;
1824 }
1825 }
1826
1827 return NS_ERROR_UNEXPECTED;
1828 }
1829
1830 NS_IMETHODIMP
FindServer(const nsACString & username,const nsACString & hostname,const nsACString & type,nsIMsgIncomingServer ** aResult)1831 nsMsgAccountManager::FindServer(const nsACString& username,
1832 const nsACString& hostname,
1833 const nsACString& type,
1834 nsIMsgIncomingServer** aResult) {
1835 return findServerInternal(username, hostname, type, 0, false, aResult);
1836 }
1837
1838 // Interface called by UI js only (always return true).
1839 NS_IMETHODIMP
FindRealServer(const nsACString & username,const nsACString & hostname,const nsACString & type,int32_t port,nsIMsgIncomingServer ** aResult)1840 nsMsgAccountManager::FindRealServer(const nsACString& username,
1841 const nsACString& hostname,
1842 const nsACString& type, int32_t port,
1843 nsIMsgIncomingServer** aResult) {
1844 *aResult = nullptr;
1845 findServerInternal(username, hostname, type, port, true, aResult);
1846 return NS_OK;
1847 }
1848
findAccountByServerKey(const nsCString & aKey,nsIMsgAccount ** aResult)1849 void nsMsgAccountManager::findAccountByServerKey(const nsCString& aKey,
1850 nsIMsgAccount** aResult) {
1851 *aResult = nullptr;
1852
1853 for (uint32_t i = 0; i < m_accounts.Length(); ++i) {
1854 nsCOMPtr<nsIMsgIncomingServer> server;
1855 nsresult rv = m_accounts[i]->GetIncomingServer(getter_AddRefs(server));
1856 if (!server || NS_FAILED(rv)) continue;
1857
1858 nsCString key;
1859 rv = server->GetKey(key);
1860 if (NS_FAILED(rv)) continue;
1861
1862 // if the keys are equal, the servers are equal
1863 if (key.Equals(aKey)) {
1864 NS_ADDREF(*aResult = m_accounts[i]);
1865 break; // stop on first found account
1866 }
1867 }
1868 }
1869
1870 NS_IMETHODIMP
FindAccountForServer(nsIMsgIncomingServer * server,nsIMsgAccount ** aResult)1871 nsMsgAccountManager::FindAccountForServer(nsIMsgIncomingServer* server,
1872 nsIMsgAccount** aResult) {
1873 NS_ENSURE_ARG_POINTER(aResult);
1874
1875 if (!server) {
1876 (*aResult) = nullptr;
1877 return NS_OK;
1878 }
1879
1880 nsresult rv;
1881
1882 nsCString key;
1883 rv = server->GetKey(key);
1884 NS_ENSURE_SUCCESS(rv, rv);
1885
1886 findAccountByServerKey(key, aResult);
1887 return NS_OK;
1888 }
1889
1890 NS_IMETHODIMP
GetFirstIdentityForServer(nsIMsgIncomingServer * aServer,nsIMsgIdentity ** aIdentity)1891 nsMsgAccountManager::GetFirstIdentityForServer(nsIMsgIncomingServer* aServer,
1892 nsIMsgIdentity** aIdentity) {
1893 NS_ENSURE_ARG_POINTER(aServer);
1894 NS_ENSURE_ARG_POINTER(aIdentity);
1895
1896 nsTArray<RefPtr<nsIMsgIdentity>> identities;
1897 nsresult rv = GetIdentitiesForServer(aServer, identities);
1898 NS_ENSURE_SUCCESS(rv, rv);
1899
1900 // not all servers have identities
1901 // for example, Local Folders
1902 if (identities.IsEmpty()) {
1903 *aIdentity = nullptr;
1904 } else {
1905 NS_IF_ADDREF(*aIdentity = identities[0]);
1906 }
1907 return rv;
1908 }
1909
1910 NS_IMETHODIMP
GetIdentitiesForServer(nsIMsgIncomingServer * server,nsTArray<RefPtr<nsIMsgIdentity>> & identities)1911 nsMsgAccountManager::GetIdentitiesForServer(
1912 nsIMsgIncomingServer* server,
1913 nsTArray<RefPtr<nsIMsgIdentity>>& identities) {
1914 NS_ENSURE_ARG_POINTER(server);
1915 nsresult rv = LoadAccounts();
1916 NS_ENSURE_SUCCESS(rv, rv);
1917
1918 identities.Clear();
1919
1920 nsAutoCString serverKey;
1921 rv = server->GetKey(serverKey);
1922 NS_ENSURE_SUCCESS(rv, rv);
1923
1924 for (auto account : m_accounts) {
1925 nsCOMPtr<nsIMsgIncomingServer> thisServer;
1926 rv = account->GetIncomingServer(getter_AddRefs(thisServer));
1927 if (NS_FAILED(rv) || !thisServer) continue;
1928
1929 nsAutoCString thisServerKey;
1930 rv = thisServer->GetKey(thisServerKey);
1931 if (serverKey.Equals(thisServerKey)) {
1932 nsTArray<RefPtr<nsIMsgIdentity>> theseIdentities;
1933 rv = account->GetIdentities(theseIdentities);
1934 NS_ENSURE_SUCCESS(rv, rv);
1935 identities.AppendElements(theseIdentities);
1936 }
1937 }
1938 return NS_OK;
1939 }
1940
1941 NS_IMETHODIMP
GetServersForIdentity(nsIMsgIdentity * aIdentity,nsTArray<RefPtr<nsIMsgIncomingServer>> & servers)1942 nsMsgAccountManager::GetServersForIdentity(
1943 nsIMsgIdentity* aIdentity,
1944 nsTArray<RefPtr<nsIMsgIncomingServer>>& servers) {
1945 NS_ENSURE_ARG_POINTER(aIdentity);
1946 servers.Clear();
1947
1948 nsresult rv;
1949 rv = LoadAccounts();
1950 NS_ENSURE_SUCCESS(rv, rv);
1951
1952 for (auto account : m_accounts) {
1953 nsTArray<RefPtr<nsIMsgIdentity>> identities;
1954 if (NS_FAILED(account->GetIdentities(identities))) continue;
1955
1956 nsCString identityKey;
1957 aIdentity->GetKey(identityKey);
1958 for (auto thisIdentity : identities) {
1959 nsCString thisIdentityKey;
1960 rv = thisIdentity->GetKey(thisIdentityKey);
1961
1962 if (NS_SUCCEEDED(rv) && identityKey.Equals(thisIdentityKey)) {
1963 nsCOMPtr<nsIMsgIncomingServer> thisServer;
1964 rv = account->GetIncomingServer(getter_AddRefs(thisServer));
1965 if (thisServer && NS_SUCCEEDED(rv)) {
1966 servers.AppendElement(thisServer);
1967 break;
1968 }
1969 }
1970 }
1971 }
1972 return NS_OK;
1973 }
1974
1975 NS_IMETHODIMP
AddRootFolderListener(nsIFolderListener * aListener)1976 nsMsgAccountManager::AddRootFolderListener(nsIFolderListener* aListener) {
1977 NS_ENSURE_TRUE(aListener, NS_OK);
1978 mFolderListeners.AppendElement(aListener);
1979 for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
1980 nsCOMPtr<nsIMsgFolder> rootFolder;
1981 nsresult rv = iter.Data()->GetRootFolder(getter_AddRefs(rootFolder));
1982 if (NS_FAILED(rv)) {
1983 continue;
1984 }
1985 rv = rootFolder->AddFolderListener(aListener);
1986 }
1987 return NS_OK;
1988 }
1989
1990 NS_IMETHODIMP
RemoveRootFolderListener(nsIFolderListener * aListener)1991 nsMsgAccountManager::RemoveRootFolderListener(nsIFolderListener* aListener) {
1992 NS_ENSURE_TRUE(aListener, NS_OK);
1993 mFolderListeners.RemoveElement(aListener);
1994 for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
1995 nsCOMPtr<nsIMsgFolder> rootFolder;
1996 nsresult rv = iter.Data()->GetRootFolder(getter_AddRefs(rootFolder));
1997 if (NS_FAILED(rv)) {
1998 continue;
1999 }
2000 rv = rootFolder->RemoveFolderListener(aListener);
2001 }
2002
2003 return NS_OK;
2004 }
2005
SetLocalFoldersServer(nsIMsgIncomingServer * aServer)2006 NS_IMETHODIMP nsMsgAccountManager::SetLocalFoldersServer(
2007 nsIMsgIncomingServer* aServer) {
2008 NS_ENSURE_ARG_POINTER(aServer);
2009 nsCString key;
2010 nsresult rv = aServer->GetKey(key);
2011 NS_ENSURE_SUCCESS(rv, rv);
2012
2013 return m_prefs->SetCharPref(PREF_MAIL_ACCOUNTMANAGER_LOCALFOLDERSSERVER, key);
2014 }
2015
GetLocalFoldersServer(nsIMsgIncomingServer ** aServer)2016 NS_IMETHODIMP nsMsgAccountManager::GetLocalFoldersServer(
2017 nsIMsgIncomingServer** aServer) {
2018 NS_ENSURE_ARG_POINTER(aServer);
2019
2020 nsCString serverKey;
2021
2022 nsresult rv = m_prefs->GetCharPref(
2023 PREF_MAIL_ACCOUNTMANAGER_LOCALFOLDERSSERVER, serverKey);
2024
2025 if (NS_SUCCEEDED(rv) && !serverKey.IsEmpty()) {
2026 rv = GetIncomingServer(serverKey, aServer);
2027 if (NS_SUCCEEDED(rv)) return rv;
2028 // otherwise, we're going to fall through to looking for an existing local
2029 // folders account, because now we fail creating one if one already exists.
2030 }
2031
2032 // try ("nobody","Local Folders","none"), and work down to any "none" server.
2033 rv = FindServer("nobody"_ns, "Local Folders"_ns, "none"_ns, aServer);
2034 if (NS_FAILED(rv) || !*aServer) {
2035 rv = FindServer("nobody"_ns, EmptyCString(), "none"_ns, aServer);
2036 if (NS_FAILED(rv) || !*aServer) {
2037 rv = FindServer(EmptyCString(), "Local Folders"_ns, "none"_ns, aServer);
2038 if (NS_FAILED(rv) || !*aServer)
2039 rv = FindServer(EmptyCString(), EmptyCString(), "none"_ns, aServer);
2040 }
2041 }
2042
2043 NS_ENSURE_SUCCESS(rv, rv);
2044 if (!*aServer) return NS_ERROR_FAILURE;
2045
2046 // we don't want the Smart Mailboxes server to be the local server.
2047 bool hidden;
2048 (*aServer)->GetHidden(&hidden);
2049 if (hidden) return NS_ERROR_FAILURE;
2050
2051 rv = SetLocalFoldersServer(*aServer);
2052 return rv;
2053 }
2054
GetLocalFoldersPrettyName(nsString & localFoldersName)2055 nsresult nsMsgAccountManager::GetLocalFoldersPrettyName(
2056 nsString& localFoldersName) {
2057 // we don't want "nobody at Local Folders" to show up in the
2058 // folder pane, so we set the pretty name to a localized "Local Folders"
2059 nsCOMPtr<nsIStringBundle> bundle;
2060 nsresult rv;
2061 nsCOMPtr<nsIStringBundleService> sBundleService =
2062 mozilla::services::GetStringBundleService();
2063 NS_ENSURE_TRUE(sBundleService, NS_ERROR_UNEXPECTED);
2064
2065 rv = sBundleService->CreateBundle(
2066 "chrome://messenger/locale/messenger.properties", getter_AddRefs(bundle));
2067 NS_ENSURE_SUCCESS(rv, rv);
2068
2069 return bundle->GetStringFromName("localFolders", localFoldersName);
2070 }
2071
2072 NS_IMETHODIMP
CreateLocalMailAccount()2073 nsMsgAccountManager::CreateLocalMailAccount() {
2074 // create the server
2075 nsCOMPtr<nsIMsgIncomingServer> server;
2076 nsresult rv = CreateIncomingServer("nobody"_ns, "Local Folders"_ns, "none"_ns,
2077 getter_AddRefs(server));
2078 NS_ENSURE_SUCCESS(rv, rv);
2079
2080 nsString localFoldersName;
2081 rv = GetLocalFoldersPrettyName(localFoldersName);
2082 NS_ENSURE_SUCCESS(rv, rv);
2083 server->SetPrettyName(localFoldersName);
2084
2085 nsCOMPtr<nsINoIncomingServer> noServer;
2086 noServer = do_QueryInterface(server, &rv);
2087 if (NS_FAILED(rv)) return rv;
2088
2089 // create the directory structure for old 4.x "Local Mail"
2090 // under <profile dir>/Mail/Local Folders or
2091 // <"mail.directory" pref>/Local Folders
2092 nsCOMPtr<nsIFile> mailDir;
2093 bool dirExists;
2094
2095 // we want <profile>/Mail
2096 rv = NS_GetSpecialDirectory(NS_APP_MAIL_50_DIR, getter_AddRefs(mailDir));
2097 if (NS_FAILED(rv)) return rv;
2098
2099 rv = mailDir->Exists(&dirExists);
2100 if (NS_SUCCEEDED(rv) && !dirExists)
2101 rv = mailDir->Create(nsIFile::DIRECTORY_TYPE, 0775);
2102 if (NS_FAILED(rv)) return rv;
2103
2104 // set the default local path for "none"
2105 rv = server->SetDefaultLocalPath(mailDir);
2106 if (NS_FAILED(rv)) return rv;
2107
2108 // Create an account when valid server values are established.
2109 // This will keep the status of accounts sane by avoiding the addition of
2110 // incomplete accounts.
2111 nsCOMPtr<nsIMsgAccount> account;
2112 rv = CreateAccount(getter_AddRefs(account));
2113 if (NS_FAILED(rv)) return rv;
2114
2115 // notice, no identity for local mail
2116 // hook the server to the account
2117 // after we set the server's local path
2118 // (see bug #66018)
2119 account->SetIncomingServer(server);
2120
2121 // remember this as the local folders server
2122 return SetLocalFoldersServer(server);
2123 }
2124
2125 // nsIUrlListener methods
2126
2127 NS_IMETHODIMP
OnStartRunningUrl(nsIURI * aUrl)2128 nsMsgAccountManager::OnStartRunningUrl(nsIURI* aUrl) { return NS_OK; }
2129
2130 NS_IMETHODIMP
OnStopRunningUrl(nsIURI * aUrl,nsresult aExitCode)2131 nsMsgAccountManager::OnStopRunningUrl(nsIURI* aUrl, nsresult aExitCode) {
2132 if (aUrl) {
2133 nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(aUrl);
2134 if (imapUrl) {
2135 nsImapAction imapAction = nsIImapUrl::nsImapTest;
2136 imapUrl->GetImapAction(&imapAction);
2137 switch (imapAction) {
2138 case nsIImapUrl::nsImapExpungeFolder:
2139 if (m_folderDoingCleanupInbox) {
2140 PR_CEnterMonitor(m_folderDoingCleanupInbox);
2141 PR_CNotifyAll(m_folderDoingCleanupInbox);
2142 m_cleanupInboxInProgress = false;
2143 PR_CExitMonitor(m_folderDoingCleanupInbox);
2144 m_folderDoingCleanupInbox = nullptr; // reset to nullptr
2145 }
2146 break;
2147 case nsIImapUrl::nsImapDeleteAllMsgs:
2148 if (m_folderDoingEmptyTrash) {
2149 PR_CEnterMonitor(m_folderDoingEmptyTrash);
2150 PR_CNotifyAll(m_folderDoingEmptyTrash);
2151 m_emptyTrashInProgress = false;
2152 PR_CExitMonitor(m_folderDoingEmptyTrash);
2153 m_folderDoingEmptyTrash = nullptr; // reset to nullptr;
2154 }
2155 break;
2156 default:
2157 break;
2158 }
2159 }
2160 }
2161 return NS_OK;
2162 }
2163
2164 NS_IMETHODIMP
SetFolderDoingEmptyTrash(nsIMsgFolder * folder)2165 nsMsgAccountManager::SetFolderDoingEmptyTrash(nsIMsgFolder* folder) {
2166 m_folderDoingEmptyTrash = folder;
2167 m_emptyTrashInProgress = true;
2168 return NS_OK;
2169 }
2170
2171 NS_IMETHODIMP
GetEmptyTrashInProgress(bool * bVal)2172 nsMsgAccountManager::GetEmptyTrashInProgress(bool* bVal) {
2173 NS_ENSURE_ARG_POINTER(bVal);
2174 *bVal = m_emptyTrashInProgress;
2175 return NS_OK;
2176 }
2177
2178 NS_IMETHODIMP
SetFolderDoingCleanupInbox(nsIMsgFolder * folder)2179 nsMsgAccountManager::SetFolderDoingCleanupInbox(nsIMsgFolder* folder) {
2180 m_folderDoingCleanupInbox = folder;
2181 m_cleanupInboxInProgress = true;
2182 return NS_OK;
2183 }
2184
2185 NS_IMETHODIMP
GetCleanupInboxInProgress(bool * bVal)2186 nsMsgAccountManager::GetCleanupInboxInProgress(bool* bVal) {
2187 NS_ENSURE_ARG_POINTER(bVal);
2188 *bVal = m_cleanupInboxInProgress;
2189 return NS_OK;
2190 }
2191
SetLastServerFound(nsIMsgIncomingServer * server,const nsACString & hostname,const nsACString & username,const int32_t port,const nsACString & type)2192 void nsMsgAccountManager::SetLastServerFound(nsIMsgIncomingServer* server,
2193 const nsACString& hostname,
2194 const nsACString& username,
2195 const int32_t port,
2196 const nsACString& type) {
2197 m_lastFindServerResult = server;
2198 m_lastFindServerHostName = hostname;
2199 m_lastFindServerUserName = username;
2200 m_lastFindServerPort = port;
2201 m_lastFindServerType = type;
2202 }
2203
2204 NS_IMETHODIMP
SaveAccountInfo()2205 nsMsgAccountManager::SaveAccountInfo() {
2206 nsresult rv;
2207 nsCOMPtr<nsIPrefService> pref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
2208 NS_ENSURE_SUCCESS(rv, rv);
2209 return pref->SavePrefFile(nullptr);
2210 }
2211
2212 NS_IMETHODIMP
GetChromePackageName(const nsACString & aExtensionName,nsACString & aChromePackageName)2213 nsMsgAccountManager::GetChromePackageName(const nsACString& aExtensionName,
2214 nsACString& aChromePackageName) {
2215 nsresult rv;
2216 nsCOMPtr<nsICategoryManager> catman =
2217 do_GetService(NS_CATEGORYMANAGER_CONTRACTID, &rv);
2218 NS_ENSURE_SUCCESS(rv, rv);
2219
2220 nsCOMPtr<nsISimpleEnumerator> e;
2221 rv = catman->EnumerateCategory(MAILNEWS_ACCOUNTMANAGER_EXTENSIONS,
2222 getter_AddRefs(e));
2223 if (NS_SUCCEEDED(rv) && e) {
2224 while (true) {
2225 nsCOMPtr<nsISupports> supports;
2226 rv = e->GetNext(getter_AddRefs(supports));
2227 nsCOMPtr<nsISupportsCString> catEntry = do_QueryInterface(supports);
2228 if (NS_FAILED(rv) || !catEntry) break;
2229
2230 nsAutoCString entryString;
2231 rv = catEntry->GetData(entryString);
2232 if (NS_FAILED(rv)) break;
2233
2234 nsCString contractidString;
2235 rv = catman->GetCategoryEntry(MAILNEWS_ACCOUNTMANAGER_EXTENSIONS,
2236 entryString, contractidString);
2237 if (NS_FAILED(rv)) break;
2238
2239 nsCOMPtr<nsIMsgAccountManagerExtension> extension =
2240 do_GetService(contractidString.get(), &rv);
2241 if (NS_FAILED(rv) || !extension) break;
2242
2243 nsCString name;
2244 rv = extension->GetName(name);
2245 if (NS_FAILED(rv)) break;
2246
2247 if (name.Equals(aExtensionName))
2248 return extension->GetChromePackageName(aChromePackageName);
2249 }
2250 }
2251 return NS_ERROR_UNEXPECTED;
2252 }
2253
2254 class VFChangeListenerEvent : public mozilla::Runnable {
2255 public:
VFChangeListenerEvent(VirtualFolderChangeListener * vfChangeListener,nsIMsgFolder * virtFolder,nsIMsgDatabase * virtDB)2256 VFChangeListenerEvent(VirtualFolderChangeListener* vfChangeListener,
2257 nsIMsgFolder* virtFolder, nsIMsgDatabase* virtDB)
2258 : mozilla::Runnable("VFChangeListenerEvent"),
2259 mVFChangeListener(vfChangeListener),
2260 mFolder(virtFolder),
2261 mDB(virtDB) {}
2262
Run()2263 NS_IMETHOD Run() {
2264 if (mVFChangeListener) mVFChangeListener->ProcessUpdateEvent(mFolder, mDB);
2265 return NS_OK;
2266 }
2267
2268 private:
2269 RefPtr<VirtualFolderChangeListener> mVFChangeListener;
2270 nsCOMPtr<nsIMsgFolder> mFolder;
2271 nsCOMPtr<nsIMsgDatabase> mDB;
2272 };
2273
NS_IMPL_ISUPPORTS(VirtualFolderChangeListener,nsIDBChangeListener)2274 NS_IMPL_ISUPPORTS(VirtualFolderChangeListener, nsIDBChangeListener)
2275
2276 VirtualFolderChangeListener::VirtualFolderChangeListener()
2277 : m_searchOnMsgStatus(false), m_batchingEvents(false) {}
2278
Init()2279 nsresult VirtualFolderChangeListener::Init() {
2280 nsCOMPtr<nsIMsgDatabase> msgDB;
2281 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
2282
2283 nsresult rv = m_virtualFolder->GetDBFolderInfoAndDB(
2284 getter_AddRefs(dbFolderInfo), getter_AddRefs(msgDB));
2285 if (NS_SUCCEEDED(rv) && msgDB) {
2286 nsCString searchTermString;
2287 dbFolderInfo->GetCharProperty("searchStr", searchTermString);
2288 nsCOMPtr<nsIMsgFilterService> filterService =
2289 do_GetService(NS_MSGFILTERSERVICE_CONTRACTID, &rv);
2290 nsCOMPtr<nsIMsgFilterList> filterList;
2291 rv = filterService->GetTempFilterList(m_virtualFolder,
2292 getter_AddRefs(filterList));
2293 NS_ENSURE_SUCCESS(rv, rv);
2294 nsCOMPtr<nsIMsgFilter> tempFilter;
2295 filterList->CreateFilter(u"temp"_ns, getter_AddRefs(tempFilter));
2296 NS_ENSURE_SUCCESS(rv, rv);
2297 filterList->ParseCondition(tempFilter, searchTermString.get());
2298 NS_ENSURE_SUCCESS(rv, rv);
2299 m_searchSession = do_CreateInstance(NS_MSGSEARCHSESSION_CONTRACTID, &rv);
2300 NS_ENSURE_SUCCESS(rv, rv);
2301
2302 nsTArray<RefPtr<nsIMsgSearchTerm>> searchTerms;
2303 rv = tempFilter->GetSearchTerms(searchTerms);
2304 NS_ENSURE_SUCCESS(rv, rv);
2305
2306 // we add the search scope right before we match the header,
2307 // because we don't want the search scope caching the body input
2308 // stream, because that holds onto the mailbox file, breaking
2309 // compaction.
2310
2311 // add each search term to the search session
2312 for (nsIMsgSearchTerm* searchTerm : searchTerms) {
2313 nsMsgSearchAttribValue attrib;
2314 searchTerm->GetAttrib(&attrib);
2315 if (attrib == nsMsgSearchAttrib::MsgStatus) m_searchOnMsgStatus = true;
2316 m_searchSession->AppendTerm(searchTerm);
2317 }
2318 }
2319 return rv;
2320 }
2321
2322 /**
2323 * nsIDBChangeListener
2324 */
2325
2326 NS_IMETHODIMP
OnHdrPropertyChanged(nsIMsgDBHdr * aHdrChanged,bool aPreChange,uint32_t * aStatus,nsIDBChangeListener * aInstigator)2327 VirtualFolderChangeListener::OnHdrPropertyChanged(
2328 nsIMsgDBHdr* aHdrChanged, bool aPreChange, uint32_t* aStatus,
2329 nsIDBChangeListener* aInstigator) {
2330 const uint32_t kMatch = 0x1;
2331 const uint32_t kRead = 0x2;
2332 const uint32_t kNew = 0x4;
2333 NS_ENSURE_ARG_POINTER(aHdrChanged);
2334 NS_ENSURE_ARG_POINTER(aStatus);
2335
2336 uint32_t flags;
2337 bool match;
2338 nsCOMPtr<nsIMsgDatabase> msgDB;
2339 nsresult rv = m_folderWatching->GetMsgDatabase(getter_AddRefs(msgDB));
2340 NS_ENSURE_SUCCESS(rv, rv);
2341 // we don't want any early returns from this function, until we've
2342 // called ClearScopes on the search session.
2343 m_searchSession->AddScopeTerm(nsMsgSearchScope::offlineMail,
2344 m_folderWatching);
2345 rv = m_searchSession->MatchHdr(aHdrChanged, msgDB, &match);
2346 m_searchSession->ClearScopes();
2347 NS_ENSURE_SUCCESS(rv, rv);
2348 aHdrChanged->GetFlags(&flags);
2349
2350 if (aPreChange) // We're looking at the old header, save status
2351 {
2352 *aStatus = 0;
2353 if (match) *aStatus |= kMatch;
2354 if (flags & nsMsgMessageFlags::Read) *aStatus |= kRead;
2355 if (flags & nsMsgMessageFlags::New) *aStatus |= kNew;
2356 return NS_OK;
2357 }
2358
2359 // This is the post change section where changes are detected
2360
2361 bool wasMatch = *aStatus & kMatch;
2362 if (!match && !wasMatch) // header not in virtual folder
2363 return NS_OK;
2364
2365 int32_t totalDelta = 0, unreadDelta = 0, newDelta = 0;
2366
2367 if (match) {
2368 totalDelta++;
2369 if (!(flags & nsMsgMessageFlags::Read)) unreadDelta++;
2370 if (flags & nsMsgMessageFlags::New) newDelta++;
2371 }
2372
2373 if (wasMatch) {
2374 totalDelta--;
2375 if (!(*aStatus & kRead)) unreadDelta--;
2376 if (*aStatus & kNew) newDelta--;
2377 }
2378
2379 if (!(unreadDelta || totalDelta || newDelta)) return NS_OK;
2380
2381 nsCOMPtr<nsIMsgDatabase> virtDatabase;
2382 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
2383 rv = m_virtualFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo),
2384 getter_AddRefs(virtDatabase));
2385 NS_ENSURE_SUCCESS(rv, rv);
2386
2387 if (unreadDelta) dbFolderInfo->ChangeNumUnreadMessages(unreadDelta);
2388
2389 if (newDelta) {
2390 int32_t numNewMessages;
2391 m_virtualFolder->GetNumNewMessages(false, &numNewMessages);
2392 m_virtualFolder->SetNumNewMessages(numNewMessages + newDelta);
2393 m_virtualFolder->SetHasNewMessages(numNewMessages + newDelta > 0);
2394 }
2395
2396 if (totalDelta) {
2397 dbFolderInfo->ChangeNumMessages(totalDelta);
2398 nsCString searchUri;
2399 m_virtualFolder->GetURI(searchUri);
2400 msgDB->UpdateHdrInCache(searchUri, aHdrChanged, totalDelta == 1);
2401 }
2402
2403 PostUpdateEvent(m_virtualFolder, virtDatabase);
2404
2405 return NS_OK;
2406 }
2407
DecrementNewMsgCount()2408 void VirtualFolderChangeListener::DecrementNewMsgCount() {
2409 int32_t numNewMessages;
2410 m_virtualFolder->GetNumNewMessages(false, &numNewMessages);
2411 if (numNewMessages > 0) numNewMessages--;
2412 m_virtualFolder->SetNumNewMessages(numNewMessages);
2413 if (!numNewMessages) m_virtualFolder->SetHasNewMessages(false);
2414 }
2415
OnHdrFlagsChanged(nsIMsgDBHdr * aHdrChanged,uint32_t aOldFlags,uint32_t aNewFlags,nsIDBChangeListener * aInstigator)2416 NS_IMETHODIMP VirtualFolderChangeListener::OnHdrFlagsChanged(
2417 nsIMsgDBHdr* aHdrChanged, uint32_t aOldFlags, uint32_t aNewFlags,
2418 nsIDBChangeListener* aInstigator) {
2419 nsCOMPtr<nsIMsgDatabase> msgDB;
2420
2421 nsresult rv = m_folderWatching->GetMsgDatabase(getter_AddRefs(msgDB));
2422 bool oldMatch = false, newMatch = false;
2423 // we don't want any early returns from this function, until we've
2424 // called ClearScopes 0n the search session.
2425 m_searchSession->AddScopeTerm(nsMsgSearchScope::offlineMail,
2426 m_folderWatching);
2427 rv = m_searchSession->MatchHdr(aHdrChanged, msgDB, &newMatch);
2428 if (NS_SUCCEEDED(rv) && m_searchOnMsgStatus) {
2429 // if status is a search criteria, check if the header matched before
2430 // it changed, in order to determine if we need to bump the counts.
2431 aHdrChanged->SetFlags(aOldFlags);
2432 rv = m_searchSession->MatchHdr(aHdrChanged, msgDB, &oldMatch);
2433 // restore new flags even on match failure.
2434 aHdrChanged->SetFlags(aNewFlags);
2435 } else
2436 oldMatch = newMatch;
2437 m_searchSession->ClearScopes();
2438 NS_ENSURE_SUCCESS(rv, rv);
2439 // we don't want to change the total counts if this virtual folder is open in
2440 // a view, because we won't remove the header from view while it's open. On
2441 // the other hand, it's hard to fix the count when the user clicks away to
2442 // another folder, w/o re-running the search, or setting some sort of pending
2443 // count change. Maybe this needs to be handled in the view code...the view
2444 // could do the same calculation and also keep track of the counts changed.
2445 // Then, when the view was closed, if it's a virtual folder, it could update
2446 // the counts for the db.
2447 if (oldMatch != newMatch ||
2448 (oldMatch && (aOldFlags & nsMsgMessageFlags::Read) !=
2449 (aNewFlags & nsMsgMessageFlags::Read))) {
2450 nsCOMPtr<nsIMsgDatabase> virtDatabase;
2451 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
2452
2453 rv = m_virtualFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo),
2454 getter_AddRefs(virtDatabase));
2455 NS_ENSURE_SUCCESS(rv, rv);
2456 int32_t totalDelta = 0, unreadDelta = 0;
2457 if (oldMatch != newMatch) {
2458 // bool isOpen = false;
2459 // nsCOMPtr<nsIMsgMailSession> mailSession =
2460 // do_GetService(NS_MSGMAILSESSION_CONTRACTID);
2461 // if (mailSession && aFolder)
2462 // mailSession->IsFolderOpenInWindow(m_virtualFolder, &isOpen);
2463 // we can't remove headers that no longer match - but we might add headers
2464 // that newly match, someday.
2465 // if (!isOpen)
2466 totalDelta = (oldMatch) ? -1 : 1;
2467 }
2468 bool msgHdrIsRead;
2469 aHdrChanged->GetIsRead(&msgHdrIsRead);
2470 if (oldMatch == newMatch) // read flag changed state
2471 unreadDelta = (msgHdrIsRead) ? -1 : 1;
2472 else if (oldMatch) // else header should removed
2473 unreadDelta = (aOldFlags & nsMsgMessageFlags::Read) ? 0 : -1;
2474 else // header should be added
2475 unreadDelta = (aNewFlags & nsMsgMessageFlags::Read) ? 0 : 1;
2476 if (unreadDelta) dbFolderInfo->ChangeNumUnreadMessages(unreadDelta);
2477 if (totalDelta) dbFolderInfo->ChangeNumMessages(totalDelta);
2478 if (unreadDelta == -1 && aOldFlags & nsMsgMessageFlags::New)
2479 DecrementNewMsgCount();
2480
2481 if (totalDelta) {
2482 nsCString searchUri;
2483 m_virtualFolder->GetURI(searchUri);
2484 msgDB->UpdateHdrInCache(searchUri, aHdrChanged, totalDelta == 1);
2485 }
2486
2487 PostUpdateEvent(m_virtualFolder, virtDatabase);
2488 } else if (oldMatch && (aOldFlags & nsMsgMessageFlags::New) &&
2489 !(aNewFlags & nsMsgMessageFlags::New))
2490 DecrementNewMsgCount();
2491
2492 return rv;
2493 }
2494
OnHdrDeleted(nsIMsgDBHdr * aHdrDeleted,nsMsgKey aParentKey,int32_t aFlags,nsIDBChangeListener * aInstigator)2495 NS_IMETHODIMP VirtualFolderChangeListener::OnHdrDeleted(
2496 nsIMsgDBHdr* aHdrDeleted, nsMsgKey aParentKey, int32_t aFlags,
2497 nsIDBChangeListener* aInstigator) {
2498 nsCOMPtr<nsIMsgDatabase> msgDB;
2499
2500 nsresult rv = m_folderWatching->GetMsgDatabase(getter_AddRefs(msgDB));
2501 NS_ENSURE_SUCCESS(rv, rv);
2502 bool match = false;
2503 m_searchSession->AddScopeTerm(nsMsgSearchScope::offlineMail,
2504 m_folderWatching);
2505 // Since the notifier went to the trouble of passing in the msg flags,
2506 // we should use them when doing the match.
2507 uint32_t msgFlags;
2508 aHdrDeleted->GetFlags(&msgFlags);
2509 aHdrDeleted->SetFlags(aFlags);
2510 rv = m_searchSession->MatchHdr(aHdrDeleted, msgDB, &match);
2511 aHdrDeleted->SetFlags(msgFlags);
2512 m_searchSession->ClearScopes();
2513 if (match) {
2514 nsCOMPtr<nsIMsgDatabase> virtDatabase;
2515 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
2516
2517 rv = m_virtualFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo),
2518 getter_AddRefs(virtDatabase));
2519 NS_ENSURE_SUCCESS(rv, rv);
2520 bool msgHdrIsRead;
2521 aHdrDeleted->GetIsRead(&msgHdrIsRead);
2522 if (!msgHdrIsRead) dbFolderInfo->ChangeNumUnreadMessages(-1);
2523 dbFolderInfo->ChangeNumMessages(-1);
2524 if (aFlags & nsMsgMessageFlags::New) {
2525 int32_t numNewMessages;
2526 m_virtualFolder->GetNumNewMessages(false, &numNewMessages);
2527 m_virtualFolder->SetNumNewMessages(numNewMessages - 1);
2528 if (numNewMessages == 1) m_virtualFolder->SetHasNewMessages(false);
2529 }
2530
2531 nsCString searchUri;
2532 m_virtualFolder->GetURI(searchUri);
2533 msgDB->UpdateHdrInCache(searchUri, aHdrDeleted, false);
2534
2535 PostUpdateEvent(m_virtualFolder, virtDatabase);
2536 }
2537 return rv;
2538 }
2539
OnHdrAdded(nsIMsgDBHdr * aNewHdr,nsMsgKey aParentKey,int32_t aFlags,nsIDBChangeListener * aInstigator)2540 NS_IMETHODIMP VirtualFolderChangeListener::OnHdrAdded(
2541 nsIMsgDBHdr* aNewHdr, nsMsgKey aParentKey, int32_t aFlags,
2542 nsIDBChangeListener* aInstigator) {
2543 nsCOMPtr<nsIMsgDatabase> msgDB;
2544
2545 nsresult rv = m_folderWatching->GetMsgDatabase(getter_AddRefs(msgDB));
2546 NS_ENSURE_SUCCESS(rv, rv);
2547 bool match = false;
2548 if (!m_searchSession) return NS_ERROR_NULL_POINTER;
2549
2550 m_searchSession->AddScopeTerm(nsMsgSearchScope::offlineMail,
2551 m_folderWatching);
2552 rv = m_searchSession->MatchHdr(aNewHdr, msgDB, &match);
2553 m_searchSession->ClearScopes();
2554 if (match) {
2555 nsCOMPtr<nsIMsgDatabase> virtDatabase;
2556 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
2557
2558 rv = m_virtualFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo),
2559 getter_AddRefs(virtDatabase));
2560 NS_ENSURE_SUCCESS(rv, rv);
2561 bool msgHdrIsRead;
2562 uint32_t msgFlags;
2563 aNewHdr->GetIsRead(&msgHdrIsRead);
2564 aNewHdr->GetFlags(&msgFlags);
2565 if (!msgHdrIsRead) dbFolderInfo->ChangeNumUnreadMessages(1);
2566 if (msgFlags & nsMsgMessageFlags::New) {
2567 int32_t numNewMessages;
2568 m_virtualFolder->GetNumNewMessages(false, &numNewMessages);
2569 m_virtualFolder->SetHasNewMessages(true);
2570 m_virtualFolder->SetNumNewMessages(numNewMessages + 1);
2571 }
2572 nsCString searchUri;
2573 m_virtualFolder->GetURI(searchUri);
2574 msgDB->UpdateHdrInCache(searchUri, aNewHdr, true);
2575 dbFolderInfo->ChangeNumMessages(1);
2576 PostUpdateEvent(m_virtualFolder, virtDatabase);
2577 }
2578 return rv;
2579 }
2580
OnParentChanged(nsMsgKey aKeyChanged,nsMsgKey oldParent,nsMsgKey newParent,nsIDBChangeListener * aInstigator)2581 NS_IMETHODIMP VirtualFolderChangeListener::OnParentChanged(
2582 nsMsgKey aKeyChanged, nsMsgKey oldParent, nsMsgKey newParent,
2583 nsIDBChangeListener* aInstigator) {
2584 return NS_OK;
2585 }
2586
OnAnnouncerGoingAway(nsIDBChangeAnnouncer * instigator)2587 NS_IMETHODIMP VirtualFolderChangeListener::OnAnnouncerGoingAway(
2588 nsIDBChangeAnnouncer* instigator) {
2589 nsCOMPtr<nsIMsgDatabase> msgDB = do_QueryInterface(instigator);
2590 if (msgDB) msgDB->RemoveListener(this);
2591 return NS_OK;
2592 }
2593
2594 NS_IMETHODIMP
OnEvent(nsIMsgDatabase * aDB,const char * aEvent)2595 VirtualFolderChangeListener::OnEvent(nsIMsgDatabase* aDB, const char* aEvent) {
2596 return NS_OK;
2597 }
2598
OnReadChanged(nsIDBChangeListener * aInstigator)2599 NS_IMETHODIMP VirtualFolderChangeListener::OnReadChanged(
2600 nsIDBChangeListener* aInstigator) {
2601 return NS_OK;
2602 }
2603
OnJunkScoreChanged(nsIDBChangeListener * aInstigator)2604 NS_IMETHODIMP VirtualFolderChangeListener::OnJunkScoreChanged(
2605 nsIDBChangeListener* aInstigator) {
2606 return NS_OK;
2607 }
2608
PostUpdateEvent(nsIMsgFolder * virtualFolder,nsIMsgDatabase * virtDatabase)2609 nsresult VirtualFolderChangeListener::PostUpdateEvent(
2610 nsIMsgFolder* virtualFolder, nsIMsgDatabase* virtDatabase) {
2611 if (m_batchingEvents) return NS_OK;
2612 m_batchingEvents = true;
2613 nsCOMPtr<nsIRunnable> event =
2614 new VFChangeListenerEvent(this, virtualFolder, virtDatabase);
2615 return NS_DispatchToCurrentThread(event);
2616 }
2617
ProcessUpdateEvent(nsIMsgFolder * virtFolder,nsIMsgDatabase * virtDB)2618 void VirtualFolderChangeListener::ProcessUpdateEvent(nsIMsgFolder* virtFolder,
2619 nsIMsgDatabase* virtDB) {
2620 m_batchingEvents = false;
2621 virtFolder->UpdateSummaryTotals(true); // force update from db.
2622 virtDB->Commit(nsMsgDBCommitType::kLargeCommit);
2623 }
2624
GetVirtualFoldersFile(nsCOMPtr<nsIFile> & aFile)2625 nsresult nsMsgAccountManager::GetVirtualFoldersFile(nsCOMPtr<nsIFile>& aFile) {
2626 nsCOMPtr<nsIFile> profileDir;
2627 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
2628 getter_AddRefs(profileDir));
2629 NS_ENSURE_SUCCESS(rv, rv);
2630
2631 rv = profileDir->AppendNative(nsDependentCString("virtualFolders.dat"));
2632 if (NS_SUCCEEDED(rv)) aFile = profileDir;
2633 return rv;
2634 }
2635
LoadVirtualFolders()2636 NS_IMETHODIMP nsMsgAccountManager::LoadVirtualFolders() {
2637 nsCOMPtr<nsIFile> file;
2638 GetVirtualFoldersFile(file);
2639 if (!file) return NS_ERROR_FAILURE;
2640
2641 if (m_virtualFoldersLoaded) return NS_OK;
2642
2643 m_loadingVirtualFolders = true;
2644
2645 nsresult rv;
2646 nsCOMPtr<nsIMsgDBService> msgDBService =
2647 do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
2648 if (msgDBService) {
2649 NS_ENSURE_SUCCESS(rv, rv);
2650 nsCOMPtr<nsIFileInputStream> fileStream =
2651 do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv);
2652 NS_ENSURE_SUCCESS(rv, rv);
2653
2654 rv = fileStream->Init(file, PR_RDONLY, 0664, false);
2655 nsCOMPtr<nsILineInputStream> lineInputStream(do_QueryInterface(fileStream));
2656
2657 bool isMore = true;
2658 nsAutoCString buffer;
2659 int32_t version = -1;
2660 nsCOMPtr<nsIMsgFolder> virtualFolder;
2661 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
2662
2663 while (isMore && NS_SUCCEEDED(lineInputStream->ReadLine(buffer, &isMore))) {
2664 if (!buffer.IsEmpty()) {
2665 if (version == -1) {
2666 buffer.Cut(0, 8);
2667 nsresult irv;
2668 version = buffer.ToInteger(&irv);
2669 continue;
2670 }
2671 if (StringBeginsWith(buffer, "uri="_ns)) {
2672 buffer.Cut(0, 4);
2673 dbFolderInfo = nullptr;
2674
2675 rv = GetOrCreateFolder(buffer, getter_AddRefs(virtualFolder));
2676 NS_ENSURE_SUCCESS(rv, rv);
2677
2678 virtualFolder->SetFlag(nsMsgFolderFlags::Virtual);
2679
2680 nsCOMPtr<nsIMsgFolder> grandParent;
2681 nsCOMPtr<nsIMsgFolder> oldParent;
2682 nsCOMPtr<nsIMsgFolder> parentFolder;
2683 bool isServer;
2684 // This loop handles creating virtual folders without an existing
2685 // parent.
2686 do {
2687 // need to add the folder as a sub-folder of its parent.
2688 int32_t lastSlash = buffer.RFindChar('/');
2689 if (lastSlash == kNotFound) break;
2690 nsDependentCSubstring parentUri(buffer, 0, lastSlash);
2691 // hold a reference so it won't get deleted before it's parented.
2692 oldParent = parentFolder;
2693
2694 rv = GetOrCreateFolder(parentUri, getter_AddRefs(parentFolder));
2695 NS_ENSURE_SUCCESS(rv, rv);
2696
2697 nsAutoString currentFolderNameStr;
2698 nsAutoCString currentFolderNameCStr;
2699 MsgUnescapeString(
2700 nsCString(Substring(buffer, lastSlash + 1, buffer.Length())), 0,
2701 currentFolderNameCStr);
2702 CopyUTF8toUTF16(currentFolderNameCStr, currentFolderNameStr);
2703 nsCOMPtr<nsIMsgFolder> childFolder;
2704 nsCOMPtr<nsIMsgDatabase> db;
2705 // force db to get created.
2706 // XXX TODO: is this SetParent() right? Won't it screw up if virtual
2707 // folder is nested >2 deep? Leave for now, but revisit when getting
2708 // rid of dangling folders (BenC).
2709 virtualFolder->SetParent(parentFolder);
2710 rv = virtualFolder->GetMsgDatabase(getter_AddRefs(db));
2711 if (rv == NS_MSG_ERROR_FOLDER_SUMMARY_MISSING)
2712 msgDBService->CreateNewDB(virtualFolder, getter_AddRefs(db));
2713 if (db)
2714 rv = db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
2715 else
2716 break;
2717
2718 parentFolder->AddSubfolder(currentFolderNameStr,
2719 getter_AddRefs(childFolder));
2720 if (childFolder) parentFolder->NotifyItemAdded(childFolder);
2721 // here we make sure if our parent is rooted - if not, we're
2722 // going to loop and add our parent as a child of its grandparent
2723 // and repeat until we get to the server, or a folder that
2724 // has its parent set.
2725 parentFolder->GetParent(getter_AddRefs(grandParent));
2726 parentFolder->GetIsServer(&isServer);
2727 buffer.SetLength(lastSlash);
2728 } while (!grandParent && !isServer);
2729 } else if (dbFolderInfo && StringBeginsWith(buffer, "scope="_ns)) {
2730 buffer.Cut(0, 6);
2731 // if this is a cross folder virtual folder, we have a list of folders
2732 // uris, and we have to add a pending listener for each of them.
2733 if (!buffer.IsEmpty()) {
2734 ParseAndVerifyVirtualFolderScope(buffer);
2735 dbFolderInfo->SetCharProperty(kSearchFolderUriProp, buffer);
2736 AddVFListenersForVF(virtualFolder, buffer, msgDBService);
2737 }
2738 } else if (dbFolderInfo && StringBeginsWith(buffer, "terms="_ns)) {
2739 buffer.Cut(0, 6);
2740 dbFolderInfo->SetCharProperty("searchStr", buffer);
2741 } else if (dbFolderInfo &&
2742 StringBeginsWith(buffer, "searchOnline="_ns)) {
2743 buffer.Cut(0, 13);
2744 dbFolderInfo->SetBooleanProperty("searchOnline",
2745 buffer.EqualsLiteral("true"));
2746 } else if (dbFolderInfo &&
2747 Substring(buffer, 0, SEARCH_FOLDER_FLAG_LEN + 1)
2748 .Equals(SEARCH_FOLDER_FLAG "=")) {
2749 buffer.Cut(0, SEARCH_FOLDER_FLAG_LEN + 1);
2750 dbFolderInfo->SetCharProperty(SEARCH_FOLDER_FLAG, buffer);
2751 }
2752 }
2753 }
2754 }
2755
2756 m_loadingVirtualFolders = false;
2757 m_virtualFoldersLoaded = true;
2758 return rv;
2759 }
2760
SaveVirtualFolders()2761 NS_IMETHODIMP nsMsgAccountManager::SaveVirtualFolders() {
2762 if (!m_virtualFoldersLoaded) return NS_OK;
2763
2764 nsCOMPtr<nsIFile> file;
2765 GetVirtualFoldersFile(file);
2766
2767 // Open a buffered, safe output stream
2768 nsCOMPtr<nsIOutputStream> outStream;
2769 nsresult rv = MsgNewSafeBufferedFileOutputStream(
2770 getter_AddRefs(outStream), file, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE,
2771 0664);
2772 NS_ENSURE_SUCCESS(rv, rv);
2773
2774 WriteLineToOutputStream("version=", "1", outStream);
2775 for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
2776 nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
2777 if (server) {
2778 nsCOMPtr<nsIMsgFolder> rootFolder;
2779 server->GetRootFolder(getter_AddRefs(rootFolder));
2780 if (rootFolder) {
2781 nsTArray<RefPtr<nsIMsgFolder>> virtualFolders;
2782 nsresult rv = rootFolder->GetFoldersWithFlags(nsMsgFolderFlags::Virtual,
2783 virtualFolders);
2784 if (NS_FAILED(rv)) {
2785 continue;
2786 }
2787 for (auto msgFolder : virtualFolders) {
2788 nsCOMPtr<nsIMsgDatabase> db;
2789 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
2790 rv = msgFolder->GetDBFolderInfoAndDB(
2791 getter_AddRefs(dbFolderInfo),
2792 getter_AddRefs(db)); // force db to get created.
2793 if (dbFolderInfo) {
2794 nsCString srchFolderUri;
2795 nsCString searchTerms;
2796 nsCString regexScope;
2797 nsCString vfFolderFlag;
2798 bool searchOnline = false;
2799 dbFolderInfo->GetBooleanProperty("searchOnline", false,
2800 &searchOnline);
2801 dbFolderInfo->GetCharProperty(kSearchFolderUriProp, srchFolderUri);
2802 dbFolderInfo->GetCharProperty("searchStr", searchTerms);
2803 // logically searchFolderFlag is an int, but since we want to
2804 // write out a string, get it as a string.
2805 dbFolderInfo->GetCharProperty(SEARCH_FOLDER_FLAG, vfFolderFlag);
2806 nsCString uri;
2807 msgFolder->GetURI(uri);
2808 if (!srchFolderUri.IsEmpty() && !searchTerms.IsEmpty()) {
2809 WriteLineToOutputStream("uri=", uri.get(), outStream);
2810 if (!vfFolderFlag.IsEmpty())
2811 WriteLineToOutputStream(SEARCH_FOLDER_FLAG "=",
2812 vfFolderFlag.get(), outStream);
2813 WriteLineToOutputStream("scope=", srchFolderUri.get(), outStream);
2814 WriteLineToOutputStream("terms=", searchTerms.get(), outStream);
2815 WriteLineToOutputStream(
2816 "searchOnline=", searchOnline ? "true" : "false", outStream);
2817 }
2818 }
2819 }
2820 }
2821 }
2822 }
2823
2824 nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outStream, &rv);
2825 NS_ASSERTION(safeStream, "expected a safe output stream!");
2826 if (safeStream) {
2827 rv = safeStream->Finish();
2828 if (NS_FAILED(rv)) {
2829 NS_WARNING("failed to save personal dictionary file! possible data loss");
2830 }
2831 }
2832 return rv;
2833 }
2834
WriteLineToOutputStream(const char * prefix,const char * line,nsIOutputStream * outputStream)2835 nsresult nsMsgAccountManager::WriteLineToOutputStream(
2836 const char* prefix, const char* line, nsIOutputStream* outputStream) {
2837 uint32_t writeCount;
2838 outputStream->Write(prefix, strlen(prefix), &writeCount);
2839 outputStream->Write(line, strlen(line), &writeCount);
2840 outputStream->Write("\n", 1, &writeCount);
2841 return NS_OK;
2842 }
2843
2844 /**
2845 * Parse the '|' separated folder uri string into individual folders, verify
2846 * that the folders are real. If we were to add things like wildcards, we
2847 * could implement the expansion into real folders here.
2848 *
2849 * @param buffer On input, list of folder uri's, on output, verified list.
2850 */
ParseAndVerifyVirtualFolderScope(nsCString & buffer)2851 void nsMsgAccountManager::ParseAndVerifyVirtualFolderScope(nsCString& buffer) {
2852 nsCString verifiedFolders;
2853 nsTArray<nsCString> folderUris;
2854 ParseString(buffer, '|', folderUris);
2855 nsCOMPtr<nsIMsgIncomingServer> server;
2856 nsCOMPtr<nsIMsgFolder> parent;
2857
2858 for (uint32_t i = 0; i < folderUris.Length(); i++) {
2859 nsCOMPtr<nsIMsgFolder> realFolder;
2860 nsresult rv = GetOrCreateFolder(folderUris[i], getter_AddRefs(realFolder));
2861 if (!NS_SUCCEEDED(rv)) {
2862 continue;
2863 }
2864 realFolder->GetParent(getter_AddRefs(parent));
2865 if (!parent) continue;
2866 realFolder->GetServer(getter_AddRefs(server));
2867 if (!server) continue;
2868 if (!verifiedFolders.IsEmpty()) verifiedFolders.Append('|');
2869 verifiedFolders.Append(folderUris[i]);
2870 }
2871 buffer.Assign(verifiedFolders);
2872 }
2873
2874 // This conveniently works to add a single folder as well.
AddVFListenersForVF(nsIMsgFolder * virtualFolder,const nsCString & srchFolderUris,nsIMsgDBService * msgDBService)2875 nsresult nsMsgAccountManager::AddVFListenersForVF(
2876 nsIMsgFolder* virtualFolder, const nsCString& srchFolderUris,
2877 nsIMsgDBService* msgDBService) {
2878 nsTArray<nsCString> folderUris;
2879 ParseString(srchFolderUris, '|', folderUris);
2880
2881 for (uint32_t i = 0; i < folderUris.Length(); i++) {
2882 nsCOMPtr<nsIMsgFolder> realFolder;
2883 nsresult rv = GetOrCreateFolder(folderUris[i], getter_AddRefs(realFolder));
2884 NS_ENSURE_SUCCESS(rv, rv);
2885 RefPtr<VirtualFolderChangeListener> dbListener =
2886 new VirtualFolderChangeListener();
2887 NS_ENSURE_TRUE(dbListener, NS_ERROR_OUT_OF_MEMORY);
2888 dbListener->m_virtualFolder = virtualFolder;
2889 dbListener->m_folderWatching = realFolder;
2890 if (NS_FAILED(dbListener->Init())) {
2891 dbListener = nullptr;
2892 continue;
2893 }
2894 m_virtualFolderListeners.AppendElement(dbListener);
2895 msgDBService->RegisterPendingListener(realFolder, dbListener);
2896 }
2897 return NS_OK;
2898 }
2899
2900 // This is called if a folder that's part of the scope of a saved search
2901 // has gone away.
RemoveVFListenerForVF(nsIMsgFolder * virtualFolder,nsIMsgFolder * folder)2902 nsresult nsMsgAccountManager::RemoveVFListenerForVF(nsIMsgFolder* virtualFolder,
2903 nsIMsgFolder* folder) {
2904 nsresult rv;
2905 nsCOMPtr<nsIMsgDBService> msgDBService(
2906 do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv));
2907 NS_ENSURE_SUCCESS(rv, rv);
2908
2909 nsTObserverArray<RefPtr<VirtualFolderChangeListener>>::ForwardIterator iter(
2910 m_virtualFolderListeners);
2911 RefPtr<VirtualFolderChangeListener> listener;
2912
2913 while (iter.HasMore()) {
2914 listener = iter.GetNext();
2915 if (listener->m_folderWatching == folder &&
2916 listener->m_virtualFolder == virtualFolder) {
2917 msgDBService->UnregisterPendingListener(listener);
2918 m_virtualFolderListeners.RemoveElement(listener);
2919 break;
2920 }
2921 }
2922 return NS_OK;
2923 }
2924
GetAllFolders(nsTArray<RefPtr<nsIMsgFolder>> & aAllFolders)2925 NS_IMETHODIMP nsMsgAccountManager::GetAllFolders(
2926 nsTArray<RefPtr<nsIMsgFolder>>& aAllFolders) {
2927 aAllFolders.Clear();
2928 nsTArray<RefPtr<nsIMsgIncomingServer>> allServers;
2929 nsresult rv = GetAllServers(allServers);
2930 NS_ENSURE_SUCCESS(rv, rv);
2931
2932 for (auto server : allServers) {
2933 if (server) {
2934 nsCOMPtr<nsIMsgFolder> rootFolder;
2935 server->GetRootFolder(getter_AddRefs(rootFolder));
2936 if (rootFolder) {
2937 nsTArray<RefPtr<nsIMsgFolder>> descendents;
2938 rootFolder->GetDescendants(descendents);
2939 aAllFolders.AppendElements(descendents);
2940 }
2941 }
2942 }
2943 return NS_OK;
2944 }
2945
OnItemAdded(nsIMsgFolder * parentItem,nsISupports * item)2946 NS_IMETHODIMP nsMsgAccountManager::OnItemAdded(nsIMsgFolder* parentItem,
2947 nsISupports* item) {
2948 nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(item);
2949 // just kick out with a success code if the item in question is not a folder
2950 if (!folder) return NS_OK;
2951
2952 uint32_t folderFlags;
2953 folder->GetFlags(&folderFlags);
2954 bool addToSmartFolders = false;
2955 folder->IsSpecialFolder(
2956 nsMsgFolderFlags::Inbox | nsMsgFolderFlags::Templates |
2957 nsMsgFolderFlags::Trash | nsMsgFolderFlags::Drafts,
2958 false, &addToSmartFolders);
2959 // For Sent/Archives/Trash, we treat sub-folders of those folders as
2960 // "special", and want to add them the smart folders search scope.
2961 // So we check if this is a sub-folder of one of those special folders
2962 // and set the corresponding folderFlag if so.
2963 if (!addToSmartFolders) {
2964 bool isSpecial = false;
2965 folder->IsSpecialFolder(nsMsgFolderFlags::SentMail, true, &isSpecial);
2966 if (isSpecial) {
2967 addToSmartFolders = true;
2968 folderFlags |= nsMsgFolderFlags::SentMail;
2969 }
2970 folder->IsSpecialFolder(nsMsgFolderFlags::Archive, true, &isSpecial);
2971 if (isSpecial) {
2972 addToSmartFolders = true;
2973 folderFlags |= nsMsgFolderFlags::Archive;
2974 }
2975 folder->IsSpecialFolder(nsMsgFolderFlags::Trash, true, &isSpecial);
2976 if (isSpecial) {
2977 addToSmartFolders = true;
2978 folderFlags |= nsMsgFolderFlags::Trash;
2979 }
2980 }
2981 nsresult rv = NS_OK;
2982 // if this is a special folder, check if we have a saved search over
2983 // folders with this flag, and if so, add this folder to the scope.
2984 if (addToSmartFolders) {
2985 // quick way to enumerate the saved searches.
2986 nsTObserverArray<RefPtr<VirtualFolderChangeListener>>::ForwardIterator iter(
2987 m_virtualFolderListeners);
2988 RefPtr<VirtualFolderChangeListener> listener;
2989
2990 while (iter.HasMore()) {
2991 listener = iter.GetNext();
2992 nsCOMPtr<nsIMsgDatabase> db;
2993 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
2994 listener->m_virtualFolder->GetDBFolderInfoAndDB(
2995 getter_AddRefs(dbFolderInfo), getter_AddRefs(db));
2996 if (dbFolderInfo) {
2997 uint32_t vfFolderFlag;
2998 dbFolderInfo->GetUint32Property("searchFolderFlag", 0, &vfFolderFlag);
2999 // found a saved search over folders w/ the same flag as the new folder.
3000 if (vfFolderFlag & folderFlags) {
3001 nsCString searchURI;
3002 dbFolderInfo->GetCharProperty(kSearchFolderUriProp, searchURI);
3003
3004 // "normalize" searchURI so we can search for |folderURI|.
3005 if (!searchURI.IsEmpty()) {
3006 searchURI.Insert('|', 0);
3007 searchURI.Append('|');
3008 }
3009 nsCString folderURI;
3010 folder->GetURI(folderURI);
3011
3012 int32_t index = searchURI.Find(folderURI);
3013 if (index == kNotFound) {
3014 searchURI.Cut(0, 1);
3015 searchURI.Append(folderURI);
3016 dbFolderInfo->SetCharProperty(kSearchFolderUriProp, searchURI);
3017 break;
3018 }
3019 // New sent or archive folder, need to add sub-folders to smart
3020 // folder.
3021 if (vfFolderFlag &
3022 (nsMsgFolderFlags::Archive | nsMsgFolderFlags::SentMail)) {
3023 nsTArray<RefPtr<nsIMsgFolder>> allDescendants;
3024 rv = folder->GetDescendants(allDescendants);
3025 NS_ENSURE_SUCCESS(rv, rv);
3026
3027 nsCOMPtr<nsIMsgFolder> parent;
3028 for (auto subFolder : allDescendants) {
3029 subFolder->GetParent(getter_AddRefs(parent));
3030 OnItemAdded(parent, subFolder);
3031 }
3032 }
3033 }
3034 }
3035 }
3036 }
3037 // need to make sure this isn't happening during loading of virtualfolders.dat
3038 if (folderFlags & nsMsgFolderFlags::Virtual && !m_loadingVirtualFolders) {
3039 // When a new virtual folder is added, need to create a db Listener for it.
3040 nsCOMPtr<nsIMsgDBService> msgDBService =
3041 do_GetService(NS_MSGDB_SERVICE_CONTRACTID, &rv);
3042 if (msgDBService) {
3043 nsCOMPtr<nsIMsgDatabase> virtDatabase;
3044 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
3045 rv = folder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo),
3046 getter_AddRefs(virtDatabase));
3047 NS_ENSURE_SUCCESS(rv, rv);
3048 nsCString srchFolderUri;
3049 dbFolderInfo->GetCharProperty(kSearchFolderUriProp, srchFolderUri);
3050 AddVFListenersForVF(folder, srchFolderUri, msgDBService);
3051 }
3052 rv = SaveVirtualFolders();
3053 }
3054 return rv;
3055 }
3056
OnItemRemoved(nsIMsgFolder * parentItem,nsISupports * item)3057 NS_IMETHODIMP nsMsgAccountManager::OnItemRemoved(nsIMsgFolder* parentItem,
3058 nsISupports* item) {
3059 nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(item);
3060 // just kick out with a success code if the item in question is not a folder
3061 if (!folder) return NS_OK;
3062 nsresult rv = NS_OK;
3063 uint32_t folderFlags;
3064 folder->GetFlags(&folderFlags);
3065 // if we removed a VF, flush VF list to disk.
3066 if (folderFlags & nsMsgFolderFlags::Virtual) {
3067 rv = SaveVirtualFolders();
3068 // clear flags on deleted folder if it's a virtual folder, so that creating
3069 // a new folder with the same name doesn't cause confusion.
3070 folder->SetFlags(0);
3071 return rv;
3072 }
3073 // need to update the saved searches to check for a few things:
3074 // 1. Folder removed was in the scope of a saved search - if so, remove the
3075 // uri from the scope of the saved search.
3076 // 2. If the scope is now empty, remove the saved search.
3077
3078 // build a "normalized" uri that we can do a find on.
3079 nsCString removedFolderURI;
3080 folder->GetURI(removedFolderURI);
3081 removedFolderURI.Insert('|', 0);
3082 removedFolderURI.Append('|');
3083
3084 // Enumerate the saved searches.
3085 nsTObserverArray<RefPtr<VirtualFolderChangeListener>>::ForwardIterator iter(
3086 m_virtualFolderListeners);
3087 RefPtr<VirtualFolderChangeListener> listener;
3088
3089 while (iter.HasMore()) {
3090 listener = iter.GetNext();
3091 nsCOMPtr<nsIMsgDatabase> db;
3092 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
3093 nsCOMPtr<nsIMsgFolder> savedSearch = listener->m_virtualFolder;
3094 savedSearch->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo),
3095 getter_AddRefs(db));
3096 if (dbFolderInfo) {
3097 nsCString searchURI;
3098 dbFolderInfo->GetCharProperty(kSearchFolderUriProp, searchURI);
3099 // "normalize" searchURI so we can search for |folderURI|.
3100 searchURI.Insert('|', 0);
3101 searchURI.Append('|');
3102 int32_t index = searchURI.Find(removedFolderURI);
3103 if (index != kNotFound) {
3104 RemoveVFListenerForVF(savedSearch, folder);
3105
3106 // remove |folderURI
3107 searchURI.Cut(index, removedFolderURI.Length() - 1);
3108 // remove last '|' we added
3109 searchURI.SetLength(searchURI.Length() - 1);
3110
3111 // if saved search is empty now, delete it.
3112 if (searchURI.IsEmpty()) {
3113 db = nullptr;
3114 dbFolderInfo = nullptr;
3115
3116 nsCOMPtr<nsIMsgFolder> parent;
3117 rv = savedSearch->GetParent(getter_AddRefs(parent));
3118 NS_ENSURE_SUCCESS(rv, rv);
3119
3120 if (!parent) continue;
3121 parent->PropagateDelete(savedSearch, true, nullptr);
3122 } else {
3123 // remove leading '|' we added (or one after |folderURI, if first URI)
3124 searchURI.Cut(0, 1);
3125 dbFolderInfo->SetCharProperty(kSearchFolderUriProp, searchURI);
3126 }
3127 }
3128 }
3129 }
3130
3131 return rv;
3132 }
3133
OnItemPropertyChanged(nsIMsgFolder * item,const nsACString & property,const nsACString & oldValue,const nsACString & newValue)3134 NS_IMETHODIMP nsMsgAccountManager::OnItemPropertyChanged(
3135 nsIMsgFolder* item, const nsACString& property, const nsACString& oldValue,
3136 const nsACString& newValue) {
3137 return NS_ERROR_NOT_IMPLEMENTED;
3138 }
3139
3140 NS_IMETHODIMP
OnItemIntPropertyChanged(nsIMsgFolder * aFolder,const nsACString & aProperty,int64_t oldValue,int64_t newValue)3141 nsMsgAccountManager::OnItemIntPropertyChanged(nsIMsgFolder* aFolder,
3142 const nsACString& aProperty,
3143 int64_t oldValue,
3144 int64_t newValue) {
3145 if (aProperty.Equals(kFolderFlag)) {
3146 uint32_t smartFlagsChanged =
3147 (oldValue ^ newValue) &
3148 (nsMsgFolderFlags::SpecialUse & ~nsMsgFolderFlags::Queue);
3149 if (smartFlagsChanged) {
3150 if (smartFlagsChanged & newValue) {
3151 // if the smart folder flag was set, calling OnItemAdded will
3152 // do the right thing.
3153 nsCOMPtr<nsIMsgFolder> parent;
3154 aFolder->GetParent(getter_AddRefs(parent));
3155 return OnItemAdded(parent, aFolder);
3156 }
3157 RemoveFolderFromSmartFolder(aFolder, smartFlagsChanged);
3158 // sent|archive flag removed, remove sub-folders from smart folder.
3159 if (smartFlagsChanged &
3160 (nsMsgFolderFlags::Archive | nsMsgFolderFlags::SentMail)) {
3161 nsTArray<RefPtr<nsIMsgFolder>> allDescendants;
3162 nsresult rv = aFolder->GetDescendants(allDescendants);
3163 NS_ENSURE_SUCCESS(rv, rv);
3164 for (auto subFolder : allDescendants) {
3165 RemoveFolderFromSmartFolder(subFolder, smartFlagsChanged);
3166 }
3167 }
3168 }
3169 }
3170 return NS_OK;
3171 }
3172
RemoveFolderFromSmartFolder(nsIMsgFolder * aFolder,uint32_t flagsChanged)3173 nsresult nsMsgAccountManager::RemoveFolderFromSmartFolder(
3174 nsIMsgFolder* aFolder, uint32_t flagsChanged) {
3175 nsCString removedFolderURI;
3176 aFolder->GetURI(removedFolderURI);
3177 removedFolderURI.Insert('|', 0);
3178 removedFolderURI.Append('|');
3179 uint32_t flags;
3180 aFolder->GetFlags(&flags);
3181 NS_ASSERTION(!(flags & flagsChanged), "smart folder flag should not be set");
3182 // Flag was removed. Look for smart folder based on that flag,
3183 // and remove this folder from its scope.
3184 nsTObserverArray<RefPtr<VirtualFolderChangeListener>>::ForwardIterator iter(
3185 m_virtualFolderListeners);
3186 RefPtr<VirtualFolderChangeListener> listener;
3187
3188 while (iter.HasMore()) {
3189 listener = iter.GetNext();
3190 nsCOMPtr<nsIMsgDatabase> db;
3191 nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
3192 listener->m_virtualFolder->GetDBFolderInfoAndDB(
3193 getter_AddRefs(dbFolderInfo), getter_AddRefs(db));
3194 if (dbFolderInfo) {
3195 uint32_t vfFolderFlag;
3196 dbFolderInfo->GetUint32Property("searchFolderFlag", 0, &vfFolderFlag);
3197 // found a smart folder over the removed flag
3198 if (vfFolderFlag & flagsChanged) {
3199 nsCString searchURI;
3200 dbFolderInfo->GetCharProperty(kSearchFolderUriProp, searchURI);
3201 // "normalize" searchURI so we can search for |folderURI|.
3202 searchURI.Insert('|', 0);
3203 searchURI.Append('|');
3204 int32_t index = searchURI.Find(removedFolderURI);
3205 if (index != kNotFound) {
3206 RemoveVFListenerForVF(listener->m_virtualFolder, aFolder);
3207
3208 // remove |folderURI
3209 searchURI.Cut(index, removedFolderURI.Length() - 1);
3210 // remove last '|' we added
3211 searchURI.SetLength(searchURI.Length() - 1);
3212
3213 // remove leading '|' we added (or one after |folderURI, if first URI)
3214 searchURI.Cut(0, 1);
3215 dbFolderInfo->SetCharProperty(kSearchFolderUriProp, searchURI);
3216 }
3217 }
3218 }
3219 }
3220 return NS_OK;
3221 }
3222
OnItemBoolPropertyChanged(nsIMsgFolder * item,const nsACString & property,bool oldValue,bool newValue)3223 NS_IMETHODIMP nsMsgAccountManager::OnItemBoolPropertyChanged(
3224 nsIMsgFolder* item, const nsACString& property, bool oldValue,
3225 bool newValue) {
3226 return NS_ERROR_NOT_IMPLEMENTED;
3227 }
3228
OnItemUnicharPropertyChanged(nsIMsgFolder * item,const nsACString & property,const nsAString & oldValue,const nsAString & newValue)3229 NS_IMETHODIMP nsMsgAccountManager::OnItemUnicharPropertyChanged(
3230 nsIMsgFolder* item, const nsACString& property, const nsAString& oldValue,
3231 const nsAString& newValue) {
3232 return NS_ERROR_NOT_IMPLEMENTED;
3233 }
3234
OnItemPropertyFlagChanged(nsIMsgDBHdr * item,const nsACString & property,uint32_t oldFlag,uint32_t newFlag)3235 NS_IMETHODIMP nsMsgAccountManager::OnItemPropertyFlagChanged(
3236 nsIMsgDBHdr* item, const nsACString& property, uint32_t oldFlag,
3237 uint32_t newFlag) {
3238 return NS_ERROR_NOT_IMPLEMENTED;
3239 }
3240
OnItemEvent(nsIMsgFolder * aFolder,const nsACString & aEvent)3241 NS_IMETHODIMP nsMsgAccountManager::OnItemEvent(nsIMsgFolder* aFolder,
3242 const nsACString& aEvent) {
3243 return NS_ERROR_NOT_IMPLEMENTED;
3244 }
3245
3246 NS_IMETHODIMP
FolderUriForPath(nsIFile * aLocalPath,nsACString & aMailboxUri)3247 nsMsgAccountManager::FolderUriForPath(nsIFile* aLocalPath,
3248 nsACString& aMailboxUri) {
3249 NS_ENSURE_ARG_POINTER(aLocalPath);
3250 bool equals;
3251 if (m_lastPathLookedUp &&
3252 NS_SUCCEEDED(aLocalPath->Equals(m_lastPathLookedUp, &equals)) && equals) {
3253 aMailboxUri = m_lastFolderURIForPath;
3254 return NS_OK;
3255 }
3256 nsTArray<RefPtr<nsIMsgFolder>> folders;
3257 nsresult rv = GetAllFolders(folders);
3258 NS_ENSURE_SUCCESS(rv, rv);
3259
3260 for (auto folder : folders) {
3261 nsCOMPtr<nsIFile> folderPath;
3262 rv = folder->GetFilePath(getter_AddRefs(folderPath));
3263 NS_ENSURE_SUCCESS(rv, rv);
3264
3265 // Check if we're equal
3266 rv = folderPath->Equals(aLocalPath, &equals);
3267 NS_ENSURE_SUCCESS(rv, rv);
3268
3269 if (equals) {
3270 rv = folder->GetURI(aMailboxUri);
3271 m_lastFolderURIForPath = aMailboxUri;
3272 aLocalPath->Clone(getter_AddRefs(m_lastPathLookedUp));
3273 return rv;
3274 }
3275 }
3276 return NS_ERROR_FAILURE;
3277 }
3278
3279 NS_IMETHODIMP
GetSortOrder(nsIMsgIncomingServer * aServer,int32_t * aSortOrder)3280 nsMsgAccountManager::GetSortOrder(nsIMsgIncomingServer* aServer,
3281 int32_t* aSortOrder) {
3282 NS_ENSURE_ARG_POINTER(aServer);
3283 NS_ENSURE_ARG_POINTER(aSortOrder);
3284
3285 // If the passed in server is the default, return its sort order as 0
3286 // regardless of its server sort order.
3287
3288 nsCOMPtr<nsIMsgAccount> defaultAccount;
3289 nsresult rv = GetDefaultAccount(getter_AddRefs(defaultAccount));
3290 if (NS_SUCCEEDED(rv) && defaultAccount) {
3291 nsCOMPtr<nsIMsgIncomingServer> defaultServer;
3292 rv = m_defaultAccount->GetIncomingServer(getter_AddRefs(defaultServer));
3293 if (NS_SUCCEEDED(rv) && (aServer == defaultServer)) {
3294 *aSortOrder = 0;
3295 return NS_OK;
3296 }
3297 // It is OK if there is no default account.
3298 }
3299
3300 // This function returns the sort order by querying the server object for its
3301 // sort order value and then incrementing it by the position of the server in
3302 // the accounts list. This ensures that even when several accounts have the
3303 // same sort order value, the returned value is not the same and keeps
3304 // their relative order in the account list when and unstable sort is run
3305 // on the returned sort order values.
3306 int32_t sortOrder;
3307 int32_t serverIndex;
3308
3309 rv = aServer->GetSortOrder(&sortOrder);
3310 if (NS_SUCCEEDED(rv)) rv = FindServerIndex(aServer, &serverIndex);
3311
3312 if (NS_FAILED(rv)) {
3313 *aSortOrder = 999999999;
3314 } else {
3315 *aSortOrder = sortOrder + serverIndex;
3316 }
3317
3318 return NS_OK;
3319 }
3320
3321 NS_IMETHODIMP
ReorderAccounts(const nsTArray<nsCString> & newAccounts)3322 nsMsgAccountManager::ReorderAccounts(const nsTArray<nsCString>& newAccounts) {
3323 nsTArray<nsCString> allNewAccounts = newAccounts.Clone();
3324
3325 // Add all hidden accounts to the list of new accounts.
3326 nsresult rv;
3327 for (auto account : m_accounts) {
3328 nsCString key;
3329 account->GetKey(key);
3330 nsCOMPtr<nsIMsgIncomingServer> server;
3331 rv = account->GetIncomingServer(getter_AddRefs(server));
3332 if (NS_SUCCEEDED(rv) && server) {
3333 bool hidden = false;
3334 rv = server->GetHidden(&hidden);
3335 if (NS_SUCCEEDED(rv) && hidden && !allNewAccounts.Contains(key)) {
3336 allNewAccounts.AppendElement(key);
3337 }
3338 }
3339 }
3340
3341 // Check that the new account list contains all the existing accounts,
3342 // just in a different order.
3343 if (allNewAccounts.Length() != m_accounts.Length())
3344 return NS_ERROR_INVALID_ARG;
3345
3346 for (uint32_t i = 0; i < m_accounts.Length(); i++) {
3347 nsCString accountKey;
3348 m_accounts[i]->GetKey(accountKey);
3349 if (!allNewAccounts.Contains(accountKey)) return NS_ERROR_INVALID_ARG;
3350 }
3351
3352 // In-place swap the elements in m_accounts to the order defined in
3353 // newAccounts.
3354 for (uint32_t i = 0; i < allNewAccounts.Length(); i++) {
3355 nsCString newKey = allNewAccounts[i];
3356 for (uint32_t j = i; j < m_accounts.Length(); j++) {
3357 nsCString oldKey;
3358 m_accounts[j]->GetKey(oldKey);
3359 if (newKey.Equals(oldKey)) {
3360 if (i != j) std::swap(m_accounts[i], m_accounts[j]);
3361 break;
3362 }
3363 }
3364 }
3365
3366 return OutputAccountsPref();
3367 }
3368