summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/base/src/nsMsgBiffManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/base/src/nsMsgBiffManager.cpp')
-rw-r--r--comm/mailnews/base/src/nsMsgBiffManager.cpp341
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;
+}