Source code
Revision control
Copy as Markdown
Other Tools
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
/**
* The account manager service - manages all accounts, servers, and identities
*/
#include "nsCOMPtr.h"
#include "nsISupports.h"
#include "nsIThread.h"
#include "nscore.h"
#include "mozilla/RefPtr.h"
#include "nsIComponentManager.h"
#include "nsMsgAccountManager.h"
#include "prmem.h"
#include "prcmon.h"
#include "prthread.h"
#include "plstr.h"
#include "nsString.h"
#include "nscore.h"
#include "prprf.h"
#include "nsIMsgFolderCache.h"
#include "nsMsgFolderCache.h"
#include "nsMsgUtils.h"
#include "nsMsgDBFolder.h"
#include "nsIFile.h"
#include "nsIURL.h"
#include "nsNetCID.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsIMsgOutgoingServerService.h"
#include "nsIMsgBiffManager.h"
#include "nsIMsgPurgeService.h"
#include "nsIObserverService.h"
#include "nsINoIncomingServer.h"
#include "nsIMsgMailSession.h"
#include "nsIDirectoryService.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsMailDirServiceDefs.h"
#include "nsMsgFolderFlags.h"
#include "nsIMsgFolderNotificationService.h"
#include "nsIImapIncomingServer.h"
#include "nsIImapUrl.h"
#include "nsIURIMutator.h"
#include "nsICategoryManager.h"
#include "nsISupportsPrimitives.h"
#include "nsIMsgFilterService.h"
#include "nsIMsgFilter.h"
#include "nsIMsgSearchSession.h"
#include "nsIMsgSearchTerm.h"
#include "nsIDBFolderInfo.h"
#include "nsIMsgHdr.h"
#include "nsILineInputStream.h"
#include "nsThreadUtils.h"
#include "nsNetUtil.h"
#include "nsIStringBundle.h"
#include "nsMsgMessageFlags.h"
#include "nsIMsgFilterList.h"
#include "nsDirectoryServiceUtils.h"
#include "mozilla/Components.h"
#include "mozilla/Services.h"
#include "nsIFileStreams.h"
#include "nsIOutputStream.h"
#include "nsISafeOutputStream.h"
#include "nsXULAppAPI.h"
#include "nsICacheStorageService.h"
#include "UrlListener.h"
#include "nsIIDNService.h"
#define PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS "mail.accountmanager.accounts"
#define PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT \
"mail.accountmanager.defaultaccount"
#define PREF_MAIL_ACCOUNTMANAGER_LOCALFOLDERSSERVER \
"mail.accountmanager.localfoldersserver"
#define PREF_MAIL_SERVER_PREFIX "mail.server."
#define ACCOUNT_PREFIX "account"
#define SERVER_PREFIX "server"
#define ID_PREFIX "id"
#define ABOUT_TO_GO_OFFLINE_TOPIC "network:offline-about-to-go-offline"
#define ACCOUNT_DELIMITER ','
#define APPEND_ACCOUNTS_VERSION_PREF_NAME "append_preconfig_accounts.version"
#define MAILNEWS_ROOT_PREF "mailnews."
#define PREF_MAIL_ACCOUNTMANAGER_APPEND_ACCOUNTS \
"mail.accountmanager.appendaccounts"
#define NS_MSGACCOUNT_CID \
{0x68b25510, 0xe641, 0x11d2, {0xb7, 0xfc, 0x0, 0x80, 0x5f, 0x5, 0xff, 0xa5}}
static NS_DEFINE_CID(kMsgAccountCID, NS_MSGACCOUNT_CID);
#define SEARCH_FOLDER_FLAG "searchFolderFlag"
#define SEARCH_FOLDER_FLAG_LEN (sizeof(SEARCH_FOLDER_FLAG) - 1)
const char* kSearchFolderUriProp = "searchFolderUri";
bool nsMsgAccountManager::m_haveShutdown = false;
bool nsMsgAccountManager::m_shutdownInProgress = false;
NS_IMPL_ISUPPORTS(nsMsgAccountManager, nsIMsgAccountManager, nsIObserver,
nsISupportsWeakReference, nsIFolderListener,
nsIAsyncShutdownBlocker)
nsMsgAccountManager::nsMsgAccountManager()
: m_accountsLoaded(false),
m_emptyTrashInProgress(false),
m_cleanupInboxInProgress(false),
m_userAuthenticated(false),
m_loadingVirtualFolders(false),
m_virtualFoldersLoaded(false),
m_lastFindServerPort(0),
m_lastUniqueServerKey(1) {}
nsMsgAccountManager::~nsMsgAccountManager() {
if (!m_haveShutdown) {
// Don't remove from Observer service in Shutdown because Shutdown also gets
// called from xpcom shutdown observer. And we don't want to remove from
// the service in that case.
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->RemoveObserver(this, "search-folders-changed");
observerService->RemoveObserver(this, ABOUT_TO_GO_OFFLINE_TOPIC);
observerService->RemoveObserver(this, "sleep_notification");
}
}
}
static nsCOMPtr<nsIAsyncShutdownService> GetShutdownService() {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIAsyncShutdownService> service =
mozilla::services::GetAsyncShutdownService();
MOZ_RELEASE_ASSERT(service);
return service;
}
static nsCOMPtr<nsIAsyncShutdownClient> GetQuitApplicationGranted() {
nsCOMPtr<nsIAsyncShutdownClient> barrier;
nsresult rv =
GetShutdownService()->GetQuitApplicationGranted(getter_AddRefs(barrier));
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
MOZ_RELEASE_ASSERT(barrier);
return barrier;
}
static nsCOMPtr<nsIAsyncShutdownClient> GetProfileBeforeChange() {
nsCOMPtr<nsIAsyncShutdownClient> barrier;
nsresult rv =
GetShutdownService()->GetProfileBeforeChange(getter_AddRefs(barrier));
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
MOZ_RELEASE_ASSERT(barrier);
return barrier;
}
nsresult nsMsgAccountManager::Init() {
if (!XRE_IsParentProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv;
m_prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->AddObserver(this, "search-folders-changed", true);
observerService->AddObserver(this, ABOUT_TO_GO_OFFLINE_TOPIC, true);
observerService->AddObserver(this, "sleep_notification", true);
}
GetQuitApplicationGranted()->AddBlocker(
this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__,
u"nsMsgAccountManager cleanup on exit"_ns);
GetProfileBeforeChange()->AddBlocker(
this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__,
u"nsMsgAccountManager shutdown"_ns);
// Make sure PSM gets initialized before any accounts use certificates.
net_EnsurePSMInit();
return NS_OK;
}
nsresult nsMsgAccountManager::Shutdown() {
if (m_haveShutdown) // do not shutdown twice
return NS_OK;
nsresult rv;
SaveVirtualFolders();
if (m_dbService) {
nsTObserverArray<RefPtr<VirtualFolderChangeListener>>::ForwardIterator iter(
m_virtualFolderListeners);
RefPtr<VirtualFolderChangeListener> listener;
while (iter.HasMore()) {
listener = iter.GetNext();
m_dbService->UnregisterPendingListener(listener);
}
m_dbService = nullptr;
}
m_virtualFolders.Clear();
if (m_msgFolderCache) WriteToFolderCache(m_msgFolderCache);
(void)ShutdownServers();
(void)UnloadAccounts();
// shutdown removes nsIIncomingServer listener from biff manager, so do it
// after accounts have been unloaded
nsCOMPtr<nsIMsgBiffManager> biffService =
do_GetService("@mozilla.org/messenger/biffManager;1", &rv);
if (NS_SUCCEEDED(rv) && biffService) biffService->Shutdown();
// shutdown removes nsIIncomingServer listener from purge service, so do it
// after accounts have been unloaded
nsCOMPtr<nsIMsgPurgeService> purgeService =
do_GetService("@mozilla.org/messenger/purgeService;1", &rv);
if (NS_SUCCEEDED(rv) && purgeService) purgeService->Shutdown();
if (m_msgFolderCache) {
// The DTOR is meant to do the flushing, but observed behaviour is
// that it doesn't always get called. So flush explicitly.
m_msgFolderCache->Flush();
m_msgFolderCache = nullptr;
}
m_haveShutdown = true;
GetProfileBeforeChange()->RemoveBlocker(this);
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::GetShutdownInProgress(bool* _retval) {
NS_ENSURE_ARG_POINTER(_retval);
*_retval = m_shutdownInProgress;
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::GetUserNeedsToAuthenticate(bool* aRetval) {
NS_ENSURE_ARG_POINTER(aRetval);
if (!m_userAuthenticated)
return m_prefs->GetBoolPref("mail.password_protect_local_cache", aRetval);
*aRetval = !m_userAuthenticated;
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::SetUserNeedsToAuthenticate(bool aUserNeedsToAuthenticate) {
m_userAuthenticated = !aUserNeedsToAuthenticate;
return NS_OK;
}
NS_IMETHODIMP nsMsgAccountManager::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* someData) {
if (!strcmp(aTopic, "search-folders-changed")) {
nsCOMPtr<nsIMsgFolder> virtualFolder = do_QueryInterface(aSubject);
nsCOMPtr<nsIMsgDatabase> db;
nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
virtualFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo),
getter_AddRefs(db));
nsCString srchFolderUris;
dbFolderInfo->GetCharProperty(kSearchFolderUriProp, srchFolderUris);
AddVFListenersForVF(virtualFolder, srchFolderUris);
return NS_OK;
}
if (!strcmp(aTopic, ABOUT_TO_GO_OFFLINE_TOPIC)) {
nsAutoString dataString(u"offline"_ns);
if (someData) {
nsAutoString someDataString(someData);
if (dataString.Equals(someDataString)) CloseCachedConnections();
}
return NS_OK;
}
if (!strcmp(aTopic, "sleep_notification")) return CloseCachedConnections();
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::GetUniqueAccountKey(nsACString& aResult) {
int32_t lastKey = 0;
nsresult rv;
nsCOMPtr<nsIPrefService> prefservice(
do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIPrefBranch> prefBranch;
prefservice->GetBranch("", getter_AddRefs(prefBranch));
rv = prefBranch->GetIntPref("mail.account.lastKey", &lastKey);
if (NS_FAILED(rv) || lastKey == 0) {
// If lastKey pref does not contain a valid value, loop over existing
// pref names mail.account.* .
nsCOMPtr<nsIPrefBranch> prefBranchAccount;
rv = prefservice->GetBranch("mail.account.",
getter_AddRefs(prefBranchAccount));
if (NS_SUCCEEDED(rv)) {
nsTArray<nsCString> prefList;
rv = prefBranchAccount->GetChildList("", prefList);
if (NS_SUCCEEDED(rv)) {
// Pref names are of the format accountX.
// Find the maximum value of 'X' used so far.
for (auto& prefName : prefList) {
if (StringBeginsWith(prefName, nsLiteralCString(ACCOUNT_PREFIX))) {
int32_t dotPos = prefName.FindChar('.');
if (dotPos != kNotFound) {
nsCString keyString(Substring(prefName, strlen(ACCOUNT_PREFIX),
dotPos - strlen(ACCOUNT_PREFIX)));
int32_t thisKey = keyString.ToInteger(&rv);
if (NS_SUCCEEDED(rv)) lastKey = std::max(lastKey, thisKey);
}
}
}
}
}
}
// Use next available key and store the value in the pref.
aResult.Assign(ACCOUNT_PREFIX);
aResult.AppendInt(++lastKey);
rv = prefBranch->SetIntPref("mail.account.lastKey", lastKey);
} else {
// If pref service is not working, try to find a free accountX key
// by checking which keys exist.
int32_t i = 1;
nsCOMPtr<nsIMsgAccount> account;
do {
aResult = ACCOUNT_PREFIX;
aResult.AppendInt(i++);
GetAccount(aResult, getter_AddRefs(account));
} while (account);
}
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::GetUniqueServerKey(nsACString& aResult) {
nsAutoCString prefResult;
nsCOMPtr<nsIPrefService> prefService = mozilla::Preferences::GetService();
// Loop over existing pref names mail.server.server(lastKey).type
nsCOMPtr<nsIPrefBranch> prefBranchServer;
nsresult rv = prefService->GetBranch(PREF_MAIL_SERVER_PREFIX,
getter_AddRefs(prefBranchServer));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString type;
nsAutoCString typeKey;
for (;; m_lastUniqueServerKey++) {
aResult.AssignLiteral(SERVER_PREFIX);
aResult.AppendInt(m_lastUniqueServerKey);
typeKey.Assign(aResult);
typeKey.AppendLiteral(".type");
prefBranchServer->GetCharPref(typeKey.get(), type);
if (type.IsEmpty()) // a server slot with no type is considered empty
return NS_OK;
}
}
nsresult nsMsgAccountManager::CreateIdentity(nsIMsgIdentity** _retval) {
NS_ENSURE_ARG_POINTER(_retval);
nsresult rv;
nsAutoCString key;
nsCOMPtr<nsIMsgIdentity> identity;
int32_t i = 1;
do {
key.AssignLiteral(ID_PREFIX);
key.AppendInt(i++);
m_identities.Get(key, getter_AddRefs(identity));
} while (identity);
rv = createKeyedIdentity(key, _retval);
return rv;
}
NS_IMETHODIMP
nsMsgAccountManager::GetIdentity(const nsACString& key,
nsIMsgIdentity** _retval) {
NS_ENSURE_ARG_POINTER(_retval);
nsresult rv = NS_OK;
*_retval = nullptr;
if (!key.IsEmpty()) {
nsCOMPtr<nsIMsgIdentity> identity;
m_identities.Get(key, getter_AddRefs(identity));
if (identity)
identity.forget(_retval);
else // identity doesn't exist. create it.
rv = createKeyedIdentity(key, _retval);
}
return rv;
}
/*
* the shared identity-creation code
* create an identity and add it to the accountmanager's list.
*/
nsresult nsMsgAccountManager::createKeyedIdentity(const nsACString& key,
nsIMsgIdentity** aIdentity) {
nsresult rv;
nsCOMPtr<nsIMsgIdentity> identity =
do_CreateInstance("@mozilla.org/messenger/identity;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
identity->SetKey(key);
m_identities.InsertOrUpdate(key, identity);
identity.forget(aIdentity);
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::CreateIncomingServer(const nsACString& username,
const nsACString& hostname,
const nsACString& type,
nsIMsgIncomingServer** _retval) {
NS_ENSURE_ARG_POINTER(_retval);
// Make sure the hostname is usable when creating a new incoming server.
if (hostname.Equals("Local%20Folders") ||
hostname.Equals("smart%20mailboxes")) {
return NS_ERROR_MALFORMED_URI;
}
if (hostname.Equals("Local Folders") || hostname.Equals("smart mailboxes")) {
// Allow these special hostnames, but only for "none" servers.
if (!type.Equals("none")) {
return NS_ERROR_MALFORMED_URI;
}
} else {
nsAutoCString unused;
nsresult rv = NS_DomainToASCII(hostname, unused);
NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI);
nsCOMPtr<nsIURL> url;
rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
.SetSpec("imap://"_ns + hostname)
.Finalize(url);
NS_ENSURE_SUCCESS(rv, NS_ERROR_MALFORMED_URI);
}
nsresult rv = LoadAccounts();
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString key;
GetUniqueServerKey(key);
m_lastUniqueServerKey++; // Make sure the key won't be used again.
rv = createKeyedServer(key, username, hostname, type, _retval);
if (*_retval) {
nsCString defaultStore;
m_prefs->GetCharPref("mail.serverDefaultStoreContractID", defaultStore);
(*_retval)->SetCharValue("storeContractID", defaultStore);
// From when we first create the account until we have created some folders,
// we can change the store type.
(*_retval)->SetBoolValue("canChangeStoreType", true);
}
return rv;
}
NS_IMETHODIMP
nsMsgAccountManager::GetIncomingServer(const nsACString& key,
nsIMsgIncomingServer** _retval) {
NS_ENSURE_ARG_POINTER(_retval);
nsresult rv;
if (m_incomingServers.Get(key, _retval)) return NS_OK;
// server doesn't exist, so create it
// this is really horrible because we are doing our own prefname munging
// instead of leaving it up to the incoming server.
// this should be fixed somehow so that we can create the incoming server
// and then read from the incoming server's attributes
// in order to create the right kind of server, we have to look
// at the pref for this server to get the username, hostname, and type
nsAutoCString serverPrefPrefix(PREF_MAIL_SERVER_PREFIX);
serverPrefPrefix.Append(key);
nsCString serverType;
nsAutoCString serverPref(serverPrefPrefix);
serverPref.AppendLiteral(".type");
rv = m_prefs->GetCharPref(serverPref.get(), serverType);
NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_INITIALIZED);
//
// .userName
serverPref = serverPrefPrefix;
serverPref.AppendLiteral(".userName");
nsCString username;
rv = m_prefs->GetCharPref(serverPref.get(), username);
// .hostname
serverPref = serverPrefPrefix;
serverPref.AppendLiteral(".hostname");
nsCString hostname;
rv = m_prefs->GetCharPref(serverPref.get(), hostname);
NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_INITIALIZED);
return createKeyedServer(key, username, hostname, serverType, _retval);
}
NS_IMETHODIMP
nsMsgAccountManager::RemoveIncomingServer(nsIMsgIncomingServer* aServer,
bool aRemoveFiles) {
NS_ENSURE_ARG_POINTER(aServer);
nsCString serverKey;
nsresult rv = aServer->GetKey(serverKey);
NS_ENSURE_SUCCESS(rv, rv);
// close cached connections and forget session password
LogoutOfServer(aServer);
// invalidate the FindServer() cache if we are removing the cached server
if (m_lastFindServerResult == aServer)
SetLastServerFound(nullptr, EmptyCString(), EmptyCString(), 0,
EmptyCString());
m_incomingServers.Remove(serverKey);
nsCOMPtr<nsIMsgFolder> rootFolder;
rv = aServer->GetRootFolder(getter_AddRefs(rootFolder));
NS_ENSURE_SUCCESS(rv, rv);
nsTArray<RefPtr<nsIMsgFolder>> allDescendants;
rv = rootFolder->GetDescendants(allDescendants);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgFolderNotificationService> notifier =
do_GetService("@mozilla.org/messenger/msgnotificationservice;1");
nsCOMPtr<nsIFolderListener> mailSession =
do_GetService("@mozilla.org/messenger/services/session;1");
for (auto folder : allDescendants) {
folder->ForceDBClosed();
if (notifier) notifier->NotifyFolderDeleted(folder);
if (mailSession) {
nsCOMPtr<nsIMsgFolder> parentFolder;
folder->GetParent(getter_AddRefs(parentFolder));
mailSession->OnFolderRemoved(parentFolder, folder);
}
}
if (notifier) notifier->NotifyFolderDeleted(rootFolder);
if (mailSession) mailSession->OnFolderRemoved(nullptr, rootFolder);
NotifyServerUnloaded(aServer);
if (aRemoveFiles) {
rv = aServer->RemoveFiles();
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->NotifyObservers(aServer, "message-server-removed",
NS_ConvertUTF8toUTF16(serverKey).get());
}
// now clear out the server once and for all.
// watch out! could be scary
aServer->ClearAllValues();
rootFolder->Shutdown(true);
return rv;
}
/**
* Create a server when you know the key and the type
*/
nsresult nsMsgAccountManager::createKeyedServer(
const nsACString& key, const nsACString& username,
const nsACString& hostnameIn, const nsACString& type,
nsIMsgIncomingServer** aServer) {
nsresult rv;
*aServer = nullptr;
nsAutoCString hostname(hostnameIn);
if (hostname.Equals("Local Folders") || hostname.Equals("smart mailboxes")) {
// Allow these special hostnames, but only for "none" servers.
if (type != "none") {
return NS_ERROR_MALFORMED_URI;
}
} else if (hostname.Equals("Local%20Folders") ||
hostname.Equals("smart%20mailboxes")) {
// Don't allow these %-encoded special hostnames.
return NS_ERROR_MALFORMED_URI;
} else {
// Check the hostname is valid.
nsAutoCString unused;
rv = NS_DomainToASCII(hostname, unused);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIURL> url;
rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
.SetSpec("imap://"_ns + hostname)
.Finalize(url);
}
if (NS_FAILED(rv)) {
// In case of failure, use a <key>.invalid hostname instead
// so that access to the account is not lost.
hostname = key + ".invalid"_ns;
}
}
// construct the contractid
nsAutoCString serverContractID("@mozilla.org/messenger/server;1?type=");
serverContractID += type;
// finally, create the server
// (This will fail if type is from an extension that has been removed)
nsCOMPtr<nsIMsgIncomingServer> server =
do_CreateInstance(serverContractID.get(), &rv);
NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_AVAILABLE);
int32_t port;
nsCOMPtr<nsIMsgIncomingServer> existingServer;
server->SetKey(key);
server->SetType(type);
server->SetUsername(username);
server->SetHostName(hostname);
server->GetPort(&port);
FindServer(username, hostname, type, port, getter_AddRefs(existingServer));
// don't allow duplicate servers.
if (existingServer) return NS_ERROR_FAILURE;
m_incomingServers.InsertOrUpdate(key, server);
// now add all listeners that are supposed to be
// waiting on root folders
nsCOMPtr<nsIMsgFolder> rootFolder;
rv = server->GetRootFolder(getter_AddRefs(rootFolder));
NS_ENSURE_SUCCESS(rv, rv);
server.forget(aServer);
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::RemoveAccount(nsIMsgAccount* aAccount,
bool aRemoveFiles = false) {
NS_ENSURE_ARG_POINTER(aAccount);
// Hold account in scope while we tidy up potentially-shared identities.
nsresult rv = LoadAccounts();
NS_ENSURE_SUCCESS(rv, rv);
if (!m_accounts.RemoveElement(aAccount)) {
return NS_ERROR_INVALID_ARG;
}
rv = OutputAccountsPref();
// If we couldn't write out the pref, restore the account.
if (NS_FAILED(rv)) {
m_accounts.AppendElement(aAccount);
return rv;
}
// If it's the default, choose a new default account.
if (m_defaultAccount == aAccount) AutosetDefaultAccount();
// XXX - need to figure out if this is the last time this server is
// being used, and only send notification then.
// (and only remove from hashtable then too!)
nsCOMPtr<nsIMsgIncomingServer> server;
rv = aAccount->GetIncomingServer(getter_AddRefs(server));
if (NS_SUCCEEDED(rv) && server) RemoveIncomingServer(server, aRemoveFiles);
nsTArray<RefPtr<nsIMsgIdentity>> identities;
rv = aAccount->GetIdentities(identities);
if (NS_SUCCEEDED(rv)) {
for (auto identity : identities) {
bool identityStillUsed = false;
// for each identity, see if any remaining account still uses it,
// and if not, clear it.
// Note that we are also searching here accounts with missing servers from
// unloaded extension types.
for (auto account : m_accounts) {
nsTArray<RefPtr<nsIMsgIdentity>> existingIdentities;
account->GetIdentities(existingIdentities);
auto pos = existingIdentities.IndexOf(identity);
if (pos != existingIdentities.NoIndex) {
identityStillUsed = true;
break;
}
}
// clear out all identity information if no other account uses it.
if (!identityStillUsed) identity->ClearAllValues();
}
}
nsCString accountKey;
aAccount->GetKey(accountKey);
// It is not a critical problem if this fails as the account was already
// removed from the list of accounts so should not ever be referenced.
// Just print it out for debugging.
rv = aAccount->ClearAllValues();
NS_ASSERTION(NS_SUCCEEDED(rv), "removing of account prefs failed");
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
obs->NotifyObservers(nullptr, "message-account-removed",
NS_ConvertUTF8toUTF16(accountKey).get());
}
return NS_OK;
}
nsresult nsMsgAccountManager::OutputAccountsPref() {
nsCString accountKey;
mAccountKeyList.Truncate();
for (uint32_t index = 0; index < m_accounts.Length(); index++) {
m_accounts[index]->GetKey(accountKey);
if (index) mAccountKeyList.Append(ACCOUNT_DELIMITER);
mAccountKeyList.Append(accountKey);
}
return m_prefs->SetCharPref(PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS,
mAccountKeyList);
}
/**
* Get the default account. If no default account, return null.
*/
NS_IMETHODIMP
nsMsgAccountManager::GetDefaultAccount(nsIMsgAccount** aDefaultAccount) {
NS_ENSURE_ARG_POINTER(aDefaultAccount);
nsresult rv = LoadAccounts();
NS_ENSURE_SUCCESS(rv, rv);
if (!m_defaultAccount) {
nsCString defaultKey;
rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT,
defaultKey);
if (NS_SUCCEEDED(rv)) {
rv = GetAccount(defaultKey, getter_AddRefs(m_defaultAccount));
if (NS_SUCCEEDED(rv) && m_defaultAccount) {
bool canBeDefault = false;
rv = CheckDefaultAccount(m_defaultAccount, canBeDefault);
if (NS_FAILED(rv) || !canBeDefault) m_defaultAccount = nullptr;
}
}
}
NS_IF_ADDREF(*aDefaultAccount = m_defaultAccount);
return NS_OK;
}
/**
* Check if the given account can be default.
*/
nsresult nsMsgAccountManager::CheckDefaultAccount(nsIMsgAccount* aAccount,
bool& aCanBeDefault) {
aCanBeDefault = false;
nsCOMPtr<nsIMsgIncomingServer> server;
// Server could be null if created by an unloaded extension.
nsresult rv = aAccount->GetIncomingServer(getter_AddRefs(server));
NS_ENSURE_SUCCESS(rv, rv);
if (server) {
// Check if server can be default.
rv = server->GetCanBeDefaultServer(&aCanBeDefault);
}
return rv;
}
/**
* Pick the first account that can be default and make it the default.
*/
nsresult nsMsgAccountManager::AutosetDefaultAccount() {
for (nsIMsgAccount* account : m_accounts) {
bool canBeDefault = false;
nsresult rv = CheckDefaultAccount(account, canBeDefault);
if (NS_SUCCEEDED(rv) && canBeDefault) {
return SetDefaultAccount(account);
}
}
// No accounts can be the default. Clear it.
if (m_defaultAccount) {
nsCOMPtr<nsIMsgAccount> oldAccount = m_defaultAccount;
m_defaultAccount = nullptr;
(void)setDefaultAccountPref(nullptr);
(void)notifyDefaultServerChange(oldAccount, nullptr);
}
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::SetDefaultAccount(nsIMsgAccount* aDefaultAccount) {
if (!aDefaultAccount) return NS_ERROR_INVALID_ARG;
if (m_defaultAccount != aDefaultAccount) {
bool canBeDefault = false;
nsresult rv = CheckDefaultAccount(aDefaultAccount, canBeDefault);
if (NS_FAILED(rv) || !canBeDefault) {
// Report failure if we were explicitly asked to use an unusable server.
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsIMsgAccount> oldAccount = m_defaultAccount;
m_defaultAccount = aDefaultAccount;
(void)setDefaultAccountPref(aDefaultAccount);
(void)notifyDefaultServerChange(oldAccount, aDefaultAccount);
}
return NS_OK;
}
// fire notifications
nsresult nsMsgAccountManager::notifyDefaultServerChange(
nsIMsgAccount* aOldAccount, nsIMsgAccount* aNewAccount) {
nsresult rv;
nsCOMPtr<nsIMsgIncomingServer> server;
nsCOMPtr<nsIMsgFolder> rootFolder;
// first tell old server it's no longer the default
if (aOldAccount) {
rv = aOldAccount->GetIncomingServer(getter_AddRefs(server));
if (NS_SUCCEEDED(rv) && server) {
rv = server->GetRootFolder(getter_AddRefs(rootFolder));
if (NS_SUCCEEDED(rv) && rootFolder)
rootFolder->NotifyBoolPropertyChanged(kDefaultServer, true, false);
}
}
// now tell new server it is.
if (aNewAccount) {
rv = aNewAccount->GetIncomingServer(getter_AddRefs(server));
if (NS_SUCCEEDED(rv) && server) {
rv = server->GetRootFolder(getter_AddRefs(rootFolder));
if (NS_SUCCEEDED(rv) && rootFolder)
rootFolder->NotifyBoolPropertyChanged(kDefaultServer, false, true);
}
}
// only notify if the user goes and changes default account
if (aOldAccount && aNewAccount) {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService)
observerService->NotifyObservers(nullptr, "mailDefaultAccountChanged",
nullptr);
}
return NS_OK;
}
nsresult nsMsgAccountManager::setDefaultAccountPref(
nsIMsgAccount* aDefaultAccount) {
nsresult rv;
if (aDefaultAccount) {
nsCString key;
rv = aDefaultAccount->GetKey(key);
NS_ENSURE_SUCCESS(rv, rv);
rv = m_prefs->SetCharPref(PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT, key);
NS_ENSURE_SUCCESS(rv, rv);
} else
m_prefs->ClearUserPref(PREF_MAIL_ACCOUNTMANAGER_DEFAULTACCOUNT);
return NS_OK;
}
void nsMsgAccountManager::LogoutOfServer(nsIMsgIncomingServer* aServer) {
if (!aServer) return;
mozilla::DebugOnly<nsresult> rv = aServer->Shutdown();
NS_ASSERTION(NS_SUCCEEDED(rv), "Shutdown of server failed");
rv = aServer->ForgetSessionPassword(false);
NS_ASSERTION(NS_SUCCEEDED(rv),
"failed to remove the password associated with server");
}
NS_IMETHODIMP nsMsgAccountManager::GetFolderCache(
nsIMsgFolderCache** aFolderCache) {
NS_ENSURE_ARG_POINTER(aFolderCache);
if (m_msgFolderCache) {
NS_IF_ADDREF(*aFolderCache = m_msgFolderCache);
return NS_OK;
}
MOZ_ASSERT(NS_IsMainThread(),
"first call to GetFolderCache must happen on the main thread");
// Create the foldercache.
nsCOMPtr<nsIFile> cacheFile;
nsresult rv = NS_GetSpecialDirectory(NS_APP_MESSENGER_FOLDER_CACHE_50_FILE,
getter_AddRefs(cacheFile));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> legacyFile;
rv = NS_GetSpecialDirectory(NS_APP_MESSENGER_LEGACY_FOLDER_CACHE_50_FILE,
getter_AddRefs(legacyFile));
NS_ENSURE_SUCCESS(rv, rv);
m_msgFolderCache = new nsMsgFolderCache();
m_msgFolderCache->Init(cacheFile, legacyFile);
NS_IF_ADDREF(*aFolderCache = m_msgFolderCache);
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::GetAccounts(nsTArray<RefPtr<nsIMsgAccount>>& accounts) {
nsresult rv = LoadAccounts();
NS_ENSURE_SUCCESS(rv, rv);
accounts.Clear();
accounts.SetCapacity(m_accounts.Length());
for (auto existingAccount : m_accounts) {
nsCOMPtr<nsIMsgIncomingServer> server;
existingAccount->GetIncomingServer(getter_AddRefs(server));
if (!server) continue;
if (server) {
bool hidden = false;
server->GetHidden(&hidden);
if (hidden) continue;
}
accounts.AppendElement(existingAccount);
}
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::GetAllIdentities(
nsTArray<RefPtr<nsIMsgIdentity>>& result) {
nsresult rv = LoadAccounts();
NS_ENSURE_SUCCESS(rv, rv);
result.Clear();
for (auto account : m_accounts) {
nsTArray<RefPtr<nsIMsgIdentity>> identities;
rv = account->GetIdentities(identities);
if (NS_FAILED(rv)) continue;
for (auto identity : identities) {
// Have we already got this identity?
nsAutoCString key;
rv = identity->GetKey(key);
if (NS_FAILED(rv)) continue;
bool found = false;
for (auto thisIdentity : result) {
nsAutoCString thisKey;
rv = thisIdentity->GetKey(thisKey);
if (NS_FAILED(rv)) continue;
if (key == thisKey) {
found = true;
break;
}
}
if (!found) result.AppendElement(identity);
}
}
return rv;
}
NS_IMETHODIMP
nsMsgAccountManager::GetAllServers(
nsTArray<RefPtr<nsIMsgIncomingServer>>& servers) {
servers.Clear();
nsresult rv = LoadAccounts();
NS_ENSURE_SUCCESS(rv, rv);
for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
if (!server) continue;
bool hidden = false;
server->GetHidden(&hidden);
if (hidden) continue;
nsCString type;
if (NS_FAILED(server->GetType(type))) {
NS_WARNING("server->GetType() failed");
continue;
}
if (!type.EqualsLiteral("im")) {
servers.AppendElement(server);
}
}
return NS_OK;
}
nsresult nsMsgAccountManager::LoadAccounts() {
nsresult rv;
// for now safeguard multiple calls to this function
if (m_accountsLoaded) return NS_OK;
// If we have code trying to do things after we've unloaded accounts,
// ignore it.
if (m_shutdownInProgress || m_haveShutdown) return NS_ERROR_FAILURE;
// Make sure correct modules are loaded before creating any server.
nsCOMPtr<nsIObserver> moduleLoader;
moduleLoader =
do_GetService("@mozilla.org/messenger/imap-module-loader;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgMailSession> mailSession =
do_GetService("@mozilla.org/messenger/services/session;1", &rv);
if (NS_SUCCEEDED(rv))
mailSession->AddFolderListener(
this, nsIFolderListener::added | nsIFolderListener::removed |
nsIFolderListener::intPropertyChanged);
// Ensure biff service has started
nsCOMPtr<nsIMsgBiffManager> biffService =
do_GetService("@mozilla.org/messenger/biffManager;1", &rv);
if (NS_SUCCEEDED(rv)) biffService->Init();
// Ensure purge service has started
nsCOMPtr<nsIMsgPurgeService> purgeService =
do_GetService("@mozilla.org/messenger/purgeService;1", &rv);
if (NS_SUCCEEDED(rv)) purgeService->Init();
nsCOMPtr<nsIPrefService> prefservice(
do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
// mail.accountmanager.accounts is the main entry point for all accounts
nsCString accountList;
rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS, accountList);
/**
* Check to see if we need to add pre-configured accounts.
* Following prefs are important to note in understanding the procedure here.
*
* 1. pref("mailnews.append_preconfig_accounts.version", version number);
* This pref registers the current version in the user prefs file. A default
* value is stored in mailnews.js file. If a given vendor needs to add more
* preconfigured accounts, the default version number can be increased.
* Comparing version number from user's prefs file and the default one from
* mailnews.js, we can add new accounts and any other version level changes
* that need to be done.
*
* 2. pref("mail.accountmanager.appendaccounts", <comma sep. account list>);
* This pref contains the list of pre-configured accounts that ISP/Vendor
* wants to add to the existing accounts list.
*/
nsCOMPtr<nsIPrefBranch> defaultsPrefBranch;
rv = prefservice->GetDefaultBranch(MAILNEWS_ROOT_PREF,
getter_AddRefs(defaultsPrefBranch));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrefBranch> prefBranch;
rv = prefservice->GetBranch(MAILNEWS_ROOT_PREF, getter_AddRefs(prefBranch));
NS_ENSURE_SUCCESS(rv, rv);
int32_t appendAccountsCurrentVersion = 0;
int32_t appendAccountsDefaultVersion = 0;
rv = prefBranch->GetIntPref(APPEND_ACCOUNTS_VERSION_PREF_NAME,
&appendAccountsCurrentVersion);
NS_ENSURE_SUCCESS(rv, rv);
rv = defaultsPrefBranch->GetIntPref(APPEND_ACCOUNTS_VERSION_PREF_NAME,
&appendAccountsDefaultVersion);
NS_ENSURE_SUCCESS(rv, rv);
// Update the account list if needed
if ((appendAccountsCurrentVersion <= appendAccountsDefaultVersion)) {
// Get a list of pre-configured accounts
nsCString appendAccountList;
rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_APPEND_ACCOUNTS,
appendAccountList);
appendAccountList.StripWhitespace();
// If there are pre-configured accounts, we need to add them to the
// existing list.
if (!appendAccountList.IsEmpty()) {
if (!accountList.IsEmpty()) {
// Tokenize the data and add each account
// in the user's current mailnews account list
nsTArray<nsCString> accountsArray;
ParseString(accountList, ACCOUNT_DELIMITER, accountsArray);
uint32_t i = accountsArray.Length();
// Append each account in the pre-configured account list
ParseString(appendAccountList, ACCOUNT_DELIMITER, accountsArray);
// Now add each account that does not already appear in the list
for (; i < accountsArray.Length(); i++) {
if (accountsArray.IndexOf(accountsArray[i]) == i) {
accountList.Append(ACCOUNT_DELIMITER);
accountList.Append(accountsArray[i]);
}
}
} else {
accountList = appendAccountList;
}
// Increase the version number so that updates will happen as and when
// needed
rv = prefBranch->SetIntPref(APPEND_ACCOUNTS_VERSION_PREF_NAME,
appendAccountsCurrentVersion + 1);
}
}
// It is ok to return null accounts like when we create new profile.
m_accountsLoaded = true;
m_haveShutdown = false;
if (accountList.IsEmpty()) return NS_OK;
/* parse accountList and run loadAccount on each string, comma-separated */
nsCOMPtr<nsIMsgAccount> account;
// Tokenize the data and add each account
// in the user's current mailnews account list
nsTArray<nsCString> accountsArray;
ParseString(accountList, ACCOUNT_DELIMITER, accountsArray);
// These are the duplicate accounts we found. We keep track of these
// because if any other server defers to one of these accounts, we need
// to defer to the correct account.
nsCOMArray<nsIMsgAccount> dupAccounts;
// Now add each account that does not already appear in the list
for (uint32_t i = 0; i < accountsArray.Length(); i++) {
// if we've already seen this exact account, advance to the next account.
// After the loop, we'll notice that we don't have as many actual accounts
// as there were accounts in the pref, and rewrite the pref.
if (accountsArray.IndexOf(accountsArray[i]) != i) continue;
// get the "server" pref to see if we already have an account with this
// server. If we do, we ignore this account.
nsAutoCString serverKeyPref("mail.account.");
serverKeyPref += accountsArray[i];
nsCOMPtr<nsIPrefBranch> accountPrefBranch;
rv = prefservice->GetBranch(serverKeyPref.get(),
getter_AddRefs(accountPrefBranch));
NS_ENSURE_SUCCESS(rv, rv);
serverKeyPref += ".server";
nsCString serverKey;
rv = m_prefs->GetCharPref(serverKeyPref.get(), serverKey);
if (NS_FAILED(rv)) continue;
nsCOMPtr<nsIMsgAccount> serverAccount;
findAccountByServerKey(serverKey, getter_AddRefs(serverAccount));
// If we have an existing account with the same server, ignore this account
if (serverAccount) continue;
if (NS_FAILED(createKeyedAccount(accountsArray[i], true,
getter_AddRefs(account))) ||
!account) {
NS_WARNING("unexpected entry in account list; prefs corrupt?");
continue;
}
// See nsIMsgAccount.idl for a description of the secondsToLeaveUnavailable
// and timeFoundUnavailable preferences
nsAutoCString toLeavePref(PREF_MAIL_SERVER_PREFIX);
toLeavePref.Append(serverKey);
nsAutoCString unavailablePref(
toLeavePref); // this is the server-specific prefix
unavailablePref.AppendLiteral(".timeFoundUnavailable");
toLeavePref.AppendLiteral(".secondsToLeaveUnavailable");
int32_t secondsToLeave = 0;
int32_t timeUnavailable = 0;
m_prefs->GetIntPref(toLeavePref.get(), &secondsToLeave);
// force load of accounts (need to find a better way to do this)
nsTArray<RefPtr<nsIMsgIdentity>> unused;
account->GetIdentities(unused);
rv = account->CreateServer();
bool deleteAccount = NS_FAILED(rv);
if (secondsToLeave) { // we need to process timeUnavailable
if (NS_SUCCEEDED(rv)) // clear the time if server is available
{
m_prefs->ClearUserPref(unavailablePref.get());
}
// NS_ERROR_NOT_AVAILABLE signifies a server that could not be
// instantiated, presumably because of an invalid type.
else if (rv == NS_ERROR_NOT_AVAILABLE) {
m_prefs->GetIntPref(unavailablePref.get(), &timeUnavailable);
if (!timeUnavailable) { // we need to set it, this must be the first
// time unavailable
uint32_t nowSeconds;
PRTime2Seconds(PR_Now(), &nowSeconds);
m_prefs->SetIntPref(unavailablePref.get(), nowSeconds);
deleteAccount = false;
}
}
}
// Our server is still unavailable. Have we timed out yet?
if (rv == NS_ERROR_NOT_AVAILABLE && timeUnavailable != 0) {
uint32_t nowSeconds;
PRTime2Seconds(PR_Now(), &nowSeconds);
if ((int32_t)nowSeconds < timeUnavailable + secondsToLeave)
deleteAccount = false;
}
if (deleteAccount) {
dupAccounts.AppendObject(account);
m_accounts.RemoveElement(account);
}
}
// Check if we removed one or more of the accounts in the pref string.
// If so, rewrite the pref string.
if (accountsArray.Length() != m_accounts.Length()) OutputAccountsPref();
int32_t cnt = dupAccounts.Count();
nsCOMPtr<nsIMsgAccount> dupAccount;
// Go through the accounts seeing if any existing server is deferred to
// an account we removed. If so, fix the deferral. Then clean up the prefs
// for the removed account.
for (int32_t i = 0; i < cnt; i++) {
dupAccount = dupAccounts[i];
for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
/*
* This loop gets run for every incoming server, and is passed a
* duplicate account. It checks that the server is not deferred to the
* duplicate account. If it is, then it looks up the information for the
* duplicate account's server (username, hostName, type), and finds an
* account with a server with the same username, hostname, and type, and
* if it finds one, defers to that account instead. Generally, this will
* be a Local Folders account, since 2.0 has a bug where duplicate Local
* Folders accounts are created.
*/
nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
nsCString type;
server->GetType(type);
if (type.EqualsLiteral("pop3")) {
nsCString deferredToAccount;
// Get the pref directly, because the GetDeferredToAccount accessor
// attempts to fix broken deferrals, but we know more about what the
// deferred to account was.
server->GetCharValue("deferred_to_account", deferredToAccount);
if (!deferredToAccount.IsEmpty()) {
nsCString dupAccountKey;
dupAccount->GetKey(dupAccountKey);
if (deferredToAccount.Equals(dupAccountKey)) {
nsresult rv;
nsCString accountPref("mail.account.");
nsCString dupAccountServerKey;
accountPref.Append(dupAccountKey);
accountPref.AppendLiteral(".server");
nsCOMPtr<nsIPrefService> prefservice(
do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
continue;
}
nsCOMPtr<nsIPrefBranch> prefBranch(
do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
if (NS_FAILED(rv)) {
continue;
}
rv =
prefBranch->GetCharPref(accountPref.get(), dupAccountServerKey);
if (NS_FAILED(rv)) {
continue;
}
nsCOMPtr<nsIPrefBranch> serverPrefBranch;
nsCString serverKeyPref(PREF_MAIL_SERVER_PREFIX);
serverKeyPref.Append(dupAccountServerKey);
serverKeyPref.Append('.');
rv = prefservice->GetBranch(serverKeyPref.get(),
getter_AddRefs(serverPrefBranch));
if (NS_FAILED(rv)) {
continue;
}
nsCString userName;
nsCString hostName;
nsCString type;
serverPrefBranch->GetCharPref("userName", userName);
serverPrefBranch->GetCharPref("hostname", hostName);
serverPrefBranch->GetCharPref("type", type);
// Find a server with the same info.
nsCOMPtr<nsIMsgAccountManager> accountManager =
do_GetService("@mozilla.org/messenger/account-manager;1", &rv);
if (NS_FAILED(rv)) {
continue;
}
nsCOMPtr<nsIMsgIncomingServer> server;
accountManager->FindServer(userName, hostName, type, 0,
getter_AddRefs(server));
if (server) {
nsCOMPtr<nsIMsgAccount> replacement;
accountManager->FindAccountForServer(server,
getter_AddRefs(replacement));
if (replacement) {
nsCString accountKey;
replacement->GetKey(accountKey);
if (!accountKey.IsEmpty())
server->SetCharValue("deferred_to_account", accountKey);
}
}
}
}
}
}
nsAutoCString accountKeyPref("mail.account.");
nsCString dupAccountKey;
dupAccount->GetKey(dupAccountKey);
if (dupAccountKey.IsEmpty()) continue;
accountKeyPref.Append(dupAccountKey);
accountKeyPref.Append('.');
nsCOMPtr<nsIPrefBranch> accountPrefBranch;
rv = prefservice->GetBranch(accountKeyPref.get(),
getter_AddRefs(accountPrefBranch));
if (accountPrefBranch) {
nsTArray<nsCString> prefNames;
nsresult rv = accountPrefBranch->GetChildList("", prefNames);
NS_ENSURE_SUCCESS(rv, rv);
for (auto& prefName : prefNames) {
accountPrefBranch->ClearUserPref(prefName.get());
}
}
}
// Make sure we have an account that points at the local folders server
nsCString localFoldersServerKey;
rv = m_prefs->GetCharPref(PREF_MAIL_ACCOUNTMANAGER_LOCALFOLDERSSERVER,
localFoldersServerKey);
if (!localFoldersServerKey.IsEmpty()) {
nsCOMPtr<nsIMsgIncomingServer> server;
rv = GetIncomingServer(localFoldersServerKey, getter_AddRefs(server));
if (server) {
nsCOMPtr<nsIMsgAccount> localFoldersAccount;
findAccountByServerKey(localFoldersServerKey,
getter_AddRefs(localFoldersAccount));
// If we don't have an existing account pointing at the local folders
// server, we're going to add one.
if (!localFoldersAccount) {
nsCOMPtr<nsIMsgAccount> account;
(void)CreateAccount(getter_AddRefs(account));
if (account) account->SetIncomingServer(server);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::ReactivateAccounts() {
for (nsIMsgAccount* account : m_accounts) {
// This will error out if the account already has its server, or
// if this isn't the account that the extension is trying to reactivate.
if (NS_SUCCEEDED(account->CreateServer())) {
nsCOMPtr<nsIMsgIncomingServer> server;
account->GetIncomingServer(getter_AddRefs(server));
// This triggers all of the notifications required by the UI.
account->SetIncomingServer(server);
}
}
return NS_OK;
}
// this routine goes through all the identities and makes sure
// that the special folders for each identity have the
// correct special folder flags set, e.g, the Sent folder has
// the sent flag set.
//
// it also goes through all the spam settings for each account
// and makes sure the folder flags are set there, too
NS_IMETHODIMP
nsMsgAccountManager::SetSpecialFolders() {
nsTArray<RefPtr<nsIMsgIdentity>> identities;
GetAllIdentities(identities);
for (auto identity : identities) {
nsresult rv;
nsCString folderUri;
nsCOMPtr<nsIMsgFolder> folder;
identity->GetFccFolder(folderUri);
if (!folderUri.IsEmpty() &&
NS_SUCCEEDED(GetOrCreateFolder(folderUri, getter_AddRefs(folder)))) {
nsCOMPtr<nsIMsgFolder> parent;
rv = folder->GetParent(getter_AddRefs(parent));
if (NS_SUCCEEDED(rv) && parent) {
rv = folder->SetFlag(nsMsgFolderFlags::SentMail);
NS_ENSURE_SUCCESS(rv, rv);
}
}
identity->GetDraftFolder(folderUri);
if (!folderUri.IsEmpty() &&
NS_SUCCEEDED(GetOrCreateFolder(folderUri, getter_AddRefs(folder)))) {
nsCOMPtr<nsIMsgFolder> parent;
rv = folder->GetParent(getter_AddRefs(parent));
if (NS_SUCCEEDED(rv) && parent) {
rv = folder->SetFlag(nsMsgFolderFlags::Drafts);
NS_ENSURE_SUCCESS(rv, rv);
}
}
identity->GetArchiveFolder(folderUri);
if (!folderUri.IsEmpty() &&
NS_SUCCEEDED(GetOrCreateFolder(folderUri, getter_AddRefs(folder)))) {
nsCOMPtr<nsIMsgFolder> parent;
rv = folder->GetParent(getter_AddRefs(parent));
if (NS_SUCCEEDED(rv) && parent) {
bool archiveEnabled;
identity->GetArchiveEnabled(&archiveEnabled);
if (archiveEnabled)
rv = folder->SetFlag(nsMsgFolderFlags::Archive);
else
rv = folder->ClearFlag(nsMsgFolderFlags::Archive);
NS_ENSURE_SUCCESS(rv, rv);
}
}
identity->GetStationeryFolder(folderUri);
if (!folderUri.IsEmpty() &&
NS_SUCCEEDED(GetOrCreateFolder(folderUri, getter_AddRefs(folder)))) {
nsCOMPtr<nsIMsgFolder> parent;
rv = folder->GetParent(getter_AddRefs(parent));
if (NS_SUCCEEDED(rv) && parent) {
folder->SetFlag(nsMsgFolderFlags::Templates);
NS_ENSURE_SUCCESS(rv, rv);
}
}
}
// XXX todo
// get all servers
// get all spam settings for each server
// set the JUNK folder flag on the spam folders, right?
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::UnloadAccounts() {
// release the default account
m_defaultAccount = nullptr;
for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
if (!server) continue;
nsresult rv;
NotifyServerUnloaded(server);
nsCOMPtr<nsIMsgFolder> rootFolder;
rv = server->GetRootFolder(getter_AddRefs(rootFolder));
if (NS_SUCCEEDED(rv)) {
rootFolder->Shutdown(true);
}
}
m_accounts.Clear(); // will release all elements
m_identities.Clear();
m_incomingServers.Clear();
mAccountKeyList.Truncate();
SetLastServerFound(nullptr, EmptyCString(), EmptyCString(), 0,
EmptyCString());
if (m_accountsLoaded) {
nsCOMPtr<nsIMsgMailSession> mailSession =
do_GetService("@mozilla.org/messenger/services/session;1");
if (mailSession) mailSession->RemoveFolderListener(this);
m_accountsLoaded = false;
}
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::ShutdownServers() {
for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
if (server) server->Shutdown();
}
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::CloseCachedConnections() {
for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
if (server) server->CloseCachedConnections();
}
return NS_OK;
}
nsresult nsMsgAccountManager::CleanupOnExit() {
// This can get called multiple times, and potentially re-entrantly.
// So add some protection against that.
if (m_shutdownInProgress) return NS_OK;
m_shutdownInProgress = true;
nsresult rv;
// If enabled, clear cache on shutdown. This is common to all accounts.
bool clearCache = false;
m_prefs->GetBoolPref("privacy.clearOnShutdown.cache", &clearCache);
if (clearCache) {
nsCOMPtr<nsICacheStorageService> cacheStorageService =
do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
if (NS_SUCCEEDED(rv)) cacheStorageService->Clear();
}
for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
bool emptyTrashOnExit = false;
bool cleanupInboxOnExit = false;
if (WeAreOffline()) break;
if (!server) continue;
server->GetEmptyTrashOnExit(&emptyTrashOnExit);
nsCOMPtr<nsIImapIncomingServer> imapserver = do_QueryInterface(server);
if (imapserver) {
imapserver->GetCleanupInboxOnExit(&cleanupInboxOnExit);
imapserver->SetShuttingDown(true);
}
if (!emptyTrashOnExit && !cleanupInboxOnExit) {
continue;
}
nsCOMPtr<nsIMsgFolder> root;
server->GetRootFolder(getter_AddRefs(root));
nsCString type;
server->GetType(type);
if (!root) {
continue;
}
nsString passwd;
int32_t authMethod = 0;
bool serverRequiresPasswordForAuthentication = true;
bool isImap = type.EqualsLiteral("imap");
if (isImap) {
server->GetServerRequiresPasswordForBiff(
&serverRequiresPasswordForAuthentication);
server->GetPassword(passwd);
server->GetAuthMethod(&authMethod);
}
if (!isImap || (isImap && (!serverRequiresPasswordForAuthentication ||
!passwd.IsEmpty() ||
authMethod == nsMsgAuthMethod::OAuth2))) {
nsCOMPtr<nsIMsgAccountManager> accountManager =
do_GetService("@mozilla.org/messenger/account-manager;1", &rv);
if (NS_FAILED(rv)) continue;
if (isImap && cleanupInboxOnExit) {
// Find the inbox.
nsTArray<RefPtr<nsIMsgFolder>> subFolders;
rv = root->GetSubFolders(subFolders);
if (NS_SUCCEEDED(rv)) {
for (nsIMsgFolder* folder : subFolders) {
uint32_t flags;
folder->GetFlags(&flags);
if (flags & nsMsgFolderFlags::Inbox) {
// This is inbox, so Compact() it. There's an implied
// Expunge too, because this is IMAP.
RefPtr<UrlListener> cleanupListener = new UrlListener();
RefPtr<nsMsgAccountManager> self = this;
// This runs when the compaction (+expunge) is complete.
cleanupListener->mStopFn = [self](nsIURI* url,
nsresult status) -> nsresult {
if (self->m_folderDoingCleanupInbox) {
PR_CEnterMonitor(self->m_folderDoingCleanupInbox);
PR_CNotifyAll(self->m_folderDoingCleanupInbox);
self->m_cleanupInboxInProgress = false;
PR_CExitMonitor(self->m_folderDoingCleanupInbox);
self->m_folderDoingCleanupInbox = nullptr;
}
return NS_OK;
};
rv = folder->Compact(cleanupListener, nullptr);
if (NS_SUCCEEDED(rv))
accountManager->SetFolderDoingCleanupInbox(folder);
break;
}
}
}
}
if (emptyTrashOnExit) {
RefPtr<UrlListener> emptyTrashListener = new UrlListener();
RefPtr<nsMsgAccountManager> self = this;
// This runs when the trash-emptying is complete.
// (It'll be a nsIImapUrl::nsImapDeleteAllMsgs url).
emptyTrashListener->mStopFn = [self](nsIURI* url,
nsresult status) -> nsresult {
if (self->m_folderDoingEmptyTrash) {
PR_CEnterMonitor(self->m_folderDoingEmptyTrash);
PR_CNotifyAll(self->m_folderDoingEmptyTrash);
self->m_emptyTrashInProgress = false;
PR_CExitMonitor(self->m_folderDoingEmptyTrash);
self->m_folderDoingEmptyTrash = nullptr;
}
return NS_OK;
};
rv = root->EmptyTrash(emptyTrashListener);
if (isImap && NS_SUCCEEDED(rv))
accountManager->SetFolderDoingEmptyTrash(root);
}
if (!isImap) {
continue;
}
nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
// Pause until any possible inbox-compaction and trash-emptying
// are complete (or time out).
bool inProgress = false;
if (cleanupInboxOnExit) {
int32_t loopCount = 0; // used to break out after 5 seconds
accountManager->GetCleanupInboxInProgress(&inProgress);
while (inProgress && loopCount++ < 5000) {
accountManager->GetCleanupInboxInProgress(&inProgress);
PR_CEnterMonitor(root);
PR_CWait(root, PR_MicrosecondsToInterval(1000UL));
PR_CExitMonitor(root);
NS_ProcessPendingEvents(thread, PR_MicrosecondsToInterval(1000UL));
}
}
if (emptyTrashOnExit) {
accountManager->GetEmptyTrashInProgress(&inProgress);
int32_t loopCount = 0;
while (inProgress && loopCount++ < 5000) {
accountManager->GetEmptyTrashInProgress(&inProgress);
PR_CEnterMonitor(root);
PR_CWait(root, PR_MicrosecondsToInterval(1000UL));
PR_CExitMonitor(root);
NS_ProcessPendingEvents(thread, PR_MicrosecondsToInterval(1000UL));
}
}
}
}
GetQuitApplicationGranted()->RemoveBlocker(this);
// Try to do this early on in the shutdown process before
// necko shuts itself down.
CloseCachedConnections();
return NS_OK;
}
// nsIAsyncShutdownBlocker implementation
NS_IMETHODIMP
nsMsgAccountManager::GetName(nsAString& aName) {
aName = u"nsMsgAccountManager: shutdown"_ns;
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::GetState(nsIPropertyBag** aState) { return NS_OK; }
NS_IMETHODIMP
nsMsgAccountManager::BlockShutdown(nsIAsyncShutdownClient* aClient) {
nsAutoString name;
aClient->GetName(name);
if (name.Equals(u"quit-application-granted"_ns)) {
return CleanupOnExit();
} else {
// profile-before-change
return Shutdown();
}
}
NS_IMETHODIMP
nsMsgAccountManager::WriteToFolderCache(nsIMsgFolderCache* folderCache) {
for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
iter.Data()->WriteToFolderCache(folderCache);
}
return NS_OK;
}
nsresult nsMsgAccountManager::createKeyedAccount(const nsCString& key,
bool forcePositionToEnd,
nsIMsgAccount** aAccount) {
nsresult rv;
nsCOMPtr<nsIMsgAccount> account = do_CreateInstance(kMsgAccountCID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
account->SetKey(key);
nsCString localFoldersAccountKey;
nsCString lastFolderAccountKey;
if (!forcePositionToEnd) {
nsCOMPtr<nsIMsgIncomingServer> localFoldersServer;
rv = GetLocalFoldersServer(getter_AddRefs(localFoldersServer));
if (NS_SUCCEEDED(rv)) {
for (auto account : m_accounts) {
nsCOMPtr<nsIMsgIncomingServer> server;
rv = account->GetIncomingServer(getter_AddRefs(server));
if (NS_SUCCEEDED(rv) && server == localFoldersServer) {
account->GetKey(localFoldersAccountKey);
break;
}
}
}
// Extracting the account key of the last mail acoount.
for (int32_t index = m_accounts.Length() - 1; index >= 0; index--) {
nsCOMPtr<nsIMsgIncomingServer> server;
rv = m_accounts[index]->GetIncomingServer(getter_AddRefs(server));
if (NS_SUCCEEDED(rv) && server) {
nsCString accountType;
rv = server->GetType(accountType);
if (NS_SUCCEEDED(rv) && !accountType.EqualsLiteral("im")) {
m_accounts[index]->GetKey(lastFolderAccountKey);
break;
}
}
}
}
if (!forcePositionToEnd && !localFoldersAccountKey.IsEmpty() &&
!lastFolderAccountKey.IsEmpty() &&
lastFolderAccountKey == localFoldersAccountKey) {
// Insert account before Local Folders if that is the last account.
m_accounts.InsertElementAt(m_accounts.Length() - 1, account);
} else {
m_accounts.AppendElement(account);
}
nsCString newAccountKeyList;
nsCString accountKey;
for (uint32_t index = 0; index < m_accounts.Length(); index++) {
m_accounts[index]->GetKey(accountKey);
if (index) newAccountKeyList.Append(ACCOUNT_DELIMITER);
newAccountKeyList.Append(accountKey);
}
mAccountKeyList = newAccountKeyList;
m_prefs->SetCharPref(PREF_MAIL_ACCOUNTMANAGER_ACCOUNTS, mAccountKeyList);
account.forget(aAccount);
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::CreateAccount(nsIMsgAccount** _retval) {
NS_ENSURE_ARG_POINTER(_retval);
nsAutoCString key;
GetUniqueAccountKey(key);
return createKeyedAccount(key, false, _retval);
}
NS_IMETHODIMP
nsMsgAccountManager::GetAccount(const nsACString& aKey,
nsIMsgAccount** aAccount) {
NS_ENSURE_ARG_POINTER(aAccount);
*aAccount = nullptr;
nsresult rv = LoadAccounts();
NS_ENSURE_SUCCESS(rv, rv);
for (uint32_t i = 0; i < m_accounts.Length(); ++i) {
nsCOMPtr<nsIMsgAccount> account(m_accounts[i]);
nsCString key;
account->GetKey(key);
if (key.Equals(aKey)) {
account.forget(aAccount);
break;
}
}
// If not found, create on demand.
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::FindServerIndex(nsIMsgIncomingServer* server,
int32_t* result) {
NS_ENSURE_ARG_POINTER(server);
NS_ENSURE_ARG_POINTER(result);
nsCString key;
nsresult rv = server->GetKey(key);
NS_ENSURE_SUCCESS(rv, rv);
// do this by account because the account list is in order
uint32_t i;
for (i = 0; i < m_accounts.Length(); ++i) {
nsCOMPtr<nsIMsgIncomingServer> server;
rv = m_accounts[i]->GetIncomingServer(getter_AddRefs(server));
if (!server || NS_FAILED(rv)) continue;
nsCString serverKey;
rv = server->GetKey(serverKey);
if (NS_FAILED(rv)) continue;
// stop when found,
// index will be set to the current index
if (serverKey.Equals(key)) break;
}
// Even if the search failed, we can return index.
// This means that all servers not in the array return an index higher
// than all "registered" servers.
*result = i;
return NS_OK;
}
NS_IMETHODIMP nsMsgAccountManager::AddIncomingServerListener(
nsIIncomingServerListener* serverListener) {
m_incomingServerListeners.AppendObject(serverListener);
return NS_OK;
}
NS_IMETHODIMP nsMsgAccountManager::RemoveIncomingServerListener(
nsIIncomingServerListener* serverListener) {
m_incomingServerListeners.RemoveObject(serverListener);
return NS_OK;
}
NS_IMETHODIMP nsMsgAccountManager::NotifyServerLoaded(
nsIMsgIncomingServer* server) {
int32_t count = m_incomingServerListeners.Count();
for (int32_t i = 0; i < count; i++) {
nsIIncomingServerListener* listener = m_incomingServerListeners[i];
listener->OnServerLoaded(server);
}
return NS_OK;
}
NS_IMETHODIMP nsMsgAccountManager::NotifyServerUnloaded(
nsIMsgIncomingServer* server) {
NS_ENSURE_ARG_POINTER(server);
int32_t count = m_incomingServerListeners.Count();
// Clear this to cut shutdown leaks. We are always passing valid non-null
// server here.
server->SetFilterList(nullptr);
for (int32_t i = 0; i < count; i++) {
nsIIncomingServerListener* listener = m_incomingServerListeners[i];
listener->OnServerUnloaded(server);
}
return NS_OK;
}
NS_IMETHODIMP nsMsgAccountManager::NotifyServerChanged(
nsIMsgIncomingServer* server) {
int32_t count = m_incomingServerListeners.Count();
for (int32_t i = 0; i < count; i++) {
nsIIncomingServerListener* listener = m_incomingServerListeners[i];
listener->OnServerChanged(server);
}
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::FindServerByURI(nsIURI* aURI,
nsIMsgIncomingServer** aResult) {
NS_ENSURE_ARG_POINTER(aURI);
nsresult rv = LoadAccounts();
NS_ENSURE_SUCCESS(rv, rv);
// Get username and hostname and port so we can get the server
nsAutoCString username;
nsAutoCString escapedUsername;
rv = aURI->GetUserPass(escapedUsername);
if (NS_SUCCEEDED(rv) && !escapedUsername.IsEmpty())
MsgUnescapeString(escapedUsername, 0, username);
nsAutoCString hostname;
nsAutoCString escapedHostname;
rv = aURI->GetHost(escapedHostname);
if (NS_SUCCEEDED(rv) && !escapedHostname.IsEmpty()) {
MsgUnescapeString(escapedHostname, 0, hostname);
}
nsAutoCString type;
rv = aURI->GetScheme(type);
if (NS_SUCCEEDED(rv) && !type.IsEmpty()) {
// Remove "-message" from the scheme in case we get called with
// "imap-message", "mailbox-message", or friends.
if (StringEndsWith(type, "-message"_ns)) type.SetLength(type.Length() - 8);
// now modify type if pop or news
if (type.EqualsLiteral("pop")) type.AssignLiteral("pop3");
// we use "nntp" in the server list so translate it here.
else if (type.EqualsLiteral("news"))
type.AssignLiteral("nntp");
// we use "any" as the wildcard type.
else if (type.EqualsLiteral("any"))
type.Truncate();
}
int32_t port = 0;
// check the port of the scheme is not 'none' or blank
if (!(type.EqualsLiteral("none") || type.IsEmpty())) {
rv = aURI->GetPort(&port);
// Set the port to zero if we got a -1 (use default)
if (NS_SUCCEEDED(rv) && (port == -1)) port = 0;
}
return findServerInternal(username, hostname, type, port, aResult);
}
nsresult nsMsgAccountManager::findServerInternal(
const nsACString& username, const nsACString& serverHostname,
const nsACString& type, int32_t port, nsIMsgIncomingServer** aResult) {
if ((m_lastFindServerUserName.Equals(username)) &&
(m_lastFindServerHostName.Equals(serverHostname)) &&
(m_lastFindServerType.Equals(type)) && (m_lastFindServerPort == port) &&
m_lastFindServerResult) {
NS_ADDREF(*aResult = m_lastFindServerResult);
return NS_OK;
}
nsresult rv;
nsCString hostname;
nsCOMPtr<nsIIDNService> idnService =
do_GetService("@mozilla.org/network/idn-service;1");
rv = idnService->ConvertToDisplayIDN(serverHostname, hostname);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURL> url;
rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
.SetSpec("imap://"_ns + hostname)
.Finalize(url);
if (NS_SUCCEEDED(rv)) {
rv = url->GetHost(hostname);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIIOService> ioService = mozilla::components::IO::Service();
NS_ENSURE_TRUE(ioService, NS_ERROR_UNEXPECTED);
for (auto iter = m_incomingServers.Iter(); !iter.Done(); iter.Next()) {
// Find matching server by user+host+type+port.
nsCOMPtr<nsIMsgIncomingServer>& server = iter.Data();
if (!server) continue;
nsCString thisHostname;
rv = server->GetHostName(thisHostname);
if (NS_FAILED(rv)) continue;
// URL mutation expects percent-escaping in the hostname, which
// `ConvertToDisplayIDN` will do for us.
nsCString normalizedHostname;
rv = idnService->ConvertToDisplayIDN(thisHostname, normalizedHostname);
if (NS_FAILED(rv)) continue;
// If the hostname will get normalized during URI mutation.
// E.g. for IP with trailing dot, or hostname that's just a number.
// We may well be here in findServerInternal to find a server from a folder
// URI. We need to use the normalized version to find the server.
// Create an imap url to see what it's normalized to. The normalization
// is the same for all protocols.
rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
.SetSpec("imap://"_ns + normalizedHostname)
.Finalize(url);
if (NS_SUCCEEDED(rv)) {
rv = url->GetHost(normalizedHostname);
if (NS_FAILED(rv)) continue;
}
nsCString thisUsername;
rv = server->GetUsername(thisUsername);
if (NS_FAILED(rv)) continue;
nsCString thisType;
rv = server->GetType(thisType);
if (NS_FAILED(rv)) continue;
int32_t thisPort = -1; // use the default port identifier
// Don't try and get a port for the 'none' scheme
if (!thisType.EqualsLiteral("none")) {
rv = server->GetPort(&thisPort);
if (NS_FAILED(rv)) {
continue;
}
}
// treat "" as a wild card, so if the caller passed in "" for the desired
// attribute treat it as a match
if ((type.IsEmpty() || thisType.Equals(type)) &&
(hostname.IsEmpty() ||
normalizedHostname.Equals(hostname,
nsCaseInsensitiveCStringComparator)) &&
(!(port != 0) || (port == thisPort)) &&
(username.IsEmpty() || thisUsername.Equals(username))) {
// stop on first find; cache for next time
SetLastServerFound(server, hostname, username, port, type);
NS_ADDREF(*aResult = server); // Was populated from member variable.
return NS_OK;
}
}
return NS_ERROR_UNEXPECTED;
}
// Always return NS_OK;
NS_IMETHODIMP
nsMsgAccountManager::FindServer(const nsACString& username,
const nsACString& hostname,
const nsACString& type, int32_t port,
nsIMsgIncomingServer** aResult) {
*aResult = nullptr;
findServerInternal(username, hostname, type, port, aResult);
return NS_OK;
}
void nsMsgAccountManager::findAccountByServerKey(const nsCString& aKey,
nsIMsgAccount** aResult) {
*aResult = nullptr;
for (uint32_t i = 0; i < m_accounts.Length(); ++i) {
nsCOMPtr<nsIMsgIncomingServer> server;
nsresult rv = m_accounts[i]->GetIncomingServer(getter_AddRefs(server));
if (!server || NS_FAILED(rv)) continue;
nsCString key;
rv = server->GetKey(key);
if (NS_FAILED(rv)) continue;
// if the keys are equal, the servers are equal
if (key.Equals(aKey)) {
NS_ADDREF(*aResult = m_accounts[i]);
break; // stop on first found account
}
}
}
NS_IMETHODIMP
nsMsgAccountManager::FindAccountForServer(nsIMsgIncomingServer* server,
nsIMsgAccount** aResult) {
NS_ENSURE_ARG_POINTER(aResult);
if (!server) {
(*aResult) = nullptr;
return NS_OK;
}
nsresult rv;
nsCString key;
rv = server->GetKey(key);
NS_ENSURE_SUCCESS(rv, rv);
findAccountByServerKey(key, aResult);
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::GetFirstIdentityForServer(nsIMsgIncomingServer* aServer,
nsIMsgIdentity** aIdentity) {
NS_ENSURE_ARG_POINTER(aServer);
NS_ENSURE_ARG_POINTER(aIdentity);
nsTArray<RefPtr<nsIMsgIdentity>> identities;
nsresult rv = GetIdentitiesForServer(aServer, identities);
NS_ENSURE_SUCCESS(rv, rv);
// not all servers have identities
// for example, Local Folders
if (identities.IsEmpty()) {
*aIdentity = nullptr;
} else {
NS_IF_ADDREF(*aIdentity = identities[0]);
}
return rv;
}
NS_IMETHODIMP
nsMsgAccountManager::GetIdentitiesForServer(
nsIMsgIncomingServer* server,
nsTArray<RefPtr<nsIMsgIdentity>>& identities) {
NS_ENSURE_ARG_POINTER(server);
nsresult rv = LoadAccounts();
NS_ENSURE_SUCCESS(rv, rv);
identities.Clear();
nsAutoCString serverKey;
rv = server->GetKey(serverKey);
NS_ENSURE_SUCCESS(rv, rv);
for (auto account : m_accounts) {
nsCOMPtr<nsIMsgIncomingServer> thisServer;
rv = account->GetIncomingServer(getter_AddRefs(thisServer));
if (NS_FAILED(rv) || !thisServer) continue;
nsAutoCString thisServerKey;
rv = thisServer->GetKey(thisServerKey);
if (serverKey.Equals(thisServerKey)) {
nsTArray<RefPtr<nsIMsgIdentity>> theseIdentities;
rv = account->GetIdentities(theseIdentities);
NS_ENSURE_SUCCESS(rv, rv);
identities.AppendElements(theseIdentities);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsMsgAccountManager::GetServersForIdentity(
nsIMsgIdentity* aIdentity,
nsTArray<RefPtr<nsIMsgIncomingServer>>& servers) {
NS_ENSURE_ARG_POINTER(aIdentity);
servers.Clear();
nsresult rv;
rv = LoadAccounts();
NS_ENSURE_SUCCESS(rv, rv);
for (auto account : m_accounts) {
nsTArray<RefPtr<nsIMsgIdentity>> identities;
if (NS_FAILED(account->GetIdentities(identities))) continue;
nsCString identityKey;
aIdentity->GetKey(identityKey);
for (auto thisIdentity : identities) {
nsCString thisIdentityKey;
rv = thisIdentity->GetKey(thisIdentityKey);