diff options
Diffstat (limited to 'mailnews/base/src/nsMsgBiffManager.cpp')
-rw-r--r-- | mailnews/base/src/nsMsgBiffManager.cpp | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/mailnews/base/src/nsMsgBiffManager.cpp b/mailnews/base/src/nsMsgBiffManager.cpp new file mode 100644 index 000000000..360600147 --- /dev/null +++ b/mailnews/base/src/nsMsgBiffManager.cpp @@ -0,0 +1,373 @@ +/* -*- 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 "nsMsgBaseCID.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 "mozilla/Services.h" +#include <algorithm> + +#define PREF_BIFF_JITTER "mail.biff.add_interval_jitter" + +static NS_DEFINE_CID(kStatusBarBiffManagerCID, NS_STATUSBARBIFFMANAGER_CID); + +static PRLogModuleInfo *MsgBiffLogModule = nullptr; + +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(NS_MSGACCOUNTMANAGER_CONTRACTID, &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); + + if (!MsgBiffLogModule) + MsgBiffLogModule = PR_NewLogModule("MsgBiff"); + + 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(NS_MSGACCOUNTMANAGER_CONTRACTID, &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. + mBiffTimer = do_CreateInstance("@mozilla.org/timer;1"); + mBiffTimer->InitWithFuncCallback(OnBiffTimer, (void*)this, 10000, + nsITimer::TYPE_ONE_SHOT); + } + 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\n", 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\n", timeInMSUint32)); + mBiffTimer = do_CreateInstance("@mozilla.org/timer;1"); + mBiffTimer->InitWithFuncCallback(OnBiffTimer, (void*)this, timeInMSUint32, + nsITimer::TYPE_ONE_SHOT); + + } + 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\n")); + + 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 = %x\n", serverKey.get(), rv)); + } + else + { + MOZ_LOG(MsgBiffLogModule, mozilla::LogLevel::Info, ("not biffing server serverBusy = %d requirespassword = %d password prompt required = %d targetFolderIndex = %d\n", + 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; +} |