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