diff options
Diffstat (limited to 'comm/mailnews/imap/src/nsAutoSyncState.cpp')
-rw-r--r-- | comm/mailnews/imap/src/nsAutoSyncState.cpp | 782 |
1 files changed, 782 insertions, 0 deletions
diff --git a/comm/mailnews/imap/src/nsAutoSyncState.cpp b/comm/mailnews/imap/src/nsAutoSyncState.cpp new file mode 100644 index 0000000000..db3216419a --- /dev/null +++ b/comm/mailnews/imap/src/nsAutoSyncState.cpp @@ -0,0 +1,782 @@ +/* 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 "nsComponentManagerUtils.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())); + } +} |