diff options
Diffstat (limited to '')
-rw-r--r-- | comm/mailnews/base/src/nsMsgBiffManager.cpp | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/comm/mailnews/base/src/nsMsgBiffManager.cpp b/comm/mailnews/base/src/nsMsgBiffManager.cpp new file mode 100644 index 0000000000..9d990a30bd --- /dev/null +++ b/comm/mailnews/base/src/nsMsgBiffManager.cpp @@ -0,0 +1,341 @@ +/* -*- 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 "nsMsgBiffManager.h" +#include "nsIMsgAccountManager.h" +#include "nsStatusBarBiffManager.h" +#include "nsCOMArray.h" +#include "mozilla/Logging.h" +#include "nspr.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "nsIObserverService.h" +#include "nsComponentManagerUtils.h" +#include "nsServiceManagerUtils.h" +#include "nsMsgUtils.h" +#include "nsITimer.h" +#include "mozilla/Services.h" + +#define PREF_BIFF_JITTER "mail.biff.add_interval_jitter" + +#define NS_STATUSBARBIFFMANAGER_CID \ + { \ + 0x7f9a9fb0, 0x4161, 0x11d4, { \ + 0x98, 0x76, 0x00, 0xc0, 0x4f, 0xa0, 0xd2, 0xa6 \ + } \ + } +static NS_DEFINE_CID(kStatusBarBiffManagerCID, NS_STATUSBARBIFFMANAGER_CID); + +static mozilla::LazyLogModule MsgBiffLogModule("MsgBiff"); + +NS_IMPL_ISUPPORTS(nsMsgBiffManager, nsIMsgBiffManager, + nsIIncomingServerListener, nsIObserver, + nsISupportsWeakReference) + +void OnBiffTimer(nsITimer* timer, void* aBiffManager) { + nsMsgBiffManager* biffManager = (nsMsgBiffManager*)aBiffManager; + biffManager->PerformBiff(); +} + +nsMsgBiffManager::nsMsgBiffManager() { + mHaveShutdown = false; + mInited = false; +} + +nsMsgBiffManager::~nsMsgBiffManager() { + if (mBiffTimer) mBiffTimer->Cancel(); + + if (!mHaveShutdown) Shutdown(); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->RemoveObserver(this, "wake_notification"); + observerService->RemoveObserver(this, "sleep_notification"); + } +} + +NS_IMETHODIMP nsMsgBiffManager::Init() { + if (mInited) return NS_OK; + + mInited = true; + nsresult rv; + + nsCOMPtr<nsIMsgAccountManager> accountManager = + do_GetService("@mozilla.org/messenger/account-manager;1", &rv); + if (NS_SUCCEEDED(rv)) accountManager->AddIncomingServerListener(this); + + // in turbo mode on profile change we don't need to do anything below this + if (mHaveShutdown) { + mHaveShutdown = false; + return NS_OK; + } + + // Ensure status bar biff service has started + nsCOMPtr<nsIFolderListener> statusBarBiffService = + do_GetService(kStatusBarBiffManagerCID, &rv); + + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->AddObserver(this, "sleep_notification", true); + observerService->AddObserver(this, "wake_notification", true); + } + return NS_OK; +} + +NS_IMETHODIMP nsMsgBiffManager::Shutdown() { + if (mBiffTimer) { + mBiffTimer->Cancel(); + mBiffTimer = nullptr; + } + + nsresult rv; + nsCOMPtr<nsIMsgAccountManager> accountManager = + do_GetService("@mozilla.org/messenger/account-manager;1", &rv); + if (NS_SUCCEEDED(rv)) accountManager->RemoveIncomingServerListener(this); + + mHaveShutdown = true; + mInited = false; + return NS_OK; +} + +NS_IMETHODIMP nsMsgBiffManager::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* someData) { + if (!strcmp(aTopic, "sleep_notification") && mBiffTimer) { + mBiffTimer->Cancel(); + mBiffTimer = nullptr; + } else if (!strcmp(aTopic, "wake_notification")) { + // wait 10 seconds after waking up to start biffing again. + nsresult rv = NS_NewTimerWithFuncCallback( + getter_AddRefs(mBiffTimer), OnBiffTimer, (void*)this, 10000, + nsITimer::TYPE_ONE_SHOT, "nsMsgBiffManager::OnBiffTimer", nullptr); + if (NS_FAILED(rv)) { + NS_WARNING("Could not start mBiffTimer timer"); + } + } + return NS_OK; +} + +NS_IMETHODIMP nsMsgBiffManager::AddServerBiff(nsIMsgIncomingServer* server) { + NS_ENSURE_ARG_POINTER(server); + + int32_t biffMinutes; + + nsresult rv = server->GetBiffMinutes(&biffMinutes); + NS_ENSURE_SUCCESS(rv, rv); + + // Don't add if biffMinutes isn't > 0 + if (biffMinutes > 0) { + int32_t serverIndex = FindServer(server); + // Only add it if it hasn't been added already. + if (serverIndex == -1) { + nsBiffEntry biffEntry; + biffEntry.server = server; + rv = SetNextBiffTime(biffEntry, PR_Now()); + NS_ENSURE_SUCCESS(rv, rv); + + AddBiffEntry(biffEntry); + SetupNextBiff(); + } + } + return NS_OK; +} + +NS_IMETHODIMP nsMsgBiffManager::RemoveServerBiff(nsIMsgIncomingServer* server) { + int32_t pos = FindServer(server); + if (pos != -1) mBiffArray.RemoveElementAt(pos); + + // Should probably reset biff time if this was the server that gets biffed + // next. + return NS_OK; +} + +NS_IMETHODIMP nsMsgBiffManager::ForceBiff(nsIMsgIncomingServer* server) { + return NS_OK; +} + +NS_IMETHODIMP nsMsgBiffManager::ForceBiffAll() { return NS_OK; } + +NS_IMETHODIMP nsMsgBiffManager::OnServerLoaded(nsIMsgIncomingServer* server) { + NS_ENSURE_ARG_POINTER(server); + + bool doBiff = false; + nsresult rv = server->GetDoBiff(&doBiff); + + if (NS_SUCCEEDED(rv) && doBiff) rv = AddServerBiff(server); + + return rv; +} + +NS_IMETHODIMP nsMsgBiffManager::OnServerUnloaded(nsIMsgIncomingServer* server) { + return RemoveServerBiff(server); +} + +NS_IMETHODIMP nsMsgBiffManager::OnServerChanged(nsIMsgIncomingServer* server) { + // nothing required. If the hostname or username changed + // the next time biff fires, we'll ping the right server + return NS_OK; +} + +int32_t nsMsgBiffManager::FindServer(nsIMsgIncomingServer* server) { + uint32_t count = mBiffArray.Length(); + for (uint32_t i = 0; i < count; i++) { + if (server == mBiffArray[i].server.get()) return i; + } + return -1; +} + +nsresult nsMsgBiffManager::AddBiffEntry(nsBiffEntry& biffEntry) { + uint32_t i; + uint32_t count = mBiffArray.Length(); + for (i = 0; i < count; i++) { + if (biffEntry.nextBiffTime < mBiffArray[i].nextBiffTime) break; + } + MOZ_LOG(MsgBiffLogModule, mozilla::LogLevel::Info, + ("inserting biff entry at %d", i)); + mBiffArray.InsertElementAt(i, biffEntry); + return NS_OK; +} + +nsresult nsMsgBiffManager::SetNextBiffTime(nsBiffEntry& biffEntry, + PRTime currentTime) { + nsIMsgIncomingServer* server = biffEntry.server; + NS_ENSURE_TRUE(server, NS_ERROR_FAILURE); + + int32_t biffInterval; + nsresult rv = server->GetBiffMinutes(&biffInterval); + NS_ENSURE_SUCCESS(rv, rv); + + // Add biffInterval, converted in microseconds, to current time. + // Force 64-bit multiplication. + PRTime chosenTimeInterval = biffInterval * 60000000LL; + biffEntry.nextBiffTime = currentTime + chosenTimeInterval; + + // Check if we should jitter. + nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); + if (prefs) { + bool shouldUseBiffJitter = false; + prefs->GetBoolPref(PREF_BIFF_JITTER, &shouldUseBiffJitter); + if (shouldUseBiffJitter) { + // Calculate a jitter of +/-5% on chosenTimeInterval + // - minimum 1 second (to avoid a modulo with 0) + // - maximum 30 seconds (to avoid problems when biffInterval is very + // large) + int64_t jitter = (int64_t)(0.05 * (int64_t)chosenTimeInterval); + jitter = + std::max<int64_t>(1000000LL, std::min<int64_t>(jitter, 30000000LL)); + jitter = ((rand() % 2) ? 1 : -1) * (rand() % jitter); + + biffEntry.nextBiffTime += jitter; + } + } + + return NS_OK; +} + +nsresult nsMsgBiffManager::SetupNextBiff() { + if (mBiffArray.Length() > 0) { + // Get the next biff entry + const nsBiffEntry& biffEntry = mBiffArray[0]; + PRTime currentTime = PR_Now(); + int64_t biffDelay; + int64_t ms(1000); + + if (currentTime > biffEntry.nextBiffTime) { + // Let's wait 30 seconds before firing biff again + biffDelay = 30 * PR_USEC_PER_SEC; + } else + biffDelay = biffEntry.nextBiffTime - currentTime; + + // Convert biffDelay into milliseconds + int64_t timeInMS = biffDelay / ms; + uint32_t timeInMSUint32 = (uint32_t)timeInMS; + + // Can't currently reset a timer when it's in the process of + // calling Notify. So, just release the timer here and create a new one. + if (mBiffTimer) mBiffTimer->Cancel(); + + MOZ_LOG(MsgBiffLogModule, mozilla::LogLevel::Info, + ("setting %d timer", timeInMSUint32)); + + nsresult rv = NS_NewTimerWithFuncCallback( + getter_AddRefs(mBiffTimer), OnBiffTimer, (void*)this, timeInMSUint32, + nsITimer::TYPE_ONE_SHOT, "nsMsgBiffManager::OnBiffTimer", nullptr); + if (NS_FAILED(rv)) { + NS_WARNING("Could not start mBiffTimer timer"); + } + } + return NS_OK; +} + +// This is the function that does a biff on all of the servers whose time it is +// to biff. +nsresult nsMsgBiffManager::PerformBiff() { + PRTime currentTime = PR_Now(); + nsCOMArray<nsIMsgFolder> targetFolders; + MOZ_LOG(MsgBiffLogModule, mozilla::LogLevel::Info, ("performing biffs")); + + uint32_t count = mBiffArray.Length(); + for (uint32_t i = 0; i < count; i++) { + // Take a copy of the entry rather than the a reference so that we can + // remove and add if necessary, but keep the references and memory alive. + nsBiffEntry current = mBiffArray[i]; + if (current.nextBiffTime < currentTime) { + bool serverBusy = false; + bool serverRequiresPassword = true; + bool passwordPromptRequired; + + current.server->GetPasswordPromptRequired(&passwordPromptRequired); + current.server->GetServerBusy(&serverBusy); + current.server->GetServerRequiresPasswordForBiff(&serverRequiresPassword); + // find the dest folder we're actually downloading to... + nsCOMPtr<nsIMsgFolder> rootMsgFolder; + current.server->GetRootMsgFolder(getter_AddRefs(rootMsgFolder)); + int32_t targetFolderIndex = targetFolders.IndexOfObject(rootMsgFolder); + if (targetFolderIndex == kNotFound) + targetFolders.AppendObject(rootMsgFolder); + + // so if we need to be authenticated to biff, check that we are + // (since we don't want to prompt the user for password UI) + // and make sure the server isn't already in the middle of downloading + // new messages + if (!serverBusy && (!serverRequiresPassword || !passwordPromptRequired) && + targetFolderIndex == kNotFound) { + nsCString serverKey; + current.server->GetKey(serverKey); + nsresult rv = current.server->PerformBiff(nullptr); + MOZ_LOG(MsgBiffLogModule, mozilla::LogLevel::Info, + ("biffing server %s rv = %" PRIx32, serverKey.get(), + static_cast<uint32_t>(rv))); + } else { + MOZ_LOG(MsgBiffLogModule, mozilla::LogLevel::Info, + ("not biffing server serverBusy = %d requirespassword = %d " + "password prompt required = %d targetFolderIndex = %d", + serverBusy, serverRequiresPassword, passwordPromptRequired, + targetFolderIndex)); + } + // if we didn't do this server because the destination server was already + // being biffed into, leave this server in the biff array so it will fire + // next. + if (targetFolderIndex == kNotFound) { + mBiffArray.RemoveElementAt(i); + i--; // Because we removed it we need to look at the one that just + // moved up. + SetNextBiffTime(current, currentTime); + AddBiffEntry(current); + } +#ifdef DEBUG_David_Bienvenu + else + printf("dest account performing biff\n"); +#endif + } else + // since we're in biff order, there's no reason to keep checking + break; + } + SetupNextBiff(); + return NS_OK; +} |