/* -*- 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 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsMsgIncomingServer.h" #include "nscore.h" #include "plstr.h" #include "prmem.h" #include "prprf.h" #include "nsIServiceManager.h" #include "nsCOMPtr.h" #include "nsString.h" #include "nsMemory.h" #include "nsISupportsPrimitives.h" #include "nsIMsgBiffManager.h" #include "nsIMsgFolder.h" #include "nsMsgDBFolder.h" #include "nsIMsgFolderCache.h" #include "nsIMsgPluggableStore.h" #include "nsIMsgFolderCacheElement.h" #include "nsIMsgWindow.h" #include "nsIMsgFilterService.h" #include "nsIMsgProtocolInfo.h" #include "nsIPrefService.h" #include "nsIRelativeFilePref.h" #include "mozilla/nsRelativeFilePref.h" #include "nsIDocShell.h" #include "nsIAuthPrompt.h" #include "nsNetUtil.h" #include "nsIWindowWatcher.h" #include "nsIStringBundle.h" #include "nsIMsgHdr.h" #include "nsIInterfaceRequestor.h" #include "nsIInterfaceRequestorUtils.h" #include "nsILoginInfo.h" #include "nsILoginManager.h" #include "nsIMsgAccountManager.h" #include "nsIMsgMdnGenerator.h" #include "nsMsgUtils.h" #include "nsMsgMessageFlags.h" #include "nsIMsgSearchTerm.h" #include "nsAppDirectoryServiceDefs.h" #include "mozilla/Components.h" #include "mozilla/Services.h" #include "nsIMsgFilter.h" #include "nsIObserverService.h" #include "mozilla/Unused.h" #include "nsIUUIDGenerator.h" #include "nsArrayUtils.h" #define PORT_NOT_SET -1 nsMsgIncomingServer::nsMsgIncomingServer() : m_rootFolder(nullptr), m_downloadedHdrs(50), m_numMsgsDownloaded(0), m_biffState(nsIMsgFolder::nsMsgBiffState_Unknown), m_serverBusy(false), m_canHaveFilters(true), m_displayStartupPage(true), mPerformingBiff(false) {} nsresult nsMsgIncomingServer::Init() { // We need to know when the password manager changes. nsCOMPtr observerService = mozilla::services::GetObserverService(); NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED); observerService->AddObserver(this, "passwordmgr-storage-changed", false); observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); return NS_OK; } nsMsgIncomingServer::~nsMsgIncomingServer() {} NS_IMPL_ISUPPORTS(nsMsgIncomingServer, nsIMsgIncomingServer, nsISupportsWeakReference, nsIObserver) /** * Observe() receives notifications for all accounts, not just this server's * account. So we ignore all notifications not intended for this server. * When the state of the password manager changes we need to clear the * this server's password from the cache in case the user just changed or * removed the password or username. * Oauth2 servers often automatically change the password manager's stored * password (the token). */ NS_IMETHODIMP nsMsgIncomingServer::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { nsresult rv; if (strcmp(aTopic, "passwordmgr-storage-changed") == 0) { nsAutoString otherFullName; nsAutoString otherUserName; // Check that the notification is for this server. nsCOMPtr loginInfo = do_QueryInterface(aSubject); if (loginInfo) { // The login info for this server has been removed with aData being // "removeLogin" or "removeAllLogins". loginInfo->GetOrigin(otherFullName); loginInfo->GetUsername(otherUserName); } else { // Probably a 2 element array containing old and new login info due to // aData being "modifyLogin". E.g., a user has modified password or // username in the password manager or an OAuth2 token string has // automatically changed. nsCOMPtr logins = do_QueryInterface(aSubject); if (logins) { // Only need to look at names in first array element (login info before // any modification) since the user might have changed the username as // found in the 2nd elements. (The hostname can't be modified in the // password manager.) nsCOMPtr login; logins->QueryElementAt(0, NS_GET_IID(nsILoginInfo), getter_AddRefs(login)); if (login) { login->GetOrigin(otherFullName); login->GetUsername(otherUserName); } } } if (!otherFullName.IsEmpty()) { nsAutoCString thisHostname; nsAutoCString thisUsername; GetHostName(thisHostname); GetUsername(thisUsername); nsAutoCString thisFullName; GetType(thisFullName); if (thisFullName.EqualsLiteral("pop3")) { // Note: POP3 now handled by MsgIncomingServer.jsm so does not occur. MOZ_ASSERT_UNREACHABLE("pop3 should not use nsMsgIncomingServer"); thisFullName = "mailbox://"_ns + thisHostname; } else { thisFullName += "://"_ns + thisHostname; } if (!thisFullName.Equals(NS_ConvertUTF16toUTF8(otherFullName)) || !thisUsername.Equals(NS_ConvertUTF16toUTF8(otherUserName))) { // Not for this server; keep this server's cached password. return NS_OK; } } else if (NS_strcmp(aData, u"hostSavingDisabled") != 0) { // "hostSavingDisabled" only occurs during test_smtpServer.js and // expects the password to be removed from memory cache. Otherwise, we // don't have enough information to decide to remove the cached // password, so keep it. return NS_OK; } // When nsMsgImapIncomingServer::ForgetSessionPassword called with // parameter modifyLogin true and if the server uses OAuth2, it causes the // password to not be cleared from cache. This is needed by autosync. When // the aData paremater of Observe() is not "modifyLogin" but is // e.g., "removeLogin" or "removeAllLogins", ForgetSessionPassword(false) // will still clear the cached password regardless of authentication method. rv = ForgetSessionPassword(NS_strcmp(aData, u"modifyLogin") == 0); NS_ENSURE_SUCCESS(rv, rv); } else if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { // Now remove ourselves from the observer service as well. nsCOMPtr observerService = mozilla::services::GetObserverService(); NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED); observerService->RemoveObserver(this, "passwordmgr-storage-changed"); observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); } return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetServerBusy(bool aServerBusy) { m_serverBusy = aServerBusy; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetServerBusy(bool* aServerBusy) { NS_ENSURE_ARG_POINTER(aServerBusy); *aServerBusy = m_serverBusy; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetKey(nsACString& serverKey) { serverKey = m_serverKey; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetKey(const nsACString& serverKey) { m_serverKey.Assign(serverKey); // in order to actually make use of the key, we need the prefs nsresult rv; nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString branchName; branchName.AssignLiteral("mail.server."); branchName.Append(m_serverKey); branchName.Append('.'); rv = prefs->GetBranch(branchName.get(), getter_AddRefs(mPrefBranch)); NS_ENSURE_SUCCESS(rv, rv); return prefs->GetBranch("mail.server.default.", getter_AddRefs(mDefPrefBranch)); } NS_IMETHODIMP nsMsgIncomingServer::GetUID(nsACString& uid) { bool hasValue; nsresult rv = mPrefBranch->PrefHasUserValue("uid", &hasValue); NS_ENSURE_SUCCESS(rv, rv); if (hasValue) { return GetCharValue("uid", uid); } nsCOMPtr uuidgen = mozilla::components::UUIDGenerator::Service(); NS_ENSURE_TRUE(uuidgen, NS_ERROR_FAILURE); nsID id; rv = uuidgen->GenerateUUIDInPlace(&id); NS_ENSURE_SUCCESS(rv, rv); char idString[NSID_LENGTH]; id.ToProvidedString(idString); uid.AppendASCII(idString + 1, NSID_LENGTH - 3); return SetUID(uid); } NS_IMETHODIMP nsMsgIncomingServer::SetUID(const nsACString& uid) { bool hasValue; nsresult rv = mPrefBranch->PrefHasUserValue("uid", &hasValue); NS_ENSURE_SUCCESS(rv, rv); if (hasValue) { return NS_ERROR_ABORT; } return SetCharValue("uid", uid); } NS_IMETHODIMP nsMsgIncomingServer::SetRootFolder(nsIMsgFolder* aRootFolder) { m_rootFolder = aRootFolder; return NS_OK; } // this will return the root folder of this account, // even if this server is deferred. NS_IMETHODIMP nsMsgIncomingServer::GetRootFolder(nsIMsgFolder** aRootFolder) { NS_ENSURE_ARG_POINTER(aRootFolder); if (!m_rootFolder) { nsresult rv = CreateRootFolder(); NS_ENSURE_SUCCESS(rv, rv); } NS_IF_ADDREF(*aRootFolder = m_rootFolder); return NS_OK; } // this will return the root folder of the deferred to account, // if this server is deferred. NS_IMETHODIMP nsMsgIncomingServer::GetRootMsgFolder(nsIMsgFolder** aRootMsgFolder) { return GetRootFolder(aRootMsgFolder); } NS_IMETHODIMP nsMsgIncomingServer::PerformExpand(nsIMsgWindow* aMsgWindow) { return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::VerifyLogon(nsIUrlListener* aUrlListener, nsIMsgWindow* aMsgWindow, nsIURI** aURL) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsMsgIncomingServer::PerformBiff(nsIMsgWindow* aMsgWindow) { // This has to be implemented in the derived class, but in case someone // doesn't implement it just return not implemented. return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsMsgIncomingServer::GetNewMessages(nsIMsgFolder* aFolder, nsIMsgWindow* aMsgWindow, nsIUrlListener* aUrlListener) { NS_ENSURE_ARG_POINTER(aFolder); return aFolder->GetNewMessages(aMsgWindow, aUrlListener); } NS_IMETHODIMP nsMsgIncomingServer::GetPerformingBiff(bool* aPerformingBiff) { NS_ENSURE_ARG_POINTER(aPerformingBiff); *aPerformingBiff = mPerformingBiff; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetPerformingBiff(bool aPerformingBiff) { mPerformingBiff = aPerformingBiff; return NS_OK; } NS_IMPL_GETSET(nsMsgIncomingServer, BiffState, uint32_t, m_biffState) NS_IMETHODIMP nsMsgIncomingServer::WriteToFolderCache( nsIMsgFolderCache* folderCache) { nsresult rv = NS_OK; if (m_rootFolder) { rv = m_rootFolder->WriteToFolderCache(folderCache, true /* deep */); } return rv; } NS_IMETHODIMP nsMsgIncomingServer::Shutdown() { nsresult rv = CloseCachedConnections(); mFilterPlugin = nullptr; NS_ENSURE_SUCCESS(rv, rv); if (mFilterList) { // close the filter log stream rv = mFilterList->SetLogStream(nullptr); NS_ENSURE_SUCCESS(rv, rv); mFilterList = nullptr; } if (mSpamSettings) { // close the spam log stream rv = mSpamSettings->SetLogStream(nullptr); NS_ENSURE_SUCCESS(rv, rv); mSpamSettings = nullptr; } return rv; } NS_IMETHODIMP nsMsgIncomingServer::CloseCachedConnections() { // derived class should override if they cache connections. return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetDownloadMessagesAtStartup(bool* getMessagesAtStartup) { // derived class should override if they need to do this. *getMessagesAtStartup = false; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetCanHaveFilters(bool* canHaveFilters) { NS_ENSURE_ARG_POINTER(canHaveFilters); *canHaveFilters = m_canHaveFilters; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetCanHaveFilters(bool aCanHaveFilters) { m_canHaveFilters = aCanHaveFilters; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetCanBeDefaultServer(bool* canBeDefaultServer) { // derived class should override if they need to do this. *canBeDefaultServer = false; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetCanSearchMessages(bool* canSearchMessages) { // derived class should override if they need to do this. NS_ENSURE_ARG_POINTER(canSearchMessages); *canSearchMessages = false; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetCanCompactFoldersOnServer( bool* canCompactFoldersOnServer) { // derived class should override if they need to do this. NS_ENSURE_ARG_POINTER(canCompactFoldersOnServer); *canCompactFoldersOnServer = true; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetCanUndoDeleteOnServer(bool* canUndoDeleteOnServer) { // derived class should override if they need to do this. NS_ENSURE_ARG_POINTER(canUndoDeleteOnServer); *canUndoDeleteOnServer = true; return NS_OK; } // construct ://[@] rootFolder; nsresult rv = GetRootFolder(getter_AddRefs(rootFolder)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr child; rv = rootFolder->GetChildNamed(folderName, getter_AddRefs(child)); if (child) return NS_OK; nsCOMPtr msgStore; rv = GetMsgStore(getter_AddRefs(msgStore)); NS_ENSURE_SUCCESS(rv, rv); return msgStore->CreateFolder(rootFolder, folderName, getter_AddRefs(child)); } nsresult nsMsgIncomingServer::CreateRootFolder() { nsresult rv; // get the URI from the incoming server nsCString serverUri; rv = GetServerURI(serverUri); NS_ENSURE_SUCCESS(rv, rv); rv = GetOrCreateFolder(serverUri, getter_AddRefs(m_rootFolder)); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetBoolValue(const char* prefname, bool* val) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; NS_ENSURE_ARG_POINTER(val); *val = false; if (NS_FAILED(mPrefBranch->GetBoolPref(prefname, val))) mDefPrefBranch->GetBoolPref(prefname, val); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetBoolValue(const char* prefname, bool val) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; bool defaultValue; nsresult rv = mDefPrefBranch->GetBoolPref(prefname, &defaultValue); if (NS_SUCCEEDED(rv) && val == defaultValue) mPrefBranch->ClearUserPref(prefname); else rv = mPrefBranch->SetBoolPref(prefname, val); return rv; } NS_IMETHODIMP nsMsgIncomingServer::GetIntValue(const char* prefname, int32_t* val) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; NS_ENSURE_ARG_POINTER(val); *val = 0; if (NS_FAILED(mPrefBranch->GetIntPref(prefname, val))) mDefPrefBranch->GetIntPref(prefname, val); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetFileValue(const char* aRelPrefName, const char* aAbsPrefName, nsIFile** aLocalFile) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; // Get the relative first nsCOMPtr relFilePref; nsresult rv = mPrefBranch->GetComplexValue(aRelPrefName, NS_GET_IID(nsIRelativeFilePref), getter_AddRefs(relFilePref)); if (relFilePref) { rv = relFilePref->GetFile(aLocalFile); NS_ASSERTION(*aLocalFile, "An nsIRelativeFilePref has no file."); if (NS_SUCCEEDED(rv)) (*aLocalFile)->Normalize(); } else { rv = mPrefBranch->GetComplexValue(aAbsPrefName, NS_GET_IID(nsIFile), reinterpret_cast(aLocalFile)); if (NS_FAILED(rv)) return rv; nsCOMPtr relFilePref = new mozilla::nsRelativeFilePref(); mozilla::Unused << relFilePref->SetFile(*aLocalFile); mozilla::Unused << relFilePref->SetRelativeToKey( nsLiteralCString(NS_APP_USER_PROFILE_50_DIR)); rv = mPrefBranch->SetComplexValue( aRelPrefName, NS_GET_IID(nsIRelativeFilePref), relFilePref); } return rv; } NS_IMETHODIMP nsMsgIncomingServer::SetFileValue(const char* aRelPrefName, const char* aAbsPrefName, nsIFile* aLocalFile) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; // Write the relative path. nsCOMPtr relFilePref = new mozilla::nsRelativeFilePref(); mozilla::Unused << relFilePref->SetFile(aLocalFile); mozilla::Unused << relFilePref->SetRelativeToKey( nsLiteralCString(NS_APP_USER_PROFILE_50_DIR)); nsresult rv = mPrefBranch->SetComplexValue( aRelPrefName, NS_GET_IID(nsIRelativeFilePref), relFilePref); if (NS_FAILED(rv)) return rv; return mPrefBranch->SetComplexValue(aAbsPrefName, NS_GET_IID(nsIFile), aLocalFile); } NS_IMETHODIMP nsMsgIncomingServer::SetIntValue(const char* prefname, int32_t val) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; int32_t defaultVal; nsresult rv = mDefPrefBranch->GetIntPref(prefname, &defaultVal); if (NS_SUCCEEDED(rv) && defaultVal == val) mPrefBranch->ClearUserPref(prefname); else rv = mPrefBranch->SetIntPref(prefname, val); return rv; } NS_IMETHODIMP nsMsgIncomingServer::GetCharValue(const char* prefname, nsACString& val) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; nsCString tmpVal; if (NS_FAILED(mPrefBranch->GetCharPref(prefname, tmpVal))) mDefPrefBranch->GetCharPref(prefname, tmpVal); val = tmpVal; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetUnicharValue(const char* prefname, nsAString& val) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; nsCString valueUtf8; if (NS_FAILED( mPrefBranch->GetStringPref(prefname, EmptyCString(), 0, valueUtf8))) mDefPrefBranch->GetStringPref(prefname, EmptyCString(), 0, valueUtf8); CopyUTF8toUTF16(valueUtf8, val); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetCharValue(const char* prefname, const nsACString& val) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; if (val.IsEmpty()) { mPrefBranch->ClearUserPref(prefname); return NS_OK; } nsCString defaultVal; nsresult rv = mDefPrefBranch->GetCharPref(prefname, defaultVal); if (NS_SUCCEEDED(rv) && defaultVal.Equals(val)) mPrefBranch->ClearUserPref(prefname); else rv = mPrefBranch->SetCharPref(prefname, val); return rv; } NS_IMETHODIMP nsMsgIncomingServer::SetUnicharValue(const char* prefname, const nsAString& val) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; if (val.IsEmpty()) { mPrefBranch->ClearUserPref(prefname); return NS_OK; } nsCString defaultVal; nsresult rv = mDefPrefBranch->GetStringPref(prefname, EmptyCString(), 0, defaultVal); if (NS_SUCCEEDED(rv) && defaultVal.Equals(NS_ConvertUTF16toUTF8(val))) mPrefBranch->ClearUserPref(prefname); else rv = mPrefBranch->SetStringPref(prefname, NS_ConvertUTF16toUTF8(val)); return rv; } // pretty name is the display name to show to the user NS_IMETHODIMP nsMsgIncomingServer::GetPrettyName(nsAString& retval) { nsresult rv = GetUnicharValue("name", retval); NS_ENSURE_SUCCESS(rv, rv); // if there's no name, then just return the hostname return retval.IsEmpty() ? GetConstructedPrettyName(retval) : rv; } NS_IMETHODIMP nsMsgIncomingServer::SetPrettyName(const nsAString& value) { SetUnicharValue("name", value); nsCOMPtr rootFolder; GetRootFolder(getter_AddRefs(rootFolder)); if (rootFolder) rootFolder->SetPrettyName(value); return NS_OK; } // construct the pretty name to show to the user if they haven't // specified one. This should be overridden for news and mail. NS_IMETHODIMP nsMsgIncomingServer::GetConstructedPrettyName(nsAString& retval) { nsCString username; nsresult rv = GetUsername(username); NS_ENSURE_SUCCESS(rv, rv); if (!username.IsEmpty()) { CopyASCIItoUTF16(username, retval); retval.AppendLiteral(" on "); } nsCString hostname; rv = GetHostName(hostname); NS_ENSURE_SUCCESS(rv, rv); retval.Append(NS_ConvertASCIItoUTF16(hostname)); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::ToString(nsAString& aResult) { aResult.AssignLiteral("[nsIMsgIncomingServer: "); aResult.Append(NS_ConvertASCIItoUTF16(m_serverKey)); aResult.Append(']'); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetPassword(const nsAString& aPassword) { m_password = aPassword; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetPassword(nsAString& aPassword) { aPassword = m_password; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetServerRequiresPasswordForBiff( bool* aServerRequiresPasswordForBiff) { NS_ENSURE_ARG_POINTER(aServerRequiresPasswordForBiff); *aServerRequiresPasswordForBiff = true; return NS_OK; } // This sets m_password if we find a password in the pw mgr. nsresult nsMsgIncomingServer::GetPasswordWithoutUI() { nsresult rv; nsCOMPtr loginMgr( do_GetService(NS_LOGINMANAGER_CONTRACTID, &rv)); NS_ENSURE_SUCCESS(rv, rv); // Get the current server URI nsCString currServerUri; rv = GetLocalStoreType(currServerUri); NS_ENSURE_SUCCESS(rv, rv); currServerUri.AppendLiteral("://"); nsCString temp; rv = GetHostName(temp); NS_ENSURE_SUCCESS(rv, rv); currServerUri.Append(temp); NS_ConvertUTF8toUTF16 currServer(currServerUri); nsTArray> logins; rv = loginMgr->FindLogins(currServer, EmptyString(), currServer, logins); // Login manager can produce valid fails, e.g. NS_ERROR_ABORT when a user // cancels the master password dialog. Therefore handle that here, but don't // warn about it. if (NS_FAILED(rv)) return rv; uint32_t numLogins = logins.Length(); // Don't abort here, if we didn't find any or failed, then we'll just have // to prompt. if (numLogins > 0) { nsCString serverCUsername; rv = GetUsername(serverCUsername); NS_ENSURE_SUCCESS(rv, rv); NS_ConvertUTF8toUTF16 serverUsername(serverCUsername); nsString username; for (uint32_t i = 0; i < numLogins; ++i) { rv = logins[i]->GetUsername(username); NS_ENSURE_SUCCESS(rv, rv); if (username.Equals(serverUsername)) { nsString password; rv = logins[i]->GetPassword(password); NS_ENSURE_SUCCESS(rv, rv); m_password = password; break; } } } return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetPasswordWithUI(const nsAString& aPromptMessage, const nsAString& aPromptTitle, nsAString& aPassword) { nsresult rv = NS_OK; if (m_password.IsEmpty()) { // let's see if we have the password in the password manager and // can avoid this prompting thing. This makes it easier to get embedders // to get up and running w/o a password prompting UI. rv = GetPasswordWithoutUI(); // If GetPasswordWithoutUI returns NS_ERROR_ABORT, the most likely case // is the user canceled getting the master password, so just return // straight away, as they won't want to get prompted again. if (rv == NS_ERROR_ABORT) return NS_MSG_PASSWORD_PROMPT_CANCELLED; } if (m_password.IsEmpty()) { nsCOMPtr authPrompt = do_GetService("@mozilla.org/messenger/msgAuthPrompt;1"); if (authPrompt) { // prompt the user for the password nsCString serverUri; rv = GetLocalStoreType(serverUri); NS_ENSURE_SUCCESS(rv, rv); serverUri.AppendLiteral("://"); nsCString temp; rv = GetUsername(temp); NS_ENSURE_SUCCESS(rv, rv); if (!temp.IsEmpty()) { nsCString escapedUsername; MsgEscapeString(temp, nsINetUtil::ESCAPE_XALPHAS, escapedUsername); serverUri.Append(escapedUsername); serverUri.Append('@'); } rv = GetHostName(temp); NS_ENSURE_SUCCESS(rv, rv); serverUri.Append(temp); // we pass in the previously used password, if any, into PromptPassword // so that it will appear as ******. This means we can't use an nsString // and getter_Copies. char16_t* uniPassword = nullptr; if (!aPassword.IsEmpty()) uniPassword = ToNewUnicode(aPassword); bool okayValue = true; rv = authPrompt->PromptPassword(PromiseFlatString(aPromptTitle).get(), PromiseFlatString(aPromptMessage).get(), NS_ConvertASCIItoUTF16(serverUri).get(), nsIAuthPrompt::SAVE_PASSWORD_PERMANENTLY, &uniPassword, &okayValue); NS_ENSURE_SUCCESS(rv, rv); if (!okayValue) // if the user pressed cancel, just return an empty // string; { aPassword.Truncate(); return NS_MSG_PASSWORD_PROMPT_CANCELLED; } // we got a password back...so remember it rv = SetPassword(nsDependentString(uniPassword)); NS_ENSURE_SUCCESS(rv, rv); PR_FREEIF(uniPassword); } // if we got a prompt dialog else return NS_ERROR_FAILURE; } // if the password is empty return GetPassword(aPassword); } NS_IMETHODIMP nsMsgIncomingServer::ForgetPassword() { nsresult rv; nsCOMPtr loginMgr = do_GetService(NS_LOGINMANAGER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); // Get the current server URI nsCString currServerUri; rv = GetLocalStoreType(currServerUri); NS_ENSURE_SUCCESS(rv, rv); currServerUri.AppendLiteral("://"); nsCString temp; rv = GetHostName(temp); NS_ENSURE_SUCCESS(rv, rv); currServerUri.Append(temp); NS_ConvertUTF8toUTF16 currServer(currServerUri); nsCString serverCUsername; rv = GetUsername(serverCUsername); NS_ENSURE_SUCCESS(rv, rv); NS_ConvertUTF8toUTF16 serverUsername(serverCUsername); nsTArray> logins; rv = loginMgr->FindLogins(currServer, EmptyString(), currServer, logins); NS_ENSURE_SUCCESS(rv, rv); // There should only be one-login stored for this url, however just in case // there isn't. nsString username; for (uint32_t i = 0; i < logins.Length(); ++i) { rv = logins[i]->GetUsername(username); int32_t atPos = serverUsername.FindChar('@'); if (NS_SUCCEEDED(rv) && (username.Equals(serverUsername) || StringHead(serverUsername, atPos).Equals(username))) { // If this fails, just continue, we'll still want to remove the password // from our local cache. loginMgr->RemoveLogin(logins[i]); } } return SetPassword(EmptyString()); } NS_IMETHODIMP nsMsgIncomingServer::ForgetSessionPassword(bool modifyLogin) { m_password.Truncate(); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetDefaultLocalPath(nsIFile* aDefaultLocalPath) { nsresult rv; nsCOMPtr protocolInfo; rv = GetProtocolInfo(getter_AddRefs(protocolInfo)); NS_ENSURE_SUCCESS(rv, rv); return protocolInfo->SetDefaultLocalPath(aDefaultLocalPath); } NS_IMETHODIMP nsMsgIncomingServer::GetLocalPath(nsIFile** aLocalPath) { nsresult rv; // if the local path has already been set, use it rv = GetFileValue("directory-rel", "directory", aLocalPath); if (NS_SUCCEEDED(rv) && *aLocalPath) return rv; // otherwise, create the path using the protocol info. // note we are using the // hostname, unless that directory exists. // this should prevent all collisions. nsCOMPtr protocolInfo; rv = GetProtocolInfo(getter_AddRefs(protocolInfo)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr localPath; rv = protocolInfo->GetDefaultLocalPath(getter_AddRefs(localPath)); NS_ENSURE_SUCCESS(rv, rv); rv = localPath->Create(nsIFile::DIRECTORY_TYPE, 0755); if (rv == NS_ERROR_FILE_ALREADY_EXISTS) rv = NS_OK; NS_ENSURE_SUCCESS(rv, rv); nsCString hostname; rv = GetHostName(hostname); NS_ENSURE_SUCCESS(rv, rv); // set the leaf name to "dummy", and then call MakeUnique with a suggested // leaf name rv = localPath->AppendNative(hostname); NS_ENSURE_SUCCESS(rv, rv); rv = localPath->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755); NS_ENSURE_SUCCESS(rv, rv); rv = SetLocalPath(localPath); NS_ENSURE_SUCCESS(rv, rv); localPath.forget(aLocalPath); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetMsgStore(nsIMsgPluggableStore** aMsgStore) { NS_ENSURE_ARG_POINTER(aMsgStore); if (!m_msgStore) { nsCString storeContractID; nsresult rv; // We don't want there to be a default pref, I think, since // we can't change the default. We may want no pref to mean // berkeley store, and then set the store pref off of some sort // of default when creating a server. But we need to make sure // that we do always write a store pref. GetCharValue("storeContractID", storeContractID); if (storeContractID.IsEmpty()) { storeContractID.AssignLiteral("@mozilla.org/msgstore/berkeleystore;1"); SetCharValue("storeContractID", storeContractID); } // After someone starts using the pluggable store, we can no longer // change the value. SetBoolValue("canChangeStoreType", false); // Right now, we just have one pluggable store per server. If we want // to support multiple, this pref could be a list of pluggable store // contract id's. m_msgStore = do_CreateInstance(storeContractID.get(), &rv); NS_ENSURE_SUCCESS(rv, rv); } NS_IF_ADDREF(*aMsgStore = m_msgStore); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetLocalPath(nsIFile* aLocalPath) { NS_ENSURE_ARG_POINTER(aLocalPath); nsresult rv = aLocalPath->Create(nsIFile::DIRECTORY_TYPE, 0755); if (rv == NS_ERROR_FILE_ALREADY_EXISTS) rv = NS_OK; NS_ENSURE_SUCCESS(rv, rv); return SetFileValue("directory-rel", "directory", aLocalPath); } NS_IMETHODIMP nsMsgIncomingServer::GetLocalStoreType(nsACString& aResult) { MOZ_ASSERT_UNREACHABLE( "nsMsgIncomingServer superclass not implementing GetLocalStoreType!"); return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP nsMsgIncomingServer::GetLocalDatabaseType(nsACString& aResult) { MOZ_ASSERT_UNREACHABLE( "nsMsgIncomingServer superclass not implementing GetLocalDatabaseType!"); return NS_ERROR_UNEXPECTED; } NS_IMETHODIMP nsMsgIncomingServer::GetAccountManagerChrome(nsAString& aResult) { aResult.AssignLiteral("am-main.xhtml"); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::Equals(nsIMsgIncomingServer* server, bool* _retval) { nsresult rv; NS_ENSURE_ARG_POINTER(server); NS_ENSURE_ARG_POINTER(_retval); nsCString key1; nsCString key2; rv = GetKey(key1); NS_ENSURE_SUCCESS(rv, rv); rv = server->GetKey(key2); NS_ENSURE_SUCCESS(rv, rv); // compare the server keys *_retval = key1.Equals(key2, nsCaseInsensitiveCStringComparator); return rv; } NS_IMETHODIMP nsMsgIncomingServer::ClearAllValues() { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; nsTArray prefNames; nsresult rv = mPrefBranch->GetChildList("", prefNames); NS_ENSURE_SUCCESS(rv, rv); for (auto& prefName : prefNames) { mPrefBranch->ClearUserPref(prefName.get()); } return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::RemoveFiles() { // IMPORTANT, see bug #77652 // TODO: Decide what to do for deferred accounts. nsCString deferredToAccount; GetCharValue("deferred_to_account", deferredToAccount); bool isDeferredTo = true; GetIsDeferredTo(&isDeferredTo); if (!deferredToAccount.IsEmpty() || isDeferredTo) { NS_ASSERTION(false, "shouldn't remove files for a deferred account"); return NS_ERROR_FAILURE; } nsCOMPtr localPath; nsresult rv = GetLocalPath(getter_AddRefs(localPath)); NS_ENSURE_SUCCESS(rv, rv); return localPath->Remove(true); } NS_IMETHODIMP nsMsgIncomingServer::SetFilterList(nsIMsgFilterList* aFilterList) { mFilterList = aFilterList; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetFilterList(nsIMsgWindow* aMsgWindow, nsIMsgFilterList** aResult) { NS_ENSURE_ARG_POINTER(aResult); if (!mFilterList) { nsCOMPtr msgFolder; // use GetRootFolder so for deferred pop3 accounts, we'll get the filters // file from the deferred account, not the deferred to account, // so that filters will still be per-server. nsresult rv = GetRootFolder(getter_AddRefs(msgFolder)); NS_ENSURE_SUCCESS(rv, rv); nsCString filterType; rv = GetCharValue("filter.type", filterType); NS_ENSURE_SUCCESS(rv, rv); if (!filterType.IsEmpty() && !filterType.EqualsLiteral("default")) { nsAutoCString contractID("@mozilla.org/filterlist;1?type="); contractID += filterType; ToLowerCase(contractID); mFilterList = do_CreateInstance(contractID.get(), &rv); NS_ENSURE_SUCCESS(rv, rv); rv = mFilterList->SetFolder(msgFolder); NS_ENSURE_SUCCESS(rv, rv); NS_ADDREF(*aResult = mFilterList); return NS_OK; } // The default case, a local folder, is a bit special. It requires // more initialization. nsCOMPtr thisFolder; rv = msgFolder->GetFilePath(getter_AddRefs(thisFolder)); NS_ENSURE_SUCCESS(rv, rv); mFilterFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = mFilterFile->InitWithFile(thisFolder); NS_ENSURE_SUCCESS(rv, rv); mFilterFile->AppendNative("msgFilterRules.dat"_ns); bool fileExists; mFilterFile->Exists(&fileExists); if (!fileExists) { nsCOMPtr oldFilterFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = oldFilterFile->InitWithFile(thisFolder); NS_ENSURE_SUCCESS(rv, rv); oldFilterFile->AppendNative("rules.dat"_ns); oldFilterFile->Exists(&fileExists); if (fileExists) // copy rules.dat --> msgFilterRules.dat { rv = oldFilterFile->CopyToNative(thisFolder, "msgFilterRules.dat"_ns); NS_ENSURE_SUCCESS(rv, rv); } } nsCOMPtr filterService = do_GetService("@mozilla.org/messenger/services/filters;1", &rv); NS_ENSURE_SUCCESS(rv, rv); rv = filterService->OpenFilterList(mFilterFile, msgFolder, aMsgWindow, getter_AddRefs(mFilterList)); NS_ENSURE_SUCCESS(rv, rv); } NS_IF_ADDREF(*aResult = mFilterList); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetEditableFilterList( nsIMsgFilterList* aEditableFilterList) { mEditableFilterList = aEditableFilterList; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetEditableFilterList(nsIMsgWindow* aMsgWindow, nsIMsgFilterList** aResult) { NS_ENSURE_ARG_POINTER(aResult); if (!mEditableFilterList) { bool editSeparate; nsresult rv = GetBoolValue("filter.editable.separate", &editSeparate); if (NS_FAILED(rv) || !editSeparate) return GetFilterList(aMsgWindow, aResult); nsCString filterType; rv = GetCharValue("filter.editable.type", filterType); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString contractID("@mozilla.org/filterlist;1?type="); contractID += filterType; ToLowerCase(contractID); mEditableFilterList = do_CreateInstance(contractID.get(), &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr msgFolder; // use GetRootFolder so for deferred pop3 accounts, we'll get the filters // file from the deferred account, not the deferred to account, // so that filters will still be per-server. rv = GetRootFolder(getter_AddRefs(msgFolder)); NS_ENSURE_SUCCESS(rv, rv); rv = mEditableFilterList->SetFolder(msgFolder); NS_ENSURE_SUCCESS(rv, rv); NS_ADDREF(*aResult = mEditableFilterList); return NS_OK; } NS_IF_ADDREF(*aResult = mEditableFilterList); return NS_OK; } // If the hostname contains ':' (like hostname:1431) // then parse and set the port number. nsresult nsMsgIncomingServer::InternalSetHostName(const nsACString& aHostname, const char* prefName) { nsCString hostname; hostname = aHostname; if (hostname.CountChar(':') == 1) { int32_t colonPos = hostname.FindChar(':'); nsAutoCString portString(Substring(hostname, colonPos)); hostname.SetLength(colonPos); nsresult err; int32_t port = portString.ToInteger(&err); if (NS_SUCCEEDED(err)) SetPort(port); } return SetCharValue(prefName, hostname); } NS_IMETHODIMP nsMsgIncomingServer::OnUserOrHostNameChanged(const nsACString& oldName, const nsACString& newName, bool hostnameChanged) { nsresult rv; // 1. Reset password so that users are prompted for new password for the new // user/host. int32_t atPos = newName.FindChar('@'); if (hostnameChanged) { ForgetPassword(); } // 2. Replace all occurrences of old name in the acct name with the new one. nsString acctName; rv = GetPrettyName(acctName); NS_ENSURE_SUCCESS(rv, rv); // 3. Clear the clientid because the user or host have changed. SetClientid(EmptyCString()); // Will be generated again when used. mPrefBranch->ClearUserPref("spamActionTargetAccount"); // If new username contains @ then better do not update the account name. if (acctName.IsEmpty() || (!hostnameChanged && (atPos != kNotFound))) return NS_OK; atPos = acctName.FindChar('@'); // get previous username and hostname nsCString userName, hostName; if (hostnameChanged) { rv = GetUsername(userName); NS_ENSURE_SUCCESS(rv, rv); hostName.Assign(oldName); } else { userName.Assign(oldName); rv = GetHostName(hostName); NS_ENSURE_SUCCESS(rv, rv); } // switch corresponding part of the account name to the new name... if (!hostnameChanged && (atPos != kNotFound)) { // ...if username changed and the previous username was equal to the part // of the account name before @ if (StringHead(acctName, atPos).Equals(NS_ConvertASCIItoUTF16(userName))) acctName.Replace(0, userName.Length(), NS_ConvertASCIItoUTF16(newName)); } if (hostnameChanged) { // ...if hostname changed and the previous hostname was equal to the part // of the account name after @, or to the whole account name if (atPos == kNotFound) atPos = 0; else atPos += 1; if (Substring(acctName, atPos).Equals(NS_ConvertASCIItoUTF16(hostName))) { acctName.Replace(atPos, acctName.Length() - atPos, NS_ConvertASCIItoUTF16(newName)); } } return SetPrettyName(acctName); } NS_IMETHODIMP nsMsgIncomingServer::SetHostName(const nsACString& aHostname) { nsCString oldName; nsresult rv = GetHostName(oldName); NS_ENSURE_SUCCESS(rv, rv); rv = InternalSetHostName(aHostname, "hostname"); if (!oldName.IsEmpty() && !aHostname.Equals(oldName, nsCaseInsensitiveCStringComparator)) rv = OnUserOrHostNameChanged(oldName, aHostname, true); return rv; } NS_IMETHODIMP nsMsgIncomingServer::GetHostName(nsACString& aResult) { nsresult rv = GetCharValue("hostname", aResult); if (aResult.CountChar(':') == 1) { // gack, we need to reformat the hostname - SetHostName will do that SetHostName(aResult); rv = GetCharValue("hostname", aResult); } return rv; } NS_IMETHODIMP nsMsgIncomingServer::SetUsername(const nsACString& aUsername) { nsCString oldName; nsresult rv = GetUsername(oldName); NS_ENSURE_SUCCESS(rv, rv); if (!oldName.IsEmpty() && !oldName.Equals(aUsername)) { // If only username changed and the new name just added a domain we can keep // the password. int32_t atPos = aUsername.FindChar('@'); if ((atPos == kNotFound) || !StringHead(NS_ConvertASCIItoUTF16(aUsername), atPos) .Equals(NS_ConvertASCIItoUTF16(oldName))) { ForgetPassword(); } rv = SetCharValue("userName", aUsername); NS_ENSURE_SUCCESS(rv, rv); rv = OnUserOrHostNameChanged(oldName, aUsername, false); } else { rv = SetCharValue("userName", aUsername); } return rv; } NS_IMETHODIMP nsMsgIncomingServer::GetUsername(nsACString& aResult) { return GetCharValue("userName", aResult); } #define BIFF_PREF_NAME "check_new_mail" NS_IMETHODIMP nsMsgIncomingServer::GetDoBiff(bool* aDoBiff) { NS_ENSURE_ARG_POINTER(aDoBiff); if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; nsresult rv; rv = mPrefBranch->GetBoolPref(BIFF_PREF_NAME, aDoBiff); if (NS_SUCCEEDED(rv)) return rv; // if the pref isn't set, use the default // value based on the protocol nsCOMPtr protocolInfo; rv = GetProtocolInfo(getter_AddRefs(protocolInfo)); NS_ENSURE_SUCCESS(rv, rv); rv = protocolInfo->GetDefaultDoBiff(aDoBiff); // note, don't call SetDoBiff() // since we keep changing our minds on // if biff should be on or off, let's keep the ability // to change the default in future builds. // if we call SetDoBiff() here, it will be in the users prefs. // and we can't do anything after that. return rv; } NS_IMETHODIMP nsMsgIncomingServer::SetDoBiff(bool aDoBiff) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; // Update biffManager immediately, no restart required. Adding/removing // existing/non-existing server is handled without error checking. nsresult rv; nsCOMPtr biffService = do_GetService("@mozilla.org/messenger/biffManager;1", &rv); if (NS_SUCCEEDED(rv) && biffService) { if (aDoBiff) (void)biffService->AddServerBiff(this); else (void)biffService->RemoveServerBiff(this); } return mPrefBranch->SetBoolPref(BIFF_PREF_NAME, aDoBiff); } NS_IMETHODIMP nsMsgIncomingServer::GetPort(int32_t* aPort) { NS_ENSURE_ARG_POINTER(aPort); nsresult rv; rv = GetIntValue("port", aPort); // We can't use a port of 0, because the URI parsing code fails. if (*aPort != PORT_NOT_SET && *aPort) return rv; // if the port isn't set, use the default // port based on the protocol nsCOMPtr protocolInfo; rv = GetProtocolInfo(getter_AddRefs(protocolInfo)); NS_ENSURE_SUCCESS(rv, rv); int32_t socketType; rv = GetSocketType(&socketType); NS_ENSURE_SUCCESS(rv, rv); bool useSSLPort = (socketType == nsMsgSocketType::SSL); return protocolInfo->GetDefaultServerPort(useSSLPort, aPort); } NS_IMETHODIMP nsMsgIncomingServer::SetPort(int32_t aPort) { nsresult rv; nsCOMPtr protocolInfo; rv = GetProtocolInfo(getter_AddRefs(protocolInfo)); NS_ENSURE_SUCCESS(rv, rv); int32_t socketType; rv = GetSocketType(&socketType); NS_ENSURE_SUCCESS(rv, rv); bool useSSLPort = (socketType == nsMsgSocketType::SSL); int32_t defaultPort; protocolInfo->GetDefaultServerPort(useSSLPort, &defaultPort); return SetIntValue("port", aPort == defaultPort ? PORT_NOT_SET : aPort); } NS_IMETHODIMP nsMsgIncomingServer::GetProtocolInfo(nsIMsgProtocolInfo** aResult) { NS_ENSURE_ARG_POINTER(aResult); nsCString type; nsresult rv = GetType(type); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString contractid(NS_MSGPROTOCOLINFO_CONTRACTID_PREFIX); contractid.Append(type); nsCOMPtr protocolInfo = do_GetService(contractid.get(), &rv); NS_ENSURE_SUCCESS(rv, rv); protocolInfo.forget(aResult); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetRetentionSettings( nsIMsgRetentionSettings** settings) { NS_ENSURE_ARG_POINTER(settings); nsMsgRetainByPreference retainByPreference; int32_t daysToKeepHdrs = 0; int32_t numHeadersToKeep = 0; int32_t daysToKeepBodies = 0; bool cleanupBodiesByDays = false; bool applyToFlaggedMessages = false; nsresult rv = NS_OK; // Create an empty retention settings object, // get the settings from the server prefs, and init the object from the prefs. nsCOMPtr retentionSettings = do_CreateInstance("@mozilla.org/msgDatabase/retentionSettings;1"); if (retentionSettings) { rv = GetIntValue("retainBy", (int32_t*)&retainByPreference); NS_ENSURE_SUCCESS(rv, rv); rv = GetIntValue("numHdrsToKeep", &numHeadersToKeep); NS_ENSURE_SUCCESS(rv, rv); rv = GetIntValue("daysToKeepHdrs", &daysToKeepHdrs); NS_ENSURE_SUCCESS(rv, rv); rv = GetIntValue("daysToKeepBodies", &daysToKeepBodies); NS_ENSURE_SUCCESS(rv, rv); rv = GetBoolValue("cleanupBodies", &cleanupBodiesByDays); NS_ENSURE_SUCCESS(rv, rv); rv = GetBoolValue("applyToFlaggedMessages", &applyToFlaggedMessages); NS_ENSURE_SUCCESS(rv, rv); retentionSettings->SetRetainByPreference(retainByPreference); retentionSettings->SetNumHeadersToKeep((uint32_t)numHeadersToKeep); retentionSettings->SetDaysToKeepBodies(daysToKeepBodies); retentionSettings->SetDaysToKeepHdrs(daysToKeepHdrs); retentionSettings->SetCleanupBodiesByDays(cleanupBodiesByDays); retentionSettings->SetApplyToFlaggedMessages(applyToFlaggedMessages); } else rv = NS_ERROR_OUT_OF_MEMORY; NS_IF_ADDREF(*settings = retentionSettings); return rv; } NS_IMETHODIMP nsMsgIncomingServer::SetRetentionSettings( nsIMsgRetentionSettings* settings) { nsMsgRetainByPreference retainByPreference; uint32_t daysToKeepHdrs = 0; uint32_t numHeadersToKeep = 0; uint32_t daysToKeepBodies = 0; bool cleanupBodiesByDays = false; bool applyToFlaggedMessages = false; settings->GetRetainByPreference(&retainByPreference); settings->GetNumHeadersToKeep(&numHeadersToKeep); settings->GetDaysToKeepBodies(&daysToKeepBodies); settings->GetDaysToKeepHdrs(&daysToKeepHdrs); settings->GetCleanupBodiesByDays(&cleanupBodiesByDays); settings->GetApplyToFlaggedMessages(&applyToFlaggedMessages); nsresult rv = SetIntValue("retainBy", retainByPreference); NS_ENSURE_SUCCESS(rv, rv); rv = SetIntValue("numHdrsToKeep", numHeadersToKeep); NS_ENSURE_SUCCESS(rv, rv); rv = SetIntValue("daysToKeepHdrs", daysToKeepHdrs); NS_ENSURE_SUCCESS(rv, rv); rv = SetIntValue("daysToKeepBodies", daysToKeepBodies); NS_ENSURE_SUCCESS(rv, rv); rv = SetBoolValue("cleanupBodies", cleanupBodiesByDays); NS_ENSURE_SUCCESS(rv, rv); rv = SetBoolValue("applyToFlaggedMessages", applyToFlaggedMessages); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetDisplayStartupPage(bool* displayStartupPage) { NS_ENSURE_ARG_POINTER(displayStartupPage); *displayStartupPage = m_displayStartupPage; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetDisplayStartupPage(bool displayStartupPage) { m_displayStartupPage = displayStartupPage; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetDownloadSettings( nsIMsgDownloadSettings** settings) { NS_ENSURE_ARG_POINTER(settings); bool downloadUnreadOnly = false; bool downloadByDate = false; uint32_t ageLimitOfMsgsToDownload = 0; nsresult rv = NS_OK; if (!m_downloadSettings) { m_downloadSettings = do_CreateInstance("@mozilla.org/msgDatabase/downloadSettings;1"); if (m_downloadSettings) { rv = GetBoolValue("downloadUnreadOnly", &downloadUnreadOnly); rv = GetBoolValue("downloadByDate", &downloadByDate); rv = GetIntValue("ageLimit", (int32_t*)&ageLimitOfMsgsToDownload); m_downloadSettings->SetDownloadUnreadOnly(downloadUnreadOnly); m_downloadSettings->SetDownloadByDate(downloadByDate); m_downloadSettings->SetAgeLimitOfMsgsToDownload(ageLimitOfMsgsToDownload); } else rv = NS_ERROR_OUT_OF_MEMORY; // Create an empty download settings object, // get the settings from the server prefs, and init the object from the // prefs. } NS_IF_ADDREF(*settings = m_downloadSettings); return rv; } NS_IMETHODIMP nsMsgIncomingServer::SetDownloadSettings( nsIMsgDownloadSettings* settings) { m_downloadSettings = settings; bool downloadUnreadOnly = false; bool downloadByDate = false; uint32_t ageLimitOfMsgsToDownload = 0; m_downloadSettings->GetDownloadUnreadOnly(&downloadUnreadOnly); m_downloadSettings->GetDownloadByDate(&downloadByDate); m_downloadSettings->GetAgeLimitOfMsgsToDownload(&ageLimitOfMsgsToDownload); nsresult rv = SetBoolValue("downloadUnreadOnly", downloadUnreadOnly); NS_ENSURE_SUCCESS(rv, rv); SetBoolValue("downloadByDate", downloadByDate); return SetIntValue("ageLimit", ageLimitOfMsgsToDownload); } NS_IMETHODIMP nsMsgIncomingServer::GetSupportsDiskSpace(bool* aSupportsDiskSpace) { NS_ENSURE_ARG_POINTER(aSupportsDiskSpace); *aSupportsDiskSpace = true; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetOfflineSupportLevel(int32_t* aSupportLevel) { NS_ENSURE_ARG_POINTER(aSupportLevel); nsresult rv = GetIntValue("offline_support_level", aSupportLevel); NS_ENSURE_SUCCESS(rv, rv); if (*aSupportLevel == OFFLINE_SUPPORT_LEVEL_UNDEFINED) *aSupportLevel = OFFLINE_SUPPORT_LEVEL_NONE; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetOfflineSupportLevel(int32_t aSupportLevel) { SetIntValue("offline_support_level", aSupportLevel); return NS_OK; } // Called only during the migration process. A unique name is generated for the // migrated account. NS_IMETHODIMP nsMsgIncomingServer::GeneratePrettyNameForMigration(nsAString& aPrettyName) { /** * 4.x had provisions for multiple imap servers to be maintained under * single identity. So, when migrated each of those server accounts need * to be represented by unique account name. nsImapIncomingServer will * override the implementation for this to do the right thing. */ return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsMsgIncomingServer::GetFilterScope(nsMsgSearchScopeValue* filterScope) { NS_ENSURE_ARG_POINTER(filterScope); *filterScope = nsMsgSearchScope::offlineMailFilter; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetSearchScope(nsMsgSearchScopeValue* searchScope) { NS_ENSURE_ARG_POINTER(searchScope); *searchScope = nsMsgSearchScope::offlineMail; return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetIsSecure(bool* aIsSecure) { NS_ENSURE_ARG_POINTER(aIsSecure); int32_t socketType; nsresult rv = GetSocketType(&socketType); NS_ENSURE_SUCCESS(rv, rv); *aIsSecure = (socketType == nsMsgSocketType::alwaysSTARTTLS || socketType == nsMsgSocketType::SSL); return NS_OK; } // use the convenience macros to implement the accessors NS_IMPL_SERVERPREF_INT(nsMsgIncomingServer, AuthMethod, "authMethod") NS_IMPL_SERVERPREF_INT(nsMsgIncomingServer, BiffMinutes, "check_time") NS_IMPL_SERVERPREF_STR(nsMsgIncomingServer, Type, "type") NS_IMPL_SERVERPREF_STR(nsMsgIncomingServer, Clientid, "clientid") NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer, ClientidEnabled, "clientidEnabled") NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer, DownloadOnBiff, "download_on_biff") NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer, Valid, "valid") NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer, EmptyTrashOnExit, "empty_trash_on_exit") NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer, CanDelete, "canDelete") NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer, LoginAtStartUp, "login_at_startup") NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer, DefaultCopiesAndFoldersPrefsToServer, "allows_specialfolders_usage") NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer, CanCreateFoldersOnServer, "canCreateFolders") NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer, CanFileMessagesOnServer, "canFileMessages") NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer, LimitOfflineMessageSize, "limit_offline_message_size") NS_IMPL_SERVERPREF_INT(nsMsgIncomingServer, MaxMessageSize, "max_size") NS_IMPL_SERVERPREF_INT(nsMsgIncomingServer, IncomingDuplicateAction, "dup_action") NS_IMPL_SERVERPREF_BOOL(nsMsgIncomingServer, Hidden, "hidden") NS_IMETHODIMP nsMsgIncomingServer::GetSocketType(int32_t* aSocketType) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; nsresult rv = mPrefBranch->GetIntPref("socketType", aSocketType); // socketType is set to default value. Look at isSecure setting if (NS_FAILED(rv)) { bool isSecure; rv = mPrefBranch->GetBoolPref("isSecure", &isSecure); if (NS_SUCCEEDED(rv) && isSecure) { *aSocketType = nsMsgSocketType::SSL; // don't call virtual method in case overrides call GetSocketType nsMsgIncomingServer::SetSocketType(*aSocketType); } else { if (!mDefPrefBranch) return NS_ERROR_NOT_INITIALIZED; rv = mDefPrefBranch->GetIntPref("socketType", aSocketType); if (NS_FAILED(rv)) *aSocketType = nsMsgSocketType::plain; } } return rv; } NS_IMETHODIMP nsMsgIncomingServer::SetSocketType(int32_t aSocketType) { if (!mPrefBranch) return NS_ERROR_NOT_INITIALIZED; int32_t socketType = nsMsgSocketType::plain; mPrefBranch->GetIntPref("socketType", &socketType); nsresult rv = mPrefBranch->SetIntPref("socketType", aSocketType); NS_ENSURE_SUCCESS(rv, rv); bool isSecureOld = (socketType == nsMsgSocketType::alwaysSTARTTLS || socketType == nsMsgSocketType::SSL); bool isSecureNew = (aSocketType == nsMsgSocketType::alwaysSTARTTLS || aSocketType == nsMsgSocketType::SSL); if ((isSecureOld != isSecureNew) && m_rootFolder) { m_rootFolder->NotifyBoolPropertyChanged(kIsSecure, isSecureOld, isSecureNew); } return NS_OK; } // Check if the password is available and return a boolean indicating whether // it is being authenticated or not. NS_IMETHODIMP nsMsgIncomingServer::GetPasswordPromptRequired(bool* aPasswordIsRequired) { NS_ENSURE_ARG_POINTER(aPasswordIsRequired); *aPasswordIsRequired = true; // If the password is not even required for biff we don't need to check any // further nsresult rv = GetServerRequiresPasswordForBiff(aPasswordIsRequired); NS_ENSURE_SUCCESS(rv, rv); if (!*aPasswordIsRequired) return NS_OK; // If the password is empty, check to see if it is stored and to be retrieved if (m_password.IsEmpty()) (void)GetPasswordWithoutUI(); *aPasswordIsRequired = m_password.IsEmpty(); if (*aPasswordIsRequired) { // Set *aPasswordIsRequired false if authMethod is oauth2. int32_t authMethod = 0; rv = GetAuthMethod(&authMethod); if (NS_SUCCEEDED(rv) && authMethod == nsMsgAuthMethod::OAuth2) { *aPasswordIsRequired = false; } } return rv; } NS_IMETHODIMP nsMsgIncomingServer::ConfigureTemporaryFilters( nsIMsgFilterList* aFilterList) { nsresult rv = ConfigureTemporaryReturnReceiptsFilter(aFilterList); if (NS_FAILED(rv)) // shut up warnings... return rv; return ConfigureTemporaryServerSpamFilters(aFilterList); } nsresult nsMsgIncomingServer::ConfigureTemporaryServerSpamFilters( nsIMsgFilterList* filterList) { nsCOMPtr spamSettings; nsresult rv = GetSpamSettings(getter_AddRefs(spamSettings)); NS_ENSURE_SUCCESS(rv, rv); bool useServerFilter; rv = spamSettings->GetUseServerFilter(&useServerFilter); NS_ENSURE_SUCCESS(rv, rv); // if we aren't configured to use server filters, then return early. if (!useServerFilter) return NS_OK; // For performance reasons, we'll handle clearing of filters if the user turns // off the server-side filters from the junk mail controls, in the junk mail // controls. nsAutoCString serverFilterName; spamSettings->GetServerFilterName(serverFilterName); if (serverFilterName.IsEmpty()) return NS_OK; int32_t serverFilterTrustFlags = 0; (void)spamSettings->GetServerFilterTrustFlags(&serverFilterTrustFlags); if (!serverFilterTrustFlags) return NS_OK; // check if filters have been setup already. nsAutoString yesFilterName, noFilterName; CopyASCIItoUTF16(serverFilterName, yesFilterName); yesFilterName.AppendLiteral("Yes"); CopyASCIItoUTF16(serverFilterName, noFilterName); noFilterName.AppendLiteral("No"); nsCOMPtr newFilter; (void)filterList->GetFilterNamed(yesFilterName, getter_AddRefs(newFilter)); if (!newFilter) (void)filterList->GetFilterNamed(noFilterName, getter_AddRefs(newFilter)); if (newFilter) return NS_OK; nsCOMPtr file; spamSettings->GetServerFilterFile(getter_AddRefs(file)); // it's possible that we can no longer find the sfd file (i.e. the user // disabled an extnsion that was supplying the .sfd file. if (!file) return NS_OK; nsCOMPtr filterService = do_GetService("@mozilla.org/messenger/services/filters;1", &rv); nsCOMPtr serverFilterList; rv = filterService->OpenFilterList(file, NULL, NULL, getter_AddRefs(serverFilterList)); NS_ENSURE_SUCCESS(rv, rv); rv = serverFilterList->GetFilterNamed(yesFilterName, getter_AddRefs(newFilter)); if (newFilter && serverFilterTrustFlags & nsISpamSettings::TRUST_POSITIVES) { newFilter->SetTemporary(true); // check if we're supposed to move junk mail to junk folder; if so, // add filter action to do so. /* * We don't want this filter to activate on messages that have * been marked by the user as not spam. This occurs when messages that * were marked as good are moved back into the inbox. But to * do this with a filter, we have to add a boolean term. That requires * that we rewrite the existing filter search terms to group them. */ // get the list of search terms from the filter nsTArray> searchTerms; rv = newFilter->GetSearchTerms(searchTerms); NS_ENSURE_SUCCESS(rv, rv); uint32_t count = searchTerms.Length(); if (count > 1) // don't need to group a single term { // beginGrouping the first term, and endGrouping the last term searchTerms[0]->SetBeginsGrouping(true); searchTerms[count - 1]->SetEndsGrouping(true); } // Create a new term, checking if the user set junk status. The term will // search for junkscoreorigin != "user" nsCOMPtr searchTerm; rv = newFilter->CreateTerm(getter_AddRefs(searchTerm)); NS_ENSURE_SUCCESS(rv, rv); searchTerm->SetAttrib(nsMsgSearchAttrib::JunkScoreOrigin); searchTerm->SetOp(nsMsgSearchOp::Isnt); searchTerm->SetBooleanAnd(true); nsCOMPtr searchValue; searchTerm->GetValue(getter_AddRefs(searchValue)); NS_ENSURE_SUCCESS(rv, rv); searchValue->SetAttrib(nsMsgSearchAttrib::JunkScoreOrigin); searchValue->SetStr(u"user"_ns); searchTerm->SetValue(searchValue); newFilter->AppendTerm(searchTerm); bool moveOnSpam, markAsReadOnSpam; spamSettings->GetMoveOnSpam(&moveOnSpam); if (moveOnSpam) { nsCString spamFolderURI; rv = spamSettings->GetSpamFolderURI(spamFolderURI); if (NS_SUCCEEDED(rv) && (!spamFolderURI.IsEmpty())) { nsCOMPtr moveAction; rv = newFilter->CreateAction(getter_AddRefs(moveAction)); if (NS_SUCCEEDED(rv)) { moveAction->SetType(nsMsgFilterAction::MoveToFolder); moveAction->SetTargetFolderUri(spamFolderURI); newFilter->AppendAction(moveAction); } } } spamSettings->GetMarkAsReadOnSpam(&markAsReadOnSpam); if (markAsReadOnSpam) { nsCOMPtr markAsReadAction; rv = newFilter->CreateAction(getter_AddRefs(markAsReadAction)); if (NS_SUCCEEDED(rv)) { markAsReadAction->SetType(nsMsgFilterAction::MarkRead); newFilter->AppendAction(markAsReadAction); } } filterList->InsertFilterAt(0, newFilter); } rv = serverFilterList->GetFilterNamed(noFilterName, getter_AddRefs(newFilter)); if (newFilter && serverFilterTrustFlags & nsISpamSettings::TRUST_NEGATIVES) { newFilter->SetTemporary(true); filterList->InsertFilterAt(0, newFilter); } return rv; } nsresult nsMsgIncomingServer::ConfigureTemporaryReturnReceiptsFilter( nsIMsgFilterList* filterList) { nsresult rv; nsCOMPtr accountMgr = do_GetService("@mozilla.org/messenger/account-manager;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr identity; rv = accountMgr->GetFirstIdentityForServer(this, getter_AddRefs(identity)); NS_ENSURE_SUCCESS(rv, rv); // this can return success and a null identity... bool useCustomPrefs = false; int32_t incorp = nsIMsgMdnGenerator::eIncorporateInbox; NS_ENSURE_TRUE(identity, NS_ERROR_NULL_POINTER); identity->GetBoolAttribute("use_custom_prefs", &useCustomPrefs); if (useCustomPrefs) rv = GetIntValue("incorporate_return_receipt", &incorp); else { nsCOMPtr prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (prefs) prefs->GetIntPref("mail.incorporate.return_receipt", &incorp); } bool enable = (incorp == nsIMsgMdnGenerator::eIncorporateSent); // this is a temporary, internal mozilla filter // it will not show up in the UI, it will not be written to disk constexpr auto internalReturnReceiptFilterName = u"mozilla-temporary-internal-MDN-receipt-filter"_ns; nsCOMPtr newFilter; rv = filterList->GetFilterNamed(internalReturnReceiptFilterName, getter_AddRefs(newFilter)); if (newFilter) newFilter->SetEnabled(enable); else if (enable) { nsCString actionTargetFolderUri; rv = identity->GetFccFolder(actionTargetFolderUri); if (!actionTargetFolderUri.IsEmpty()) { filterList->CreateFilter(internalReturnReceiptFilterName, getter_AddRefs(newFilter)); if (newFilter) { newFilter->SetEnabled(true); // this internal filter is temporary // and should not show up in the UI or be written to disk newFilter->SetTemporary(true); nsCOMPtr term; nsCOMPtr value; rv = newFilter->CreateTerm(getter_AddRefs(term)); if (NS_SUCCEEDED(rv)) { rv = term->GetValue(getter_AddRefs(value)); if (NS_SUCCEEDED(rv)) { // we need to use OtherHeader + 1 so nsMsgFilter::GetTerm will // return our custom header. value->SetAttrib(nsMsgSearchAttrib::OtherHeader + 1); value->SetStr(u"multipart/report"_ns); term->SetAttrib(nsMsgSearchAttrib::OtherHeader + 1); term->SetOp(nsMsgSearchOp::Contains); term->SetBooleanAnd(true); term->SetArbitraryHeader("Content-Type"_ns); term->SetValue(value); newFilter->AppendTerm(term); } } rv = newFilter->CreateTerm(getter_AddRefs(term)); if (NS_SUCCEEDED(rv)) { rv = term->GetValue(getter_AddRefs(value)); if (NS_SUCCEEDED(rv)) { // XXX todo // determine if ::OtherHeader is the best way to do this. // see nsMsgSearchOfflineMail::MatchTerms() value->SetAttrib(nsMsgSearchAttrib::OtherHeader + 1); value->SetStr(u"disposition-notification"_ns); term->SetAttrib(nsMsgSearchAttrib::OtherHeader + 1); term->SetOp(nsMsgSearchOp::Contains); term->SetBooleanAnd(true); term->SetArbitraryHeader("Content-Type"_ns); term->SetValue(value); newFilter->AppendTerm(term); } } nsCOMPtr filterAction; rv = newFilter->CreateAction(getter_AddRefs(filterAction)); if (NS_SUCCEEDED(rv)) { filterAction->SetType(nsMsgFilterAction::MoveToFolder); filterAction->SetTargetFolderUri(actionTargetFolderUri); newFilter->AppendAction(filterAction); filterList->InsertFilterAt(0, newFilter); } } } } return rv; } NS_IMETHODIMP nsMsgIncomingServer::ClearTemporaryReturnReceiptsFilter() { if (mFilterList) { nsCOMPtr mdnFilter; nsresult rv = mFilterList->GetFilterNamed( u"mozilla-temporary-internal-MDN-receipt-filter"_ns, getter_AddRefs(mdnFilter)); if (NS_SUCCEEDED(rv) && mdnFilter) return mFilterList->RemoveFilter(mdnFilter); } return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetMsgFolderFromURI(nsIMsgFolder* aFolderResource, const nsACString& aURI, nsIMsgFolder** aFolder) { nsCOMPtr rootMsgFolder; nsresult rv = GetRootMsgFolder(getter_AddRefs(rootMsgFolder)); NS_ENSURE_TRUE(rootMsgFolder, NS_ERROR_UNEXPECTED); nsCOMPtr msgFolder; rv = rootMsgFolder->GetChildWithURI(aURI, true, true /*caseInsensitive*/, getter_AddRefs(msgFolder)); if (NS_FAILED(rv) || !msgFolder) msgFolder = aFolderResource; NS_IF_ADDREF(*aFolder = msgFolder); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetSpamSettings(nsISpamSettings** aSpamSettings) { NS_ENSURE_ARG_POINTER(aSpamSettings); nsAutoCString spamActionTargetAccount; GetCharValue("spamActionTargetAccount", spamActionTargetAccount); if (spamActionTargetAccount.IsEmpty()) { GetServerURI(spamActionTargetAccount); SetCharValue("spamActionTargetAccount", spamActionTargetAccount); } if (!mSpamSettings) { nsresult rv; mSpamSettings = do_CreateInstance("@mozilla.org/messenger/spamsettings;1", &rv); NS_ENSURE_SUCCESS(rv, rv); mSpamSettings->Initialize(this); NS_ENSURE_SUCCESS(rv, rv); } NS_ADDREF(*aSpamSettings = mSpamSettings); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetSpamFilterPlugin(nsIMsgFilterPlugin** aFilterPlugin) { NS_ENSURE_ARG_POINTER(aFilterPlugin); if (!mFilterPlugin) { nsresult rv; mFilterPlugin = do_GetService( "@mozilla.org/messenger/filter-plugin;1?name=bayesianfilter", &rv); NS_ENSURE_SUCCESS(rv, rv); } NS_IF_ADDREF(*aFilterPlugin = mFilterPlugin); return NS_OK; } // get all the servers that defer to the account for the passed in server. Note // that destServer may not be "this" nsresult nsMsgIncomingServer::GetDeferredServers( nsIMsgIncomingServer* destServer, nsTArray>& aServers) { nsresult rv; nsCOMPtr accountManager = do_GetService("@mozilla.org/messenger/account-manager;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr thisAccount; accountManager->FindAccountForServer(destServer, getter_AddRefs(thisAccount)); if (thisAccount) { nsCString accountKey; thisAccount->GetKey(accountKey); nsTArray> allServers; accountManager->GetAllServers(allServers); for (auto server : allServers) { nsCOMPtr popServer(do_QueryInterface(server)); if (popServer) { nsCString deferredToAccount; popServer->GetDeferredToAccount(deferredToAccount); if (deferredToAccount.Equals(accountKey)) aServers.AppendElement(popServer); } } } return rv; } NS_IMETHODIMP nsMsgIncomingServer::GetIsDeferredTo(bool* aIsDeferredTo) { NS_ENSURE_ARG_POINTER(aIsDeferredTo); nsCOMPtr accountManager = do_GetService("@mozilla.org/messenger/account-manager;1"); if (accountManager) { nsCOMPtr thisAccount; accountManager->FindAccountForServer(this, getter_AddRefs(thisAccount)); if (thisAccount) { nsCString accountKey; thisAccount->GetKey(accountKey); nsTArray> allServers; accountManager->GetAllServers(allServers); for (auto server : allServers) { if (server) { nsCString deferredToAccount; server->GetCharValue("deferred_to_account", deferredToAccount); if (deferredToAccount.Equals(accountKey)) { *aIsDeferredTo = true; return NS_OK; } } } } } *aIsDeferredTo = false; return NS_OK; } const long kMaxDownloadTableSize = 500; // hash the concatenation of the message-id and subject as the hash table key, // and store the arrival index as the value. To limit the size of the hash // table, we just throw out ones with a lower ordinal value than the cut-off // point. NS_IMETHODIMP nsMsgIncomingServer::IsNewHdrDuplicate(nsIMsgDBHdr* aNewHdr, bool* aResult) { NS_ENSURE_ARG_POINTER(aResult); NS_ENSURE_ARG_POINTER(aNewHdr); *aResult = false; // If the message has been partially downloaded, the message should not // be considered a duplicated message. See bug 714090. uint32_t flags; aNewHdr->GetFlags(&flags); if (flags & nsMsgMessageFlags::Partial) return NS_OK; nsAutoCString strHashKey; nsCString messageId, subject; aNewHdr->GetMessageId(getter_Copies(messageId)); strHashKey.Append(messageId); aNewHdr->GetSubject(subject); // err on the side of caution and ignore messages w/o subject or messageid. if (subject.IsEmpty() || messageId.IsEmpty()) return NS_OK; strHashKey.Append(subject); int32_t hashValue = m_downloadedHdrs.Get(strHashKey); if (hashValue) *aResult = true; else { // we store the current size of the hash table as the hash // value - this allows us to delete older entries. m_downloadedHdrs.InsertOrUpdate(strHashKey, ++m_numMsgsDownloaded); // Check if hash table is larger than some reasonable size // and if is it, iterate over hash table deleting messages // with an arrival index < number of msgs downloaded - half the reasonable // size. if (m_downloadedHdrs.Count() >= kMaxDownloadTableSize) { for (auto iter = m_downloadedHdrs.Iter(); !iter.Done(); iter.Next()) { if (iter.Data() < m_numMsgsDownloaded - kMaxDownloadTableSize / 2) { iter.Remove(); } else if (m_downloadedHdrs.Count() <= kMaxDownloadTableSize / 2) { break; } } } } return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::GetForcePropertyEmpty(const char* aPropertyName, bool* _retval) { NS_ENSURE_ARG_POINTER(_retval); nsAutoCString nameEmpty(aPropertyName); nameEmpty.AppendLiteral(".empty"); nsCString value; GetCharValue(nameEmpty.get(), value); *_retval = value.EqualsLiteral("true"); return NS_OK; } NS_IMETHODIMP nsMsgIncomingServer::SetForcePropertyEmpty(const char* aPropertyName, bool aValue) { nsAutoCString nameEmpty(aPropertyName); nameEmpty.AppendLiteral(".empty"); return SetCharValue(nameEmpty.get(), aValue ? "true"_ns : ""_ns); } NS_IMETHODIMP nsMsgIncomingServer::GetSortOrder(int32_t* aSortOrder) { NS_ENSURE_ARG_POINTER(aSortOrder); *aSortOrder = 100000000; return NS_OK; }