summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/imap/src/nsImapOfflineSync.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/imap/src/nsImapOfflineSync.cpp')
-rw-r--r--comm/mailnews/imap/src/nsImapOfflineSync.cpp1175
1 files changed, 1175 insertions, 0 deletions
diff --git a/comm/mailnews/imap/src/nsImapOfflineSync.cpp b/comm/mailnews/imap/src/nsImapOfflineSync.cpp
new file mode 100644
index 0000000000..6b43a106bb
--- /dev/null
+++ b/comm/mailnews/imap/src/nsImapOfflineSync.cpp
@@ -0,0 +1,1175 @@
+/* -*- 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 "msgCore.h"
+#include "netCore.h"
+#include "nsNetUtil.h"
+#include "nsImapOfflineSync.h"
+#include "nsImapMailFolder.h"
+#include "nsMsgFolderFlags.h"
+#include "nsMsgMessageFlags.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsIMsgAccountManager.h"
+#include "nsINntpIncomingServer.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsISeekableStream.h"
+#include "nsIMsgCopyService.h"
+#include "nsImapProtocol.h"
+#include "nsMsgUtils.h"
+#include "nsIAutoSyncManager.h"
+#include "mozilla/Unused.h"
+
+NS_IMPL_ISUPPORTS(nsImapOfflineSync, nsIUrlListener, nsIMsgCopyServiceListener,
+ nsIDBChangeListener, nsIImapOfflineSync)
+
+nsImapOfflineSync::nsImapOfflineSync() {
+ m_singleFolderToUpdate = nullptr;
+ m_window = nullptr;
+ mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kFlagsChanged;
+ m_mailboxupdatesStarted = false;
+ m_mailboxupdatesFinished = false;
+ m_createdOfflineFolders = false;
+ m_pseudoOffline = false;
+ m_KeyIndex = 0;
+ mCurrentUIDValidity = nsMsgKey_None;
+ m_listener = nullptr;
+}
+
+NS_IMETHODIMP
+nsImapOfflineSync::Init(nsIMsgWindow* window, nsIUrlListener* listener,
+ nsIMsgFolder* singleFolderOnly, bool isPseudoOffline) {
+ m_window = window;
+ m_listener = listener;
+ m_singleFolderToUpdate = singleFolderOnly;
+ m_pseudoOffline = isPseudoOffline;
+
+ // not the perfect place for this, but I think it will work.
+ if (m_window) m_window->SetStopped(false);
+
+ return NS_OK;
+}
+
+nsImapOfflineSync::~nsImapOfflineSync() {}
+
+void nsImapOfflineSync::SetWindow(nsIMsgWindow* window) { m_window = window; }
+
+NS_IMETHODIMP nsImapOfflineSync::OnStartRunningUrl(nsIURI* url) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapOfflineSync::OnStopRunningUrl(nsIURI* url, nsresult exitCode) {
+ nsresult rv = exitCode;
+
+ // where do we make sure this gets cleared when we start running urls?
+ bool stopped = false;
+ if (m_window) m_window->GetStopped(&stopped);
+
+ if (m_curTempFile) {
+ m_curTempFile->Remove(false);
+ m_curTempFile = nullptr;
+ }
+ // NS_BINDING_ABORTED is used for the user pressing stop, which
+ // should cause us to abort the offline process. Other errors
+ // should allow us to continue.
+ if (stopped) {
+ if (m_listener) m_listener->OnStopRunningUrl(url, NS_BINDING_ABORTED);
+ return NS_OK;
+ }
+ nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(url);
+
+ if (imapUrl)
+ nsImapProtocol::LogImapUrl(NS_SUCCEEDED(rv) ? "offline imap url succeeded "
+ : "offline imap url failed ",
+ imapUrl);
+
+ // If we succeeded, or it was an imap move/copy that timed out, clear the
+ // operation.
+ bool moveCopy =
+ mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgCopy ||
+ mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kMsgMoved;
+ if (NS_SUCCEEDED(exitCode) || exitCode == NS_MSG_ERROR_IMAP_COMMAND_FAILED ||
+ (moveCopy && exitCode == NS_ERROR_NET_TIMEOUT)) {
+ ClearCurrentOps();
+ rv = ProcessNextOperation();
+ }
+ // else if it's a non-stop error, and we're doing multiple folders,
+ // go to the next folder.
+ else if (!m_singleFolderToUpdate) {
+ if (AdvanceToNextFolder())
+ rv = ProcessNextOperation();
+ else if (m_listener)
+ m_listener->OnStopRunningUrl(url, rv);
+ }
+
+ return rv;
+}
+
+/**
+ * Leaves m_currentServer at the next imap or local mail "server" that
+ * might have offline events to playback, and m_folderQueue holding
+ * a (reversed) list of all the folders to consider for that server.
+ * If no more servers, m_currentServer will be left at nullptr and the
+ * function returns false.
+ */
+bool nsImapOfflineSync::AdvanceToNextServer() {
+ nsresult rv = NS_OK;
+
+ if (m_allServers.IsEmpty()) {
+ NS_ASSERTION(!m_currentServer, "this shouldn't be set");
+ m_currentServer = nullptr;
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService("@mozilla.org/messenger/account-manager;1", &rv);
+ NS_ASSERTION(accountManager && NS_SUCCEEDED(rv),
+ "couldn't get account mgr");
+ if (!accountManager || NS_FAILED(rv)) return false;
+
+ rv = accountManager->GetAllServers(m_allServers);
+ NS_ENSURE_SUCCESS(rv, false);
+ }
+ size_t serverIndex = 0;
+ if (m_currentServer) {
+ serverIndex = m_allServers.IndexOf(m_currentServer);
+ if (serverIndex == m_allServers.NoIndex) {
+ serverIndex = 0;
+ } else {
+ // Move to the next server
+ ++serverIndex;
+ }
+ }
+ m_currentServer = nullptr;
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+
+ while (serverIndex < m_allServers.Length()) {
+ nsCOMPtr<nsIMsgIncomingServer> server(m_allServers[serverIndex]);
+ serverIndex++;
+
+ nsCOMPtr<nsINntpIncomingServer> newsServer = do_QueryInterface(server);
+ if (newsServer) // news servers aren't involved in offline imap
+ continue;
+
+ if (server) {
+ m_currentServer = server;
+ server->GetRootFolder(getter_AddRefs(rootFolder));
+ if (rootFolder) {
+ rv = rootFolder->GetDescendants(m_folderQueue);
+ if (NS_SUCCEEDED(rv)) {
+ if (!m_folderQueue.IsEmpty()) {
+ // We'll be popping folders off the end as they are processed.
+ m_folderQueue.Reverse();
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * Sets m_currentFolder to the next folder to process.
+ *
+ * @return True if next folder to process was found, otherwise false.
+ */
+bool nsImapOfflineSync::AdvanceToNextFolder() {
+ // we always start by changing flags
+ mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kFlagsChanged;
+
+ if (m_currentFolder) {
+ m_currentFolder->SetMsgDatabase(nullptr);
+ m_currentFolder = nullptr;
+ }
+
+ bool hasMore = false;
+ if (m_currentServer) {
+ hasMore = !m_folderQueue.IsEmpty();
+ }
+ if (!hasMore) {
+ hasMore = AdvanceToNextServer();
+ }
+ if (hasMore) {
+ m_currentFolder = m_folderQueue.PopLastElement();
+ }
+ ClearDB();
+ return m_currentFolder;
+}
+
+void nsImapOfflineSync::AdvanceToFirstIMAPFolder() {
+ m_currentServer = nullptr;
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder;
+ while (!imapFolder && AdvanceToNextFolder()) {
+ imapFolder = do_QueryInterface(m_currentFolder);
+ }
+}
+
+void nsImapOfflineSync::ProcessFlagOperation(nsIMsgOfflineImapOperation* op) {
+ nsCOMPtr<nsIMsgOfflineImapOperation> currentOp = op;
+ nsTArray<nsMsgKey> matchingFlagKeys;
+ uint32_t currentKeyIndex = m_KeyIndex;
+
+ imapMessageFlagsType matchingFlags;
+ currentOp->GetNewFlags(&matchingFlags);
+ bool flagsMatch = true;
+ do { // loop for all messages with the same flags
+ if (flagsMatch) {
+ nsMsgKey curKey;
+ currentOp->GetMessageKey(&curKey);
+ matchingFlagKeys.AppendElement(curKey);
+ currentOp->SetPlayingBack(true);
+ m_currentOpsToClear.AppendObject(currentOp);
+ }
+ currentOp = nullptr;
+ imapMessageFlagsType newFlags = kNoImapMsgFlag;
+ imapMessageFlagsType flagOperation = kNoImapMsgFlag;
+ if (++currentKeyIndex < m_CurrentKeys.Length())
+ m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], false,
+ getter_AddRefs(currentOp));
+ if (currentOp) {
+ currentOp->GetFlagOperation(&flagOperation);
+ currentOp->GetNewFlags(&newFlags);
+ }
+ flagsMatch = (flagOperation & nsIMsgOfflineImapOperation::kFlagsChanged) &&
+ (newFlags == matchingFlags);
+ } while (currentOp);
+
+ if (!matchingFlagKeys.IsEmpty()) {
+ nsAutoCString uids;
+ nsImapMailFolder::AllocateUidStringFromKeys(matchingFlagKeys, uids);
+ uint32_t curFolderFlags;
+ m_currentFolder->GetFlags(&curFolderFlags);
+
+ if (uids.get() && (curFolderFlags & nsMsgFolderFlags::ImapBox)) {
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
+ do_QueryInterface(m_currentFolder);
+ nsCOMPtr<nsIURI> uriToSetFlags;
+ if (imapFolder) {
+ rv = imapFolder->SetImapFlags(uids.get(), matchingFlags,
+ getter_AddRefs(uriToSetFlags));
+ if (NS_SUCCEEDED(rv) && uriToSetFlags) {
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl =
+ do_QueryInterface(uriToSetFlags);
+ if (mailnewsUrl) mailnewsUrl->RegisterListener(this);
+ }
+ }
+ }
+ } else
+ ProcessNextOperation();
+}
+
+void nsImapOfflineSync::ProcessKeywordOperation(
+ nsIMsgOfflineImapOperation* op) {
+ nsCOMPtr<nsIMsgOfflineImapOperation> currentOp = op;
+ nsTArray<nsMsgKey> matchingKeywordKeys;
+ uint32_t currentKeyIndex = m_KeyIndex;
+
+ nsAutoCString keywords;
+ if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
+ currentOp->GetKeywordsToAdd(getter_Copies(keywords));
+ else
+ currentOp->GetKeywordsToRemove(getter_Copies(keywords));
+ bool keywordsMatch = true;
+ do { // loop for all messages with the same keywords
+ if (keywordsMatch) {
+ nsMsgKey curKey;
+ currentOp->GetMessageKey(&curKey);
+ matchingKeywordKeys.AppendElement(curKey);
+ currentOp->SetPlayingBack(true);
+ m_currentOpsToClear.AppendObject(currentOp);
+ }
+ currentOp = nullptr;
+ if (++currentKeyIndex < m_CurrentKeys.Length())
+ m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex], false,
+ getter_AddRefs(currentOp));
+ if (currentOp) {
+ nsAutoCString curOpKeywords;
+ nsOfflineImapOperationType operation;
+ currentOp->GetOperation(&operation);
+ if (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
+ currentOp->GetKeywordsToAdd(getter_Copies(curOpKeywords));
+ else
+ currentOp->GetKeywordsToRemove(getter_Copies(curOpKeywords));
+ keywordsMatch = (operation & mCurrentPlaybackOpType) &&
+ (curOpKeywords.Equals(keywords));
+ }
+ } while (currentOp);
+
+ if (!matchingKeywordKeys.IsEmpty()) {
+ uint32_t curFolderFlags;
+ m_currentFolder->GetFlags(&curFolderFlags);
+
+ if (curFolderFlags & nsMsgFolderFlags::ImapBox) {
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
+ do_QueryInterface(m_currentFolder);
+ nsCOMPtr<nsIURI> uriToStoreCustomKeywords;
+ if (imapFolder) {
+ rv = imapFolder->StoreCustomKeywords(
+ m_window,
+ (mCurrentPlaybackOpType == nsIMsgOfflineImapOperation::kAddKeywords)
+ ? keywords
+ : EmptyCString(),
+ (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kRemoveKeywords)
+ ? keywords
+ : EmptyCString(),
+ matchingKeywordKeys, getter_AddRefs(uriToStoreCustomKeywords));
+ if (NS_SUCCEEDED(rv) && uriToStoreCustomKeywords) {
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl =
+ do_QueryInterface(uriToStoreCustomKeywords);
+ if (mailnewsUrl) mailnewsUrl->RegisterListener(this);
+ }
+ }
+ }
+ } else
+ ProcessNextOperation();
+}
+
+// XXX This should not be void but return an error to indicate which low
+// level routine failed.
+void nsImapOfflineSync::ProcessAppendMsgOperation(
+ nsIMsgOfflineImapOperation* currentOp, int32_t opType) {
+ nsMsgKey msgKey;
+ currentOp->GetMessageKey(&msgKey);
+ nsCOMPtr<nsIMsgDBHdr> mailHdr;
+ nsresult rv = m_currentDB->GetMsgHdrForKey(msgKey, getter_AddRefs(mailHdr));
+ if (NS_FAILED(rv) || !mailHdr) {
+ m_currentDB->RemoveOfflineOp(currentOp);
+ ProcessNextOperation();
+ return;
+ }
+
+ uint64_t messageOffset;
+ uint32_t messageSize;
+ mailHdr->GetMessageOffset(&messageOffset);
+ mailHdr->GetOfflineMessageSize(&messageSize);
+ nsCOMPtr<nsIFile> tmpFile;
+
+ if (NS_WARN_IF(NS_FAILED(GetSpecialDirectoryWithFileName(
+ NS_OS_TEMP_DIR, "nscpmsg.txt", getter_AddRefs(tmpFile)))))
+ return;
+
+ if (NS_WARN_IF(
+ NS_FAILED(tmpFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 00600))))
+ return;
+
+ nsCOMPtr<nsIOutputStream> outputStream;
+ rv = MsgNewBufferedFileOutputStream(getter_AddRefs(outputStream), tmpFile,
+ PR_WRONLY | PR_CREATE_FILE, 00600);
+ if (NS_WARN_IF(NS_FAILED(rv) || !outputStream)) return;
+
+ // We break out of the loop to get to the clean-up code.
+ bool setPlayingBack = false;
+ do {
+ nsCString moveDestination;
+ currentOp->GetDestinationFolderURI(moveDestination);
+
+ nsCOMPtr<nsIMsgFolder> destFolder;
+ rv = GetOrCreateFolder(moveDestination, getter_AddRefs(destFolder));
+ if (NS_WARN_IF(NS_FAILED(rv))) break;
+
+ nsCOMPtr<nsIInputStream> offlineStoreInputStream;
+ rv = destFolder->GetMsgInputStream(mailHdr,
+ getter_AddRefs(offlineStoreInputStream));
+ if (NS_WARN_IF((NS_FAILED(rv) || !offlineStoreInputStream))) break;
+
+ nsCOMPtr<nsISeekableStream> seekStream =
+ do_QueryInterface(offlineStoreInputStream);
+ MOZ_ASSERT(seekStream, "non seekable stream - can't read from offline msg");
+ if (!seekStream) break;
+
+ // From this point onwards, we need to set "playing back".
+ setPlayingBack = true;
+
+ rv = seekStream->Seek(PR_SEEK_SET, messageOffset);
+ if (NS_WARN_IF(NS_FAILED(rv))) break;
+
+ // Copy the dest folder offline store msg to the temp file.
+ int32_t inputBufferSize = FILE_IO_BUFFER_SIZE;
+ char* inputBuffer = (char*)PR_Malloc(inputBufferSize);
+ int32_t bytesLeft;
+ uint32_t bytesRead, bytesWritten;
+
+ bytesLeft = messageSize;
+ rv = inputBuffer ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+ while (bytesLeft > 0 && NS_SUCCEEDED(rv)) {
+ int32_t bytesToRead = std::min(inputBufferSize, bytesLeft);
+ rv = offlineStoreInputStream->Read(inputBuffer, bytesToRead, &bytesRead);
+ if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) break;
+ rv = outputStream->Write(inputBuffer, bytesRead, &bytesWritten);
+ if (NS_WARN_IF(NS_FAILED(rv))) break;
+ MOZ_ASSERT(bytesWritten == bytesRead,
+ "wrote out incorrect number of bytes");
+ bytesLeft -= bytesRead;
+ }
+ PR_FREEIF(inputBuffer);
+
+ // rv could have an error from Read/Write.
+ nsresult rv2 = outputStream->Close();
+ if (NS_FAILED(rv2)) {
+ NS_WARNING("ouputStream->Close() failed");
+ }
+ outputStream = nullptr; // Don't try to close it again below.
+
+ // rv: Read/Write, rv2: Close
+ if (NS_FAILED(rv) || NS_FAILED(rv2)) {
+ // This Remove() will fail under Windows if the output stream
+ // fails to close above.
+ mozilla::Unused << NS_WARN_IF(NS_FAILED(tmpFile->Remove(false)));
+ break;
+ }
+
+ nsCOMPtr<nsIFile> cloneTmpFile;
+ // clone the tmp file to defeat nsIFile's stat/size caching.
+ tmpFile->Clone(getter_AddRefs(cloneTmpFile));
+ m_curTempFile = cloneTmpFile;
+ nsCOMPtr<nsIMsgCopyService> copyService =
+ do_GetService("@mozilla.org/messenger/messagecopyservice;1");
+
+ // CopyFileMessage returns error async to this->OnStopCopy
+ // if copyService is null, let's crash here and now.
+ rv = copyService->CopyFileMessage(cloneTmpFile, destFolder,
+ nullptr, // nsIMsgDBHdr* msgToReplace
+ true, // isDraftOrTemplate
+ 0, // new msg flags
+ EmptyCString(), this, m_window);
+ MOZ_ASSERT(NS_SUCCEEDED(rv),
+ "CopyFileMessage() failed. Fatal. Error in call setup?");
+ } while (false);
+
+ if (setPlayingBack) {
+ currentOp->SetPlayingBack(true);
+ m_currentOpsToClear.AppendObject(currentOp);
+ m_currentDB->DeleteHeader(mailHdr, nullptr, true, true);
+ }
+
+ // Close the output stream if it's not already closed.
+ if (outputStream)
+ mozilla::Unused << NS_WARN_IF(NS_FAILED(outputStream->Close()));
+}
+
+void nsImapOfflineSync::ClearCurrentOps() {
+ int32_t opCount = m_currentOpsToClear.Count();
+ for (int32_t i = opCount - 1; i >= 0; i--) {
+ m_currentOpsToClear[i]->SetPlayingBack(false);
+ m_currentOpsToClear[i]->ClearOperation(mCurrentPlaybackOpType);
+ m_currentOpsToClear.RemoveObjectAt(i);
+ }
+}
+
+void nsImapOfflineSync::ProcessMoveOperation(nsIMsgOfflineImapOperation* op) {
+ nsTArray<nsMsgKey> matchingFlagKeys;
+ uint32_t currentKeyIndex = m_KeyIndex;
+ nsCString moveDestination;
+ op->GetDestinationFolderURI(moveDestination);
+ bool moveMatches = true;
+ nsCOMPtr<nsIMsgOfflineImapOperation> currentOp = op;
+ do { // loop for all messages with the same destination
+ if (moveMatches) {
+ nsMsgKey curKey;
+ currentOp->GetMessageKey(&curKey);
+ matchingFlagKeys.AppendElement(curKey);
+ currentOp->SetPlayingBack(true);
+ m_currentOpsToClear.AppendObject(currentOp);
+ }
+ currentOp = nullptr;
+
+ if (++currentKeyIndex < m_CurrentKeys.Length()) {
+ nsCString nextDestination;
+ nsresult rv = m_currentDB->GetOfflineOpForKey(
+ m_CurrentKeys[currentKeyIndex], false, getter_AddRefs(currentOp));
+ moveMatches = false;
+ if (NS_SUCCEEDED(rv) && currentOp) {
+ nsOfflineImapOperationType opType;
+ currentOp->GetOperation(&opType);
+ if (opType & nsIMsgOfflineImapOperation::kMsgMoved) {
+ currentOp->GetDestinationFolderURI(nextDestination);
+ moveMatches = moveDestination.Equals(nextDestination);
+ }
+ }
+ }
+ } while (currentOp);
+
+ nsCOMPtr<nsIMsgFolder> destFolder;
+ FindFolder(moveDestination, getter_AddRefs(destFolder));
+ // if the dest folder doesn't really exist, these operations are
+ // going to fail, so clear them out and move on.
+ if (!destFolder) {
+ NS_WARNING("trying to playing back move to non-existent folder");
+ ClearCurrentOps();
+ ProcessNextOperation();
+ return;
+ }
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
+ do_QueryInterface(m_currentFolder);
+ if (imapFolder && DestFolderOnSameServer(destFolder)) {
+ uint32_t curFolderFlags;
+ m_currentFolder->GetFlags(&curFolderFlags);
+ bool curFolderOffline = curFolderFlags & nsMsgFolderFlags::Offline;
+ imapFolder->ReplayOfflineMoveCopy(matchingFlagKeys, true, destFolder, this,
+ m_window, curFolderOffline);
+ } else {
+ nsresult rv;
+ nsTArray<RefPtr<nsIMsgDBHdr>> messages;
+ for (uint32_t keyIndex = 0; keyIndex < matchingFlagKeys.Length();
+ keyIndex++) {
+ nsCOMPtr<nsIMsgDBHdr> mailHdr = nullptr;
+ rv = m_currentFolder->GetMessageHeader(
+ matchingFlagKeys.ElementAt(keyIndex), getter_AddRefs(mailHdr));
+ if (NS_SUCCEEDED(rv) && mailHdr) {
+ uint32_t msgSize;
+ // in case of a move, the header has already been deleted,
+ // so we've really got a fake header. We need to get its flags and
+ // size from the offline op to have any chance of doing the move.
+ mailHdr->GetMessageSize(&msgSize);
+ if (!msgSize) {
+ imapMessageFlagsType newImapFlags;
+ uint32_t msgFlags = 0;
+ op->GetMsgSize(&msgSize);
+ op->GetNewFlags(&newImapFlags);
+ // first three bits are the same
+ msgFlags |= (newImapFlags & 0x07);
+ if (newImapFlags & kImapMsgForwardedFlag)
+ msgFlags |= nsMsgMessageFlags::Forwarded;
+ mailHdr->SetFlags(msgFlags);
+ mailHdr->SetMessageSize(msgSize);
+ }
+ messages.AppendElement(mailHdr);
+ }
+ }
+ nsCOMPtr<nsIMsgCopyService> copyService =
+ do_GetService("@mozilla.org/messenger/messagecopyservice;1", &rv);
+ if (copyService) {
+ copyService->CopyMessages(m_currentFolder, messages, destFolder, true,
+ this, m_window, false);
+ }
+ }
+}
+
+// I'm tempted to make this a method on nsIMsgFolder, but that interface
+// is already so huge, and there are only a few places in the code that do this.
+// If there end up to be more places that need this, then we can reconsider.
+bool nsImapOfflineSync::DestFolderOnSameServer(nsIMsgFolder* destFolder) {
+ nsCOMPtr<nsIMsgIncomingServer> srcServer;
+ nsCOMPtr<nsIMsgIncomingServer> dstServer;
+
+ bool sameServer = false;
+ if (NS_SUCCEEDED(m_currentFolder->GetServer(getter_AddRefs(srcServer))) &&
+ NS_SUCCEEDED(destFolder->GetServer(getter_AddRefs(dstServer))))
+ dstServer->Equals(srcServer, &sameServer);
+ return sameServer;
+}
+
+void nsImapOfflineSync::ProcessCopyOperation(
+ nsIMsgOfflineImapOperation* aCurrentOp) {
+ nsCOMPtr<nsIMsgOfflineImapOperation> currentOp = aCurrentOp;
+
+ nsTArray<nsMsgKey> matchingFlagKeys;
+ uint32_t currentKeyIndex = m_KeyIndex;
+ nsCString copyDestination;
+ currentOp->GetCopyDestination(0, getter_Copies(copyDestination));
+ bool copyMatches = true;
+ nsresult rv;
+
+ do { // loop for all messages with the same destination
+ if (copyMatches) {
+ nsMsgKey curKey;
+ currentOp->GetMessageKey(&curKey);
+ matchingFlagKeys.AppendElement(curKey);
+ currentOp->SetPlayingBack(true);
+ m_currentOpsToClear.AppendObject(currentOp);
+ }
+ currentOp = nullptr;
+
+ if (++currentKeyIndex < m_CurrentKeys.Length()) {
+ nsCString nextDestination;
+ rv = m_currentDB->GetOfflineOpForKey(m_CurrentKeys[currentKeyIndex],
+ false, getter_AddRefs(currentOp));
+ copyMatches = false;
+ if (NS_SUCCEEDED(rv) && currentOp) {
+ nsOfflineImapOperationType opType;
+ currentOp->GetOperation(&opType);
+ if (opType & nsIMsgOfflineImapOperation::kMsgCopy) {
+ currentOp->GetCopyDestination(0, getter_Copies(nextDestination));
+ copyMatches = copyDestination.Equals(nextDestination);
+ }
+ }
+ }
+ } while (currentOp);
+
+ nsAutoCString uids;
+ nsCOMPtr<nsIMsgFolder> destFolder;
+ FindFolder(copyDestination, getter_AddRefs(destFolder));
+ // if the dest folder doesn't really exist, these operations are
+ // going to fail, so clear them out and move on.
+ if (!destFolder) {
+ NS_ERROR("trying to playing back copy to non-existent folder");
+ ClearCurrentOps();
+ ProcessNextOperation();
+ return;
+ }
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
+ do_QueryInterface(m_currentFolder);
+ if (imapFolder && DestFolderOnSameServer(destFolder)) {
+ uint32_t curFolderFlags;
+ m_currentFolder->GetFlags(&curFolderFlags);
+ bool curFolderOffline = curFolderFlags & nsMsgFolderFlags::Offline;
+ rv = imapFolder->ReplayOfflineMoveCopy(matchingFlagKeys, false, destFolder,
+ this, m_window, curFolderOffline);
+ } else {
+ nsTArray<RefPtr<nsIMsgDBHdr>> messages;
+ for (uint32_t keyIndex = 0; keyIndex < matchingFlagKeys.Length();
+ keyIndex++) {
+ nsCOMPtr<nsIMsgDBHdr> mailHdr = nullptr;
+ rv = m_currentFolder->GetMessageHeader(
+ matchingFlagKeys.ElementAt(keyIndex), getter_AddRefs(mailHdr));
+ if (NS_SUCCEEDED(rv) && mailHdr) messages.AppendElement(mailHdr);
+ }
+ nsCOMPtr<nsIMsgCopyService> copyService =
+ do_GetService("@mozilla.org/messenger/messagecopyservice;1", &rv);
+ if (copyService)
+ copyService->CopyMessages(m_currentFolder, messages, destFolder, false,
+ this, m_window, false);
+ }
+}
+
+void nsImapOfflineSync::ProcessEmptyTrash() {
+ m_currentFolder->EmptyTrash(this);
+ ClearDB(); // EmptyTrash closes and deletes the trash db.
+}
+
+// returns true if we found a folder to create, false if we're done creating
+// folders.
+bool nsImapOfflineSync::CreateOfflineFolders() {
+ while (m_currentFolder) {
+ uint32_t flags;
+ m_currentFolder->GetFlags(&flags);
+ bool offlineCreate = (flags & nsMsgFolderFlags::CreatedOffline) != 0;
+ if (offlineCreate) {
+ if (CreateOfflineFolder(m_currentFolder)) return true;
+ }
+ AdvanceToNextFolder();
+ }
+ return false;
+}
+
+bool nsImapOfflineSync::CreateOfflineFolder(nsIMsgFolder* folder) {
+ nsCOMPtr<nsIMsgFolder> parent;
+ folder->GetParent(getter_AddRefs(parent));
+
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(parent);
+ nsCOMPtr<nsIURI> createFolderURI;
+ nsCString onlineName;
+ imapFolder->GetOnlineName(onlineName);
+
+ NS_ConvertASCIItoUTF16 folderName(onlineName);
+ nsresult rv = imapFolder->PlaybackOfflineFolderCreate(
+ folderName, nullptr, getter_AddRefs(createFolderURI));
+ if (createFolderURI && NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIMsgMailNewsUrl> mailnewsUrl =
+ do_QueryInterface(createFolderURI);
+ if (mailnewsUrl) mailnewsUrl->RegisterListener(this);
+ }
+ return NS_SUCCEEDED(rv) ? true
+ : false; // this is asynch, we have to return and be
+ // called again by the OfflineOpExitFunction
+}
+
+int32_t nsImapOfflineSync::GetCurrentUIDValidity() {
+ if (m_currentFolder) {
+ nsCOMPtr<nsIImapMailFolderSink> imapFolderSink =
+ do_QueryInterface(m_currentFolder);
+ if (imapFolderSink) imapFolderSink->GetUidValidity(&mCurrentUIDValidity);
+ }
+ return mCurrentUIDValidity;
+}
+
+/**
+ * Playing back offline operations is one giant state machine that runs through
+ * ProcessNextOperation.
+ * The first state is creating online any folders created offline (we do this
+ * first, so we can play back any operations in them in the next pass)
+ */
+NS_IMETHODIMP
+nsImapOfflineSync::ProcessNextOperation() {
+ nsresult rv = NS_OK;
+
+ // if we haven't created offline folders, and we're updating all folders,
+ // first, find offline folders to create.
+ if (!m_createdOfflineFolders) {
+ if (m_singleFolderToUpdate) {
+ if (!m_pseudoOffline) {
+ AdvanceToFirstIMAPFolder();
+ if (CreateOfflineFolders()) return NS_OK;
+ }
+ } else {
+ if (CreateOfflineFolders()) return NS_OK;
+ m_currentServer = nullptr;
+ AdvanceToNextFolder();
+ }
+ m_createdOfflineFolders = true;
+ }
+ // if updating one folder only, restore m_currentFolder to that folder
+ if (m_singleFolderToUpdate) m_currentFolder = m_singleFolderToUpdate;
+
+ uint32_t folderFlags;
+ nsCOMPtr<nsIDBFolderInfo> folderInfo;
+ while (m_currentFolder && !m_currentDB) {
+ m_currentFolder->GetFlags(&folderFlags);
+ // need to check if folder has offline events, /* or is configured for
+ // offline */ shouldn't need to check if configured for offline use, since
+ // any folder with events should have nsMsgFolderFlags::OfflineEvents set.
+ if (folderFlags &
+ (nsMsgFolderFlags::OfflineEvents /* | nsMsgFolderFlags::Offline */)) {
+ nsCOMPtr<nsIMsgDatabase> db;
+ m_currentFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo),
+ getter_AddRefs(db));
+ if (db) {
+ m_currentDB = do_QueryInterface(db, &rv);
+ m_currentDB->AddListener(this);
+ }
+ }
+
+ if (m_currentDB) {
+ m_CurrentKeys.Clear();
+ m_KeyIndex = 0;
+ if (NS_FAILED(m_currentDB->ListAllOfflineOpIds(m_CurrentKeys)) ||
+ m_CurrentKeys.IsEmpty()) {
+ ClearDB();
+ folderInfo = nullptr; // can't hold onto folderInfo longer than db
+ m_currentFolder->ClearFlag(nsMsgFolderFlags::OfflineEvents);
+ } else {
+ // trash any ghost msgs
+ bool deletedGhostMsgs = false;
+ for (uint32_t fakeIndex = 0; fakeIndex < m_CurrentKeys.Length();
+ fakeIndex++) {
+ nsCOMPtr<nsIMsgOfflineImapOperation> currentOp;
+ m_currentDB->GetOfflineOpForKey(m_CurrentKeys[fakeIndex], false,
+ getter_AddRefs(currentOp));
+ if (currentOp) {
+ nsOfflineImapOperationType opType;
+ currentOp->GetOperation(&opType);
+
+ if (opType == nsIMsgOfflineImapOperation::kMoveResult) {
+ nsMsgKey curKey;
+ currentOp->GetMessageKey(&curKey);
+ m_currentDB->RemoveOfflineOp(currentOp);
+ deletedGhostMsgs = true;
+
+ // Remember the pseudo headers before we delete them,
+ // and when we download new headers, tell listeners about the
+ // message key change between the pseudo headers and the real
+ // downloaded headers. Note that we're not currently sending
+ // a msgsDeleted notification for these headers, but the
+ // db listeners are notified about the deletion.
+ // for imap folders, we should adjust the pending counts, because
+ // we have a header that we know about, but don't have in the db.
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
+ do_QueryInterface(m_currentFolder);
+ if (imapFolder) {
+ bool hdrIsRead;
+ m_currentDB->IsRead(curKey, &hdrIsRead);
+ imapFolder->ChangePendingTotal(1);
+ if (!hdrIsRead) imapFolder->ChangePendingUnread(1);
+ imapFolder->AddMoveResultPseudoKey(curKey);
+ }
+ m_currentDB->DeleteMessage(curKey, nullptr, false);
+ }
+ }
+ }
+
+ if (deletedGhostMsgs) m_currentFolder->SummaryChanged();
+
+ m_CurrentKeys.Clear();
+ if (NS_FAILED(m_currentDB->ListAllOfflineOpIds(m_CurrentKeys)) ||
+ m_CurrentKeys.IsEmpty()) {
+ ClearDB();
+ } else if (folderFlags & nsMsgFolderFlags::ImapBox) {
+ // if pseudo offline, falls through to playing ops back.
+ if (!m_pseudoOffline) {
+ // there are operations to playback so check uid validity
+ SetCurrentUIDValidity(0); // force initial invalid state
+ // do a lite select here and hook ourselves up as a listener.
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder =
+ do_QueryInterface(m_currentFolder, &rv);
+ if (imapFolder) rv = imapFolder->LiteSelect(this, m_window);
+ // this is async, we will be called again by OnStopRunningUrl.
+ return rv;
+ }
+ }
+ }
+ }
+
+ if (!m_currentDB) {
+ // only advance if we are doing all folders
+ if (!m_singleFolderToUpdate)
+ AdvanceToNextFolder();
+ else
+ m_currentFolder = nullptr; // force update of this folder now.
+ }
+ }
+
+ if (m_currentFolder) m_currentFolder->GetFlags(&folderFlags);
+ // do the current operation
+ if (m_currentDB) {
+ bool currentFolderFinished = false;
+ if (!folderInfo) m_currentDB->GetDBFolderInfo(getter_AddRefs(folderInfo));
+ // user canceled the lite select! if GetCurrentUIDValidity() == 0
+ if (folderInfo && (m_KeyIndex < m_CurrentKeys.Length()) &&
+ (m_pseudoOffline || (GetCurrentUIDValidity() != 0) ||
+ !(folderFlags & nsMsgFolderFlags::ImapBox))) {
+ int32_t curFolderUidValidity;
+ folderInfo->GetImapUidValidity(&curFolderUidValidity);
+ bool uidvalidityChanged =
+ (!m_pseudoOffline && folderFlags & nsMsgFolderFlags::ImapBox) &&
+ (GetCurrentUIDValidity() != curFolderUidValidity);
+ nsCOMPtr<nsIMsgOfflineImapOperation> currentOp;
+ if (uidvalidityChanged)
+ DeleteAllOfflineOpsForCurrentDB();
+ else
+ m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], false,
+ getter_AddRefs(currentOp));
+
+ if (currentOp) {
+ nsOfflineImapOperationType opType;
+ currentOp->GetOperation(&opType);
+ // loop until we find the next db record that matches the current
+ // playback operation
+ while (currentOp && !(opType & mCurrentPlaybackOpType)) {
+ // remove operations with no type.
+ if (!opType) m_currentDB->RemoveOfflineOp(currentOp);
+ currentOp = nullptr;
+ ++m_KeyIndex;
+ if (m_KeyIndex < m_CurrentKeys.Length())
+ m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], false,
+ getter_AddRefs(currentOp));
+ if (currentOp) currentOp->GetOperation(&opType);
+ }
+ // if we did not find a db record that matches the current playback
+ // operation, then move to the next playback operation and recurse.
+ if (!currentOp) {
+ // we are done with the current type
+ if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kFlagsChanged) {
+ mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kAddKeywords;
+ // recurse to deal with next type of operation
+ m_KeyIndex = 0;
+ ProcessNextOperation();
+ } else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kAddKeywords) {
+ mCurrentPlaybackOpType =
+ nsIMsgOfflineImapOperation::kRemoveKeywords;
+ // recurse to deal with next type of operation
+ m_KeyIndex = 0;
+ ProcessNextOperation();
+ } else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kRemoveKeywords) {
+ mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kMsgCopy;
+ // recurse to deal with next type of operation
+ m_KeyIndex = 0;
+ ProcessNextOperation();
+ } else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kMsgCopy) {
+ mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kMsgMoved;
+ // recurse to deal with next type of operation
+ m_KeyIndex = 0;
+ ProcessNextOperation();
+ } else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kMsgMoved) {
+ mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kAppendDraft;
+ // recurse to deal with next type of operation
+ m_KeyIndex = 0;
+ ProcessNextOperation();
+ } else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kAppendDraft) {
+ mCurrentPlaybackOpType =
+ nsIMsgOfflineImapOperation::kAppendTemplate;
+ // recurse to deal with next type of operation
+ m_KeyIndex = 0;
+ ProcessNextOperation();
+ } else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kAppendTemplate) {
+ mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kDeleteAllMsgs;
+ m_KeyIndex = 0;
+ ProcessNextOperation();
+ } else {
+ DeleteAllOfflineOpsForCurrentDB();
+ currentFolderFinished = true;
+ }
+
+ } else {
+ if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kFlagsChanged)
+ ProcessFlagOperation(currentOp);
+ else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kAddKeywords ||
+ mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kRemoveKeywords)
+ ProcessKeywordOperation(currentOp);
+ else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kMsgCopy)
+ ProcessCopyOperation(currentOp);
+ else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kMsgMoved)
+ ProcessMoveOperation(currentOp);
+ else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kAppendDraft)
+ ProcessAppendMsgOperation(currentOp,
+ nsIMsgOfflineImapOperation::kAppendDraft);
+ else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kAppendTemplate)
+ ProcessAppendMsgOperation(
+ currentOp, nsIMsgOfflineImapOperation::kAppendTemplate);
+ else if (mCurrentPlaybackOpType ==
+ nsIMsgOfflineImapOperation::kDeleteAllMsgs) {
+ // empty trash is going to delete the db, so we'd better release the
+ // reference to the offline operation first.
+ currentOp = nullptr;
+ ProcessEmptyTrash();
+ } else
+ NS_WARNING("invalid playback op type");
+ }
+ } else
+ currentFolderFinished = true;
+ } else
+ currentFolderFinished = true;
+
+ if (currentFolderFinished) {
+ ClearDB();
+ if (!m_singleFolderToUpdate) {
+ AdvanceToNextFolder();
+ ProcessNextOperation();
+ return NS_OK;
+ }
+ m_currentFolder = nullptr;
+ }
+ }
+
+ if (!m_currentFolder && !m_mailboxupdatesStarted) {
+ m_mailboxupdatesStarted = true;
+
+ // if we are updating more than one folder then we need the iterator
+ if (!m_singleFolderToUpdate) {
+ m_currentServer = nullptr;
+ AdvanceToNextFolder();
+ }
+ if (m_singleFolderToUpdate) {
+ m_singleFolderToUpdate->ClearFlag(nsMsgFolderFlags::OfflineEvents);
+ m_singleFolderToUpdate->UpdateFolder(m_window);
+ }
+ }
+ // if we get here, then I *think* we're done. Not sure, though.
+#ifdef DEBUG_bienvenu
+ printf("done with offline imap sync\n");
+#endif
+ nsCOMPtr<nsIUrlListener> saveListener = m_listener;
+ m_listener = nullptr;
+
+ if (saveListener)
+ saveListener->OnStopRunningUrl(nullptr /* don't know url */, rv);
+ return rv;
+}
+
+void nsImapOfflineSync::DeleteAllOfflineOpsForCurrentDB() {
+ m_KeyIndex = 0;
+ nsCOMPtr<nsIMsgOfflineImapOperation> currentOp;
+ m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], false,
+ getter_AddRefs(currentOp));
+ while (currentOp) {
+ // NS_ASSERTION(currentOp->GetOperationFlags() == 0);
+ // delete any ops that have already played back
+ m_currentDB->RemoveOfflineOp(currentOp);
+ currentOp = nullptr;
+
+ if (++m_KeyIndex < m_CurrentKeys.Length())
+ m_currentDB->GetOfflineOpForKey(m_CurrentKeys[m_KeyIndex], false,
+ getter_AddRefs(currentOp));
+ }
+ m_currentDB->Commit(nsMsgDBCommitType::kLargeCommit);
+ // turn off nsMsgFolderFlags::OfflineEvents
+ if (m_currentFolder)
+ m_currentFolder->ClearFlag(nsMsgFolderFlags::OfflineEvents);
+}
+
+nsImapOfflineDownloader::nsImapOfflineDownloader(nsIMsgWindow* aMsgWindow,
+ nsIUrlListener* aListener)
+ : nsImapOfflineSync() {
+ Init(aMsgWindow, aListener, nullptr, false);
+ // pause auto-sync service
+ nsresult rv;
+ nsCOMPtr<nsIAutoSyncManager> autoSyncMgr =
+ do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) autoSyncMgr->Pause();
+}
+
+nsImapOfflineDownloader::~nsImapOfflineDownloader() {}
+
+NS_IMETHODIMP
+nsImapOfflineDownloader::ProcessNextOperation() {
+ nsresult rv = NS_OK;
+ m_mailboxupdatesStarted = true;
+
+ if (!m_mailboxupdatesFinished) {
+ if (AdvanceToNextServer()) {
+ nsCOMPtr<nsIMsgFolder> rootMsgFolder;
+ m_currentServer->GetRootFolder(getter_AddRefs(rootMsgFolder));
+ nsCOMPtr<nsIMsgFolder> inbox;
+ if (rootMsgFolder) {
+ // Update the INBOX first so the updates on the remaining
+ // folders pickup the results of any filter moves.
+ rootMsgFolder->GetFolderWithFlags(nsMsgFolderFlags::Inbox,
+ getter_AddRefs(inbox));
+ if (inbox) {
+ nsCOMPtr<nsIMsgFolder> offlineImapFolder;
+ nsCOMPtr<nsIMsgImapMailFolder> imapInbox = do_QueryInterface(inbox);
+ if (imapInbox) {
+ rootMsgFolder->GetFolderWithFlags(
+ nsMsgFolderFlags::Offline, getter_AddRefs(offlineImapFolder));
+ if (!offlineImapFolder) {
+ // no imap folders configured for offline use - check if the
+ // account is set up so that we always download inbox msg bodies
+ // for offline use
+ nsCOMPtr<nsIImapIncomingServer> imapServer =
+ do_QueryInterface(m_currentServer);
+ if (imapServer) {
+ bool downloadBodiesOnGetNewMail = false;
+ imapServer->GetDownloadBodiesOnGetNewMail(
+ &downloadBodiesOnGetNewMail);
+ if (downloadBodiesOnGetNewMail) offlineImapFolder = inbox;
+ }
+ }
+ }
+ // if this isn't an imap inbox, or we have an offline imap sub-folder,
+ // then update the inbox. otherwise, it's an imap inbox for an account
+ // with no folders configured for offline use, so just advance to the
+ // next server.
+ if (!imapInbox || offlineImapFolder) {
+ // here we should check if this a pop3 server/inbox, and the user
+ // doesn't want to download pop3 mail for offline use.
+ if (!imapInbox) {
+ }
+ rv = inbox->GetNewMessages(m_window, this);
+ if (NS_SUCCEEDED(rv)) return rv; // otherwise, fall through.
+ }
+ }
+ }
+ return ProcessNextOperation(); // recurse and do next server.
+ }
+ m_allServers.Clear();
+ m_mailboxupdatesFinished = true;
+ }
+
+ while (AdvanceToNextFolder()) {
+ uint32_t folderFlags;
+
+ ClearDB();
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder;
+ if (m_currentFolder) imapFolder = do_QueryInterface(m_currentFolder);
+ m_currentFolder->GetFlags(&folderFlags);
+ // need to check if folder has offline events, or is configured for offline
+ if (imapFolder && folderFlags & nsMsgFolderFlags::Offline &&
+ !(folderFlags & nsMsgFolderFlags::Virtual)) {
+ rv = m_currentFolder->DownloadAllForOffline(this, m_window);
+ if (NS_SUCCEEDED(rv) || rv == NS_BINDING_ABORTED) return rv;
+ // if this fails and the user didn't cancel/stop, fall through to code
+ // that advances to next folder
+ }
+ }
+ if (m_listener) m_listener->OnStopRunningUrl(nullptr, NS_OK);
+ return rv;
+}
+
+NS_IMETHODIMP nsImapOfflineSync::OnStartCopy() {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void OnProgress (in uint32_t aProgress, in uint32_t aProgressMax); */
+NS_IMETHODIMP nsImapOfflineSync::OnProgress(uint32_t aProgress,
+ uint32_t aProgressMax) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void SetMessageKey (in uint32_t aKey); */
+NS_IMETHODIMP nsImapOfflineSync::SetMessageKey(uint32_t aKey) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* [noscript] void GetMessageId (in nsCString aMessageId); */
+NS_IMETHODIMP nsImapOfflineSync::GetMessageId(nsACString& messageId) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* void OnStopCopy (in nsresult aStatus); */
+NS_IMETHODIMP nsImapOfflineSync::OnStopCopy(nsresult aStatus) {
+ return OnStopRunningUrl(nullptr, aStatus);
+}
+
+void nsImapOfflineSync::ClearDB() {
+ m_currentOpsToClear.Clear();
+ if (m_currentDB) m_currentDB->RemoveListener(this);
+ m_currentDB = nullptr;
+}
+
+NS_IMETHODIMP
+nsImapOfflineSync::OnHdrPropertyChanged(nsIMsgDBHdr* aHdrToChange,
+ const nsACString& property,
+ bool aPreChange, uint32_t* aStatus,
+ nsIDBChangeListener* aInstigator) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapOfflineSync::OnHdrFlagsChanged(nsIMsgDBHdr* aHdrChanged,
+ uint32_t aOldFlags, uint32_t aNewFlags,
+ nsIDBChangeListener* aInstigator) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapOfflineSync::OnHdrDeleted(nsIMsgDBHdr* aHdrChanged, nsMsgKey aParentKey,
+ int32_t aFlags,
+ nsIDBChangeListener* aInstigator) {
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsImapOfflineSync::OnHdrAdded(nsIMsgDBHdr* aHdrAdded, nsMsgKey aParentKey,
+ int32_t aFlags,
+ nsIDBChangeListener* aInstigator) {
+ return NS_OK;
+}
+
+/* void OnParentChanged (in nsMsgKey aKeyChanged, in nsMsgKey oldParent, in
+ * nsMsgKey newParent, in nsIDBChangeListener aInstigator); */
+NS_IMETHODIMP
+nsImapOfflineSync::OnParentChanged(nsMsgKey aKeyChanged, nsMsgKey oldParent,
+ nsMsgKey newParent,
+ nsIDBChangeListener* aInstigator) {
+ return NS_OK;
+}
+
+/* void OnAnnouncerGoingAway (in nsIDBChangeAnnouncer instigator); */
+NS_IMETHODIMP
+nsImapOfflineSync::OnAnnouncerGoingAway(nsIDBChangeAnnouncer* instigator) {
+ ClearDB();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsImapOfflineSync::OnEvent(nsIMsgDatabase* aDB,
+ const char* aEvent) {
+ return NS_OK;
+}
+
+/* void OnReadChanged (in nsIDBChangeListener instigator); */
+NS_IMETHODIMP
+nsImapOfflineSync::OnReadChanged(nsIDBChangeListener* instigator) {
+ return NS_OK;
+}
+
+/* void OnJunkScoreChanged (in nsIDBChangeListener instigator); */
+NS_IMETHODIMP
+nsImapOfflineSync::OnJunkScoreChanged(nsIDBChangeListener* instigator) {
+ return NS_OK;
+}