Source code

Revision control

Copy as Markdown

Other Tools

/* 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 "nsAutoSyncState.h"
#include "nsImapMailFolder.h"
#include "nsIImapService.h"
#include "nsIMsgMailNewsUrl.h"
#include "nsIMsgMailSession.h"
#include "nsMsgFolderFlags.h"
#include "nsIAutoSyncManager.h"
#include "nsIAutoSyncMsgStrategy.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/Logging.h"
using namespace mozilla;
extern LazyLogModule gAutoSyncLog; // defined in nsAutoSyncManager.cpp
MsgStrategyComparatorAdaptor::MsgStrategyComparatorAdaptor(
nsIAutoSyncMsgStrategy* aStrategy, nsIMsgFolder* aFolder,
nsIMsgDatabase* aDatabase)
: mStrategy(aStrategy), mFolder(aFolder), mDatabase(aDatabase) {}
/** @return True if the elements are equals; false otherwise. */
bool MsgStrategyComparatorAdaptor::Equals(const nsMsgKey& a,
const nsMsgKey& b) const {
nsCOMPtr<nsIMsgDBHdr> hdrA;
nsCOMPtr<nsIMsgDBHdr> hdrB;
mDatabase->GetMsgHdrForKey(a, getter_AddRefs(hdrA));
mDatabase->GetMsgHdrForKey(b, getter_AddRefs(hdrB));
if (hdrA && hdrB) {
nsresult rv = NS_OK;
nsAutoSyncStrategyDecisionType decision = nsAutoSyncStrategyDecisions::Same;
if (mStrategy) rv = mStrategy->Sort(mFolder, hdrA, hdrB, &decision);
if (NS_SUCCEEDED(rv))
return (decision == nsAutoSyncStrategyDecisions::Same);
}
return false;
}
/** @return True if (a < b); false otherwise. */
bool MsgStrategyComparatorAdaptor::LessThan(const nsMsgKey& a,
const nsMsgKey& b) const {
nsCOMPtr<nsIMsgDBHdr> hdrA;
nsCOMPtr<nsIMsgDBHdr> hdrB;
mDatabase->GetMsgHdrForKey(a, getter_AddRefs(hdrA));
mDatabase->GetMsgHdrForKey(b, getter_AddRefs(hdrB));
if (hdrA && hdrB) {
nsresult rv = NS_OK;
nsAutoSyncStrategyDecisionType decision = nsAutoSyncStrategyDecisions::Same;
if (mStrategy) rv = mStrategy->Sort(mFolder, hdrA, hdrB, &decision);
if (NS_SUCCEEDED(rv))
return (decision == nsAutoSyncStrategyDecisions::Lower);
}
return false;
}
nsAutoSyncState::nsAutoSyncState(nsImapMailFolder* aOwnerFolder,
PRTime aLastSyncTime)
: mSyncState(stCompletedIdle),
mOffset(0U),
mLastOffset(0U),
mLastServerTotal(0),
mLastServerRecent(0),
mLastServerUnseen(0),
mLastNextUID(0),
mLastSyncTime(aLastSyncTime),
mLastUpdateTime(0UL),
mProcessPointer(0U),
mIsDownloadQChanged(false),
mRetryCounter(0U) {
mOwnerFolder =
do_GetWeakReference(static_cast<nsIMsgImapMailFolder*>(aOwnerFolder));
mHaveAStatusResponse = false;
}
nsAutoSyncState::~nsAutoSyncState() {}
// TODO:XXXemre should be implemented when we start
// doing space management
nsresult nsAutoSyncState::ManageStorageSpace() { return NS_OK; }
nsresult nsAutoSyncState::PlaceIntoDownloadQ(
const nsTArray<nsMsgKey>& aMsgKeyList) {
nsresult rv = NS_OK;
if (!aMsgKeyList.IsEmpty()) {
nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgDatabase> database;
rv = folder->GetMsgDatabase(getter_AddRefs(database));
if (!database) return NS_ERROR_FAILURE;
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr =
do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAutoSyncMsgStrategy> msgStrategy;
autoSyncMgr->GetMsgStrategy(getter_AddRefs(msgStrategy));
// increase the array size
mDownloadQ.SetCapacity(mDownloadQ.Length() + aMsgKeyList.Length());
// remove excluded messages
int32_t elemCount = aMsgKeyList.Length();
for (int32_t idx = 0; idx < elemCount; idx++) {
nsCOMPtr<nsIMsgDBHdr> hdr;
bool containsKey;
database->ContainsKey(aMsgKeyList[idx], &containsKey);
if (!containsKey) continue;
rv = database->GetMsgHdrForKey(aMsgKeyList[idx], getter_AddRefs(hdr));
if (!hdr)
continue; // can't get message header, continue with the next one
bool doesFit = true;
rv = autoSyncMgr->DoesMsgFitDownloadCriteria(hdr, &doesFit);
if (NS_SUCCEEDED(rv) && !mDownloadSet.Contains(aMsgKeyList[idx]) &&
doesFit) {
bool excluded = false;
if (msgStrategy) {
rv = msgStrategy->IsExcluded(folder, hdr, &excluded);
if (NS_SUCCEEDED(rv) && !excluded) {
mIsDownloadQChanged = true;
mDownloadSet.PutEntry(aMsgKeyList[idx]);
mDownloadQ.AppendElement(aMsgKeyList[idx]);
}
}
}
} // endfor
if (mIsDownloadQChanged) {
LogOwnerFolderName("Download Q is created for ");
LogQWithSize(mDownloadQ, 0);
rv = autoSyncMgr->OnDownloadQChanged(this);
}
}
return rv;
}
nsresult nsAutoSyncState::SortQueueBasedOnStrategy(nsTArray<nsMsgKey>& aQueue) {
nsresult rv;
nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgDatabase> database;
rv = folder->GetMsgDatabase(getter_AddRefs(database));
if (!database) return NS_ERROR_FAILURE;
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr =
do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAutoSyncMsgStrategy> msgStrategy;
rv = autoSyncMgr->GetMsgStrategy(getter_AddRefs(msgStrategy));
NS_ENSURE_SUCCESS(rv, rv);
MsgStrategyComparatorAdaptor strategyComp(msgStrategy, folder, database);
aQueue.Sort(strategyComp);
return rv;
}
// This method is a hack to prioritize newly inserted messages,
// without changing the size of the queue. It is required since
// we cannot sort ranges in nsTArray.
nsresult nsAutoSyncState::SortSubQueueBasedOnStrategy(
nsTArray<nsMsgKey>& aQueue, uint32_t aStartingOffset) {
NS_ASSERTION(aStartingOffset < aQueue.Length(),
"*** Starting offset is out of range");
// Copy already downloaded messages into a temporary queue,
// we want to exclude them from the sort.
nsTArray<nsMsgKey> tmpQ;
tmpQ.AppendElements(aQueue.Elements(), aStartingOffset);
// Remove already downloaded messages and sort the resulting queue
aQueue.RemoveElementsAt(0, aStartingOffset);
nsresult rv = SortQueueBasedOnStrategy(aQueue);
// copy excluded messages back
aQueue.InsertElementsAt(0, tmpQ);
return rv;
}
NS_IMETHODIMP nsAutoSyncState::GetNextGroupOfMessages(
uint32_t aSuggestedGroupSizeLimit, uint32_t* aActualGroupSize,
nsTArray<RefPtr<nsIMsgDBHdr>>& aMessages) {
NS_ENSURE_ARG_POINTER(aActualGroupSize);
aMessages.Clear();
*aActualGroupSize = 0;
nsresult rv;
nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgDatabase> database;
folder->GetMsgDatabase(getter_AddRefs(database));
if (database) {
if (!mDownloadQ.IsEmpty()) {
// sort the download queue if new items are added since the last time
if (mIsDownloadQChanged) {
// we want to sort only pending messages. mOffset is
// the position of the first pending message in the download queue
rv = (mOffset > 0) ? SortSubQueueBasedOnStrategy(mDownloadQ, mOffset)
: SortQueueBasedOnStrategy(mDownloadQ);
if (NS_SUCCEEDED(rv)) mIsDownloadQChanged = false;
}
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr =
do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
uint32_t msgCount = mDownloadQ.Length();
uint32_t idx = mOffset;
nsCOMPtr<nsIAutoSyncMsgStrategy> msgStrategy;
autoSyncMgr->GetMsgStrategy(getter_AddRefs(msgStrategy));
for (; idx < msgCount; idx++) {
bool containsKey = false;
database->ContainsKey(mDownloadQ[idx], &containsKey);
if (!containsKey) {
mDownloadSet.RemoveEntry(mDownloadQ[idx]);
mDownloadQ.RemoveElementAt(idx--);
msgCount--;
continue;
}
nsCOMPtr<nsIMsgDBHdr> qhdr;
database->GetMsgHdrForKey(mDownloadQ[idx], getter_AddRefs(qhdr));
if (!qhdr) continue; // maybe deleted, skip it!
// ensure that we don't have this message body offline already,
// possible if the user explicitly selects this message prior
// to auto-sync kicks in
bool hasMessageOffline;
folder->HasMsgOffline(mDownloadQ[idx], &hasMessageOffline);
if (hasMessageOffline) continue;
// this check point allows msg strategy function
// to do last minute decisions based on the current
// state of TB such as the size of the message store etc.
if (msgStrategy) {
bool excluded = false;
if (NS_SUCCEEDED(msgStrategy->IsExcluded(folder, qhdr, &excluded)) &&
excluded)
continue;
}
uint32_t msgSize;
qhdr->GetMessageSize(&msgSize);
// ignore 0 byte messages; the imap parser asserts when we try
// to download them, and there's no point anyway.
if (!msgSize) continue;
if (!*aActualGroupSize && msgSize >= aSuggestedGroupSizeLimit) {
*aActualGroupSize = msgSize;
aMessages.AppendElement(qhdr);
idx++;
break;
}
if ((*aActualGroupSize) + msgSize > aSuggestedGroupSizeLimit) break;
aMessages.AppendElement(qhdr);
*aActualGroupSize += msgSize;
} // endfor
mLastOffset = mOffset;
mOffset = idx;
}
LogOwnerFolderName("Next group of messages to be downloaded.");
LogQWithSize(aMessages, 0);
} // endif
return NS_OK;
}
/**
* Called by nsAutoSyncManager::TimerCallback to process message headers for a
* folder in the discovery queue. The queue is created on the kAutoSyncFreq
* time base (1 hour). Headers lacking offline store are placed in download
* queue.
*/
NS_IMETHODIMP nsAutoSyncState::ProcessExistingHeaders(
uint32_t aNumOfHdrsToProcess, uint32_t* aLeftToProcess) {
NS_ENSURE_ARG_POINTER(aLeftToProcess);
nsresult rv;
nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgDatabase> database;
rv = folder->GetMsgDatabase(getter_AddRefs(database));
if (!database) return NS_ERROR_FAILURE;
// create a queue to process existing headers for the first time
if (mExistingHeadersQ.IsEmpty()) {
nsTArray<nsMsgKey> keys;
rv = database->ListAllKeys(keys);
NS_ENSURE_SUCCESS(rv, rv);
keys.Sort();
mExistingHeadersQ.AppendElements(keys);
mProcessPointer = 0;
}
// process the existing headers and find the messages not downloaded yet
uint32_t lastIdx = mProcessPointer;
nsTArray<nsMsgKey> msgKeys;
uint32_t keyCount = mExistingHeadersQ.Length();
for (; mProcessPointer < (lastIdx + aNumOfHdrsToProcess) &&
mProcessPointer < keyCount;
mProcessPointer++) {
bool hasMessageOffline;
folder->HasMsgOffline(mExistingHeadersQ[mProcessPointer],
&hasMessageOffline);
if (!hasMessageOffline)
msgKeys.AppendElement(mExistingHeadersQ[mProcessPointer]);
}
if (!msgKeys.IsEmpty()) {
nsCString folderName;
folder->GetURI(folderName);
MOZ_LOG(
gAutoSyncLog, LogLevel::Debug,
("%s: %zu messages will be added into the download q of folder %s\n",
__func__, msgKeys.Length(), folderName.get()));
rv = PlaceIntoDownloadQ(msgKeys);
if (NS_FAILED(rv)) mProcessPointer = lastIdx;
}
*aLeftToProcess = keyCount - mProcessPointer;
// cleanup if we are done processing
if (0 == *aLeftToProcess) {
mLastSyncTime = PR_Now();
mExistingHeadersQ.Clear();
mProcessPointer = 0;
folder->SetMsgDatabase(nullptr);
}
return rv;
}
void nsAutoSyncState::OnNewHeaderFetchCompleted(
const nsTArray<nsMsgKey>& aMsgKeyList) {
SetLastUpdateTime(PR_Now());
if (!aMsgKeyList.IsEmpty()) PlaceIntoDownloadQ(aMsgKeyList);
MOZ_LOG(
gAutoSyncLog, LogLevel::Debug,
("%s: %zu msg keys put into download q", __func__, aMsgKeyList.Length()));
}
NS_IMETHODIMP nsAutoSyncState::UpdateFolder() {
nsresult rv;
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr =
do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIUrlListener> autoSyncMgrListener =
do_QueryInterface(autoSyncMgr, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
do_QueryReferent(mOwnerFolder, &rv);
SetState(nsAutoSyncState::stUpdateIssued);
return imapFolder->UpdateFolderWithListener(nullptr, autoSyncMgrListener);
}
NS_IMETHODIMP nsAutoSyncState::OnStartRunningUrl(nsIURI* aUrl) {
nsresult rv = NS_OK;
// if there is a problem to start the download, set rv with the
// corresponding error code. In that case, AutoSyncManager is going to
// set the autosync state to nsAutoSyncState::stReadyToDownload
// to resume downloading another time
// TODO: is there a way to make sure that download started without
// problem through nsIURI interface?
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr =
do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
return autoSyncMgr->OnDownloadStarted(this, rv);
}
/**
* This is called when a folder status URL finishes. It is also called when
* needed message downloads (imap fetch) for a folder completes.
*/
NS_IMETHODIMP nsAutoSyncState::OnStopRunningUrl(nsIURI* aUrl,
nsresult aExitCode) {
nsresult rv;
nsCOMPtr<nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAutoSyncManager> autoSyncMgr =
do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIUrlListener> autoSyncMgrListener =
do_QueryInterface(autoSyncMgr, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (mSyncState == stStatusIssued) {
nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
int32_t serverTotal, serverUnseen, serverRecent, serverNextUID;
imapFolder->GetServerTotal(&serverTotal);
imapFolder->GetServerUnseen(&serverUnseen);
imapFolder->GetServerRecent(&serverRecent);
imapFolder->GetServerNextUID(&serverNextUID);
// Note: UNSEEN often shows a change when nothing else changes. This is
// because UNSEEN produced by SELECT is not the number of unseen messages.
// So ignore change to UNSEEN to avoid spurious folder updates. Commented
// out below.
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("%s: serverUnseen=%d lastServerUnseen=%d", __func__, serverUnseen,
mLastServerUnseen));
if (serverNextUID != mLastNextUID || serverTotal != mLastServerTotal ||
serverRecent != mLastServerRecent //||
/*(serverUnseen != mLastServerUnseen)*/) {
if (MOZ_LOG_TEST(gAutoSyncLog, LogLevel::Debug)) {
nsCString folderName;
ownerFolder->GetURI(folderName);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("%s: folder %s status changed serverNextUID=%d lastNextUID=%d",
__func__, folderName.get(), serverNextUID, mLastNextUID));
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("%s: serverTotal = %d lastServerTotal = %d serverRecent = %d "
"lastServerRecent = %d\n",
__func__, serverTotal, mLastServerTotal, serverRecent,
mLastServerRecent));
}
SetServerCounts(serverTotal, serverRecent, serverUnseen, serverNextUID);
SetState(nsAutoSyncState::stUpdateIssued);
rv = imapFolder->UpdateFolderWithListener(nullptr, autoSyncMgrListener);
} else // folderstatus detected no change
{
if (MOZ_LOG_TEST(gAutoSyncLog, LogLevel::Debug)) {
nsCString folderName;
ownerFolder->GetURI(folderName);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("%s: folder %s status or noop issued, no change", __func__,
folderName.get()));
}
// Status detected no change. This may be due to an previously deleted and
// now empty database so change compares above could be invalid. If so,
// force an update which will re-populate the database (.msf) and download
// all the message to mbox/maildir store. This check is only done on the
// first imap STATUS response after start-up and if the server response
// reports that the folder is not empty.
if (!mHaveAStatusResponse && serverTotal != 0) {
nsCOMPtr<nsIMsgDatabase> database;
ownerFolder->GetMsgDatabase(getter_AddRefs(database));
bool hasHeader = false;
if (database) {
nsCOMPtr<nsIMsgEnumerator> hdrs;
database->EnumerateMessages(getter_AddRefs(hdrs));
if (hdrs) hdrs->HasMoreElements(&hasHeader);
}
if (!hasHeader) {
if (MOZ_LOG_TEST(gAutoSyncLog, LogLevel::Debug)) {
nsCString folderName;
ownerFolder->GetURI(folderName);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("%s: folder %s has empty DB, force an update", __func__,
folderName.get()));
}
SetServerCounts(serverTotal, serverRecent, serverUnseen,
serverNextUID);
SetState(nsAutoSyncState::stUpdateIssued);
rv = imapFolder->UpdateFolderWithListener(nullptr,
autoSyncMgrListener);
}
}
if (mSyncState == stStatusIssued) {
// Didn't force an update above so transition back to stCompletedIdle
ownerFolder->SetMsgDatabase(nullptr);
// nothing more to do.
SetState(nsAutoSyncState::stCompletedIdle);
// autoSyncMgr needs this notification, so manufacture it.
rv = autoSyncMgrListener->OnStopRunningUrl(aUrl, NS_OK);
}
} // end no change detected
mHaveAStatusResponse = true;
} else // URL not folderstatus but FETCH of message body
{
// XXXemre how we recover from this error?
rv = ownerFolder->ReleaseSemaphore(ownerFolder);
NS_ASSERTION(NS_SUCCEEDED(rv), "*** Cannot release folder semaphore");
nsCOMPtr<nsIMsgMailNewsUrl> mailUrl = do_QueryInterface(aUrl);
if (mailUrl) rv = mailUrl->UnRegisterListener(this);
if (MOZ_LOG_TEST(gAutoSyncLog, LogLevel::Debug)) {
nsCString folderName;
ownerFolder->GetURI(folderName);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("%s: URL for FETCH of msg body/bodies complete, folder %s",
__func__, folderName.get()));
}
rv = autoSyncMgr->OnDownloadCompleted(this, aExitCode);
}
return rv;
}
NS_IMETHODIMP nsAutoSyncState::GetState(int32_t* aState) {
NS_ENSURE_ARG_POINTER(aState);
*aState = mSyncState;
return NS_OK;
}
// clang-format off
const char* stateStrings[] = {
"stCompletedIdle:0", // Initial state
"stStatusIssued:1", // Imap STATUS or NOOP to occur to detect new msgs
"stUpdateNeeded:2", // Imap SELECT to occur due to "pending" msgs
"stUpdateIssued:3", // Imap SELECT to occur then fetch new headers
"stReadyToDownload:4", // Ready to download a group of new messages
"stDownloadInProgress:5" // Download, go to 4 if more msgs then 0 when all done
};
// clang-format on
NS_IMETHODIMP nsAutoSyncState::SetState(int32_t aState) {
mSyncState = aState;
if (aState == stCompletedIdle) {
ResetDownloadQ();
// tell folder to let go of its cached msg db pointer
nsresult rv;
nsCOMPtr<nsIMsgMailSession> session =
do_GetService("@mozilla.org/messenger/services/session;1", &rv);
if (NS_SUCCEEDED(rv) && session) {
nsCOMPtr<nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
bool folderOpen;
uint32_t folderFlags;
ownerFolder->GetFlags(&folderFlags);
session->IsFolderOpenInWindow(ownerFolder, &folderOpen);
if (!folderOpen && !(folderFlags & nsMsgFolderFlags::Inbox))
ownerFolder->SetMsgDatabase(nullptr);
}
}
nsCString logStr("Sync State set to |");
logStr.Append(stateStrings[aState]);
logStr.AppendLiteral("| for ");
LogOwnerFolderName(logStr.get());
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::TryCurrentGroupAgain(uint32_t aRetryCount) {
SetState(stReadyToDownload);
nsresult rv;
if (++mRetryCounter > aRetryCount) {
ResetRetryCounter();
rv = NS_ERROR_FAILURE;
} else
rv = Rollback();
return rv;
}
NS_IMETHODIMP nsAutoSyncState::ResetRetryCounter() {
mRetryCounter = 0;
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::GetPendingMessageCount(int32_t* aMsgCount) {
NS_ENSURE_ARG_POINTER(aMsgCount);
*aMsgCount = mDownloadQ.Length() - mOffset;
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::GetTotalMessageCount(int32_t* aMsgCount) {
NS_ENSURE_ARG_POINTER(aMsgCount);
*aMsgCount = mDownloadQ.Length();
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::GetOwnerFolder(nsIMsgFolder** aFolder) {
NS_ENSURE_ARG_POINTER(aFolder);
nsresult rv;
nsCOMPtr<nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
ownerFolder.forget(aFolder);
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::Rollback() {
mOffset = mLastOffset;
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::ResetDownloadQ() {
mOffset = mLastOffset = 0;
mDownloadSet.Clear();
mDownloadQ.Clear();
mDownloadQ.Compact();
return NS_OK;
}
/**
* Tests whether the given folder is owned by the same imap server
* or not.
*/
NS_IMETHODIMP nsAutoSyncState::IsSibling(nsIAutoSyncState* aAnotherStateObj,
bool* aResult) {
NS_ENSURE_ARG_POINTER(aResult);
*aResult = false;
nsresult rv;
nsCOMPtr<nsIMsgFolder> folderA, folderB;
rv = GetOwnerFolder(getter_AddRefs(folderA));
NS_ENSURE_SUCCESS(rv, rv);
rv = aAnotherStateObj->GetOwnerFolder(getter_AddRefs(folderB));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMsgIncomingServer> serverA, serverB;
rv = folderA->GetServer(getter_AddRefs(serverA));
NS_ENSURE_SUCCESS(rv, rv);
rv = folderB->GetServer(getter_AddRefs(serverB));
NS_ENSURE_SUCCESS(rv, rv);
bool isSibling;
rv = serverA->Equals(serverB, &isSibling);
if (NS_SUCCEEDED(rv)) *aResult = isSibling;
return rv;
}
/**
* Test whether the download queue is empty.
*/
NS_IMETHODIMP nsAutoSyncState::IsDownloadQEmpty(bool* aResult) {
NS_ENSURE_ARG_POINTER(aResult);
*aResult = mDownloadQ.IsEmpty();
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::DownloadMessagesForOffline(
nsTArray<RefPtr<nsIMsgDBHdr>> const& messages) {
nsresult rv;
nsCOMPtr<nsIImapService> imapService =
do_GetService("@mozilla.org/messenger/imapservice;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString messageIds;
nsTArray<nsMsgKey> msgKeys;
rv = nsImapMailFolder::BuildIdsAndKeyArray(messages, messageIds, msgKeys);
if (NS_FAILED(rv) || messageIds.IsEmpty()) return rv;
// acquire semaphore for offline store. If it fails, we won't download
nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mOwnerFolder, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = folder->AcquireSemaphore(folder);
NS_ENSURE_SUCCESS(rv, rv);
if (MOZ_LOG_TEST(gAutoSyncLog, LogLevel::Debug)) {
nsCString folderName;
folder->GetURI(folderName);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("%s: downloading UIDs %s for folder %s", __func__,
messageIds.get(), folderName.get()));
}
// start downloading
rv = imapService->DownloadMessagesForOffline(messageIds, folder, this,
nullptr);
if (NS_SUCCEEDED(rv)) SetState(stDownloadInProgress);
return rv;
}
NS_IMETHODIMP nsAutoSyncState::GetLastSyncTime(PRTime* aLastSyncTime) {
NS_ENSURE_ARG_POINTER(aLastSyncTime);
*aLastSyncTime = mLastSyncTime;
return NS_OK;
}
void nsAutoSyncState::SetLastSyncTimeInSec(int32_t aLastSyncTime) {
mLastSyncTime = ((PRTime)aLastSyncTime * PR_USEC_PER_SEC);
}
NS_IMETHODIMP nsAutoSyncState::GetLastUpdateTime(PRTime* aLastUpdateTime) {
NS_ENSURE_ARG_POINTER(aLastUpdateTime);
*aLastUpdateTime = mLastUpdateTime;
return NS_OK;
}
NS_IMETHODIMP nsAutoSyncState::SetLastUpdateTime(PRTime aLastUpdateTime) {
mLastUpdateTime = aLastUpdateTime;
return NS_OK;
}
void nsAutoSyncState::SetServerCounts(int32_t total, int32_t recent,
int32_t unseen, int32_t nextUID) {
mLastServerTotal = total;
mLastServerRecent = recent;
mLastServerUnseen = unseen;
mLastNextUID = nextUID;
}
NS_IMPL_ISUPPORTS(nsAutoSyncState, nsIAutoSyncState, nsIUrlListener)
void nsAutoSyncState::LogQWithSize(nsTArray<nsMsgKey>& q, uint32_t toOffset) {
nsCOMPtr<nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder);
if (ownerFolder) {
nsCOMPtr<nsIMsgDatabase> database;
ownerFolder->GetMsgDatabase(getter_AddRefs(database));
uint32_t x = q.Length();
while (x > toOffset && database) {
x--;
nsCOMPtr<nsIMsgDBHdr> h;
database->GetMsgHdrForKey(q[x], getter_AddRefs(h));
uint32_t s;
if (h) {
h->GetMessageSize(&s);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("Elem #%d, size: %u bytes\n", x + 1, s));
} else
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("unable to get header for key %ul", q[x]));
}
}
}
void nsAutoSyncState::LogQWithSize(nsTArray<RefPtr<nsIMsgDBHdr>> const& q,
uint32_t toOffset) {
nsCOMPtr<nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder);
if (ownerFolder) {
nsCOMPtr<nsIMsgDatabase> database;
ownerFolder->GetMsgDatabase(getter_AddRefs(database));
uint32_t x = q.Length();
while (x > toOffset && database) {
x--;
if (q[x]) {
uint32_t s;
q[x]->GetMessageSize(&s);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("Elem #%d, size: %u bytes\n", x + 1, s));
} else
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("null header in q at index %ul", x));
}
}
}
void nsAutoSyncState::LogOwnerFolderName(const char* s) {
nsCOMPtr<nsIMsgFolder> ownerFolder = do_QueryReferent(mOwnerFolder);
if (ownerFolder) {
nsCString folderName;
ownerFolder->GetURI(folderName);
MOZ_LOG(gAutoSyncLog, LogLevel::Debug,
("*** %s Folder: %s ***\n", s, folderName.get()));
}
}