in mailnews/base/src/nsMsgAccountManager.cpp [1019:1374]
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;
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->GetStringValue("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->SetStringValue("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;
}