summaryrefslogtreecommitdiffstats
path: root/mailnews/imap/src/nsImapOfflineSync.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/imap/src/nsImapOfflineSync.cpp')
-rw-r--r--mailnews/imap/src/nsImapOfflineSync.cpp1292
1 files changed, 1292 insertions, 0 deletions
diff --git a/mailnews/imap/src/nsImapOfflineSync.cpp b/mailnews/imap/src/nsImapOfflineSync.cpp
new file mode 100644
index 000000000..df31bd299
--- /dev/null
+++ b/mailnews/imap/src/nsImapOfflineSync.cpp
@@ -0,0 +1,1292 @@
+/* -*- Mode: C++; tab-width: 4; 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 "nsIRDFService.h"
+#include "nsMsgBaseCID.h"
+#include "nsRDFCID.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsIMsgAccountManager.h"
+#include "nsINntpIncomingServer.h"
+#include "nsIRequestObserver.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsISeekableStream.h"
+#include "nsIMsgCopyService.h"
+#include "nsImapProtocol.h"
+#include "nsMsgUtils.h"
+#include "nsIMutableArray.h"
+#include "nsIAutoSyncManager.h"
+#include "nsAlgorithm.h"
+#include "nsArrayUtils.h"
+#include <algorithm>
+
+static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+
+NS_IMPL_ISUPPORTS(nsImapOfflineSync, nsIUrlListener, nsIMsgCopyServiceListener, nsIDBChangeListener)
+
+nsImapOfflineSync::nsImapOfflineSync(nsIMsgWindow *window, nsIUrlListener *listener, nsIMsgFolder *singleFolderOnly, bool isPseudoOffline)
+{
+ m_singleFolderToUpdate = singleFolderOnly;
+ m_window = window;
+ // not the perfect place for this, but I think it will work.
+ if (m_window)
+ m_window->SetStopped(false);
+
+ mCurrentPlaybackOpType = nsIMsgOfflineImapOperation::kFlagsChanged;
+ m_mailboxupdatesStarted = false;
+ m_mailboxupdatesFinished = false;
+ m_createdOfflineFolders = false;
+ m_pseudoOffline = isPseudoOffline;
+ m_KeyIndex = 0;
+ mCurrentUIDValidity = nsMsgKey_None;
+ m_listener = listener;
+}
+
+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. If no more servers,
+ * m_currentServer will be left at nullptr and the function returns false.
+ * Also, sets up m_serverEnumerator to enumerate over the server.
+ */
+bool nsImapOfflineSync::AdvanceToNextServer()
+{
+ nsresult rv = NS_OK;
+
+ if (!m_allServers)
+ {
+ NS_ASSERTION(!m_currentServer, "this shouldn't be set");
+ m_currentServer = nullptr;
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ NS_ASSERTION(accountManager && NS_SUCCEEDED(rv), "couldn't get account mgr");
+ if (!accountManager || NS_FAILED(rv))
+ return false;
+
+ rv = accountManager->GetAllServers(getter_AddRefs(m_allServers));
+ NS_ENSURE_SUCCESS(rv, false);
+ }
+ uint32_t serverIndex = 0;
+ if (m_currentServer)
+ {
+ rv = m_allServers->IndexOf(0, m_currentServer, &serverIndex);
+ if (NS_FAILED(rv))
+ serverIndex = -1;
+
+ // Move to the next server
+ ++serverIndex;
+ }
+ m_currentServer = nullptr;
+ uint32_t numServers;
+ m_allServers->GetLength(&numServers);
+ nsCOMPtr <nsIMsgFolder> rootFolder;
+
+ while (serverIndex < numServers)
+ {
+ nsCOMPtr<nsIMsgIncomingServer> server(do_QueryElementAt(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(getter_AddRefs(m_allFolders));
+ if (NS_SUCCEEDED(rv))
+ {
+ rv = m_allFolders->Enumerate(getter_AddRefs(m_serverEnumerator));
+ if (NS_SUCCEEDED(rv) && m_serverEnumerator)
+ {
+ bool hasMore = false;
+ rv = m_serverEnumerator->HasMoreElements(&hasMore);
+ if (NS_SUCCEEDED(rv) && hasMore)
+ 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)
+ m_serverEnumerator->HasMoreElements(&hasMore);
+ if (!hasMore)
+ hasMore = AdvanceToNextServer();
+
+ if (hasMore)
+ {
+ nsCOMPtr<nsISupports> supports;
+ nsresult rv = m_serverEnumerator->GetNext(getter_AddRefs(supports));
+ if (NS_SUCCEEDED(rv))
+ m_currentFolder = do_QueryInterface(supports);
+ }
+ 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);
+ imapMessageFlagsType flagOperation;
+ imapMessageFlagsType newFlags;
+ bool flagsMatch = true;
+ do
+ { // loop for all messsages with the same flags
+ if (flagsMatch)
+ {
+ nsMsgKey curKey;
+ currentOp->GetMessageKey(&curKey);
+ matchingFlagKeys.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)
+ {
+ currentOp->GetFlagOperation(&flagOperation);
+ currentOp->GetNewFlags(&newFlags);
+ }
+ flagsMatch = (flagOperation & nsIMsgOfflineImapOperation::kFlagsChanged)
+ && (newFlags == matchingFlags);
+ } while (currentOp);
+
+ if (!matchingFlagKeys.IsEmpty())
+ {
+ nsAutoCString uids;
+ nsImapMailFolder::AllocateUidStringFromKeys(matchingFlagKeys.Elements(), matchingFlagKeys.Length(), 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 messsages 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.Elements(),
+ matchingKeywordKeys.Length(), getter_AddRefs(uriToStoreCustomKeywords));
+ if (NS_SUCCEEDED(rv) && uriToStoreCustomKeywords)
+ {
+ nsCOMPtr <nsIMsgMailNewsUrl> mailnewsUrl = do_QueryInterface(uriToStoreCustomKeywords);
+ if (mailnewsUrl)
+ mailnewsUrl->RegisterListener(this);
+ }
+ }
+ }
+ }
+ else
+ ProcessNextOperation();
+}
+
+void
+nsImapOfflineSync::ProcessAppendMsgOperation(nsIMsgOfflineImapOperation *currentOp, int32_t opType)
+{
+ nsCOMPtr <nsIMsgDBHdr> mailHdr;
+ nsMsgKey msgKey;
+ currentOp->GetMessageKey(&msgKey);
+ nsresult rv = m_currentDB->GetMsgHdrForKey(msgKey, getter_AddRefs(mailHdr));
+ if (NS_SUCCEEDED(rv) && mailHdr)
+ {
+ uint64_t messageOffset;
+ uint32_t messageSize;
+ mailHdr->GetMessageOffset(&messageOffset);
+ mailHdr->GetOfflineMessageSize(&messageSize);
+ nsCOMPtr<nsIFile> tmpFile;
+
+ if (NS_FAILED(GetSpecialDirectoryWithFileName(NS_OS_TEMP_DIR,
+ "nscpmsg.txt",
+ getter_AddRefs(tmpFile))))
+ return;
+
+ 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_SUCCEEDED(rv) && outputStream)
+ {
+ nsCString moveDestination;
+ currentOp->GetDestinationFolderURI(getter_Copies(moveDestination));
+ nsCOMPtr<nsIRDFService> rdf(do_GetService(kRDFServiceCID, &rv));
+ nsCOMPtr<nsIRDFResource> res;
+ if (NS_FAILED(rv)) return ; // ### return error code.
+ rv = rdf->GetResource(moveDestination, getter_AddRefs(res));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgFolder> destFolder(do_QueryInterface(res, &rv));
+ if (NS_SUCCEEDED(rv) && destFolder)
+ {
+ nsCOMPtr <nsIInputStream> offlineStoreInputStream;
+ bool reusable;
+ rv = destFolder->GetMsgInputStream(
+ mailHdr, &reusable, getter_AddRefs(offlineStoreInputStream));
+ if (NS_SUCCEEDED(rv) && offlineStoreInputStream)
+ {
+ nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(offlineStoreInputStream);
+ NS_ASSERTION(seekStream, "non seekable stream - can't read from offline msg");
+ if (seekStream)
+ {
+ rv = seekStream->Seek(PR_SEEK_SET, messageOffset);
+ if (NS_SUCCEEDED(rv))
+ {
+ // now, 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_SUCCEEDED(rv) && bytesRead > 0)
+ {
+ rv = outputStream->Write(inputBuffer, bytesRead, &bytesWritten);
+ NS_ASSERTION(bytesWritten == bytesRead, "wrote out incorrect number of bytes");
+ }
+ else
+ break;
+ bytesLeft -= bytesRead;
+ }
+ PR_FREEIF(inputBuffer);
+
+ outputStream->Flush();
+ outputStream->Close();
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIFile> cloneTmpFile;
+ // clone the tmp file to defeat nsIFile's stat/size caching.
+ tmpFile->Clone(getter_AddRefs(cloneTmpFile));
+ m_curTempFile = do_QueryInterface(cloneTmpFile);
+ nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID);
+ if (copyService)
+ rv = copyService->CopyFileMessage(cloneTmpFile, destFolder,
+ /* nsIMsgDBHdr* msgToReplace */ nullptr,
+ true /* isDraftOrTemplate */,
+ 0, // new msg flags - are there interesting flags here?
+ EmptyCString(), /* are there keywords we should get? */
+ this,
+ m_window);
+ }
+ else
+ tmpFile->Remove(false);
+ }
+ currentOp->SetPlayingBack(true);
+ m_currentOpsToClear.AppendObject(currentOp);
+ m_currentDB->DeleteHeader(mailHdr, nullptr, true, true);
+ }
+ }
+ // want to close in failure case too
+ outputStream->Close();
+ }
+ }
+ }
+ }
+ else
+ {
+ m_currentDB->RemoveOfflineOp(currentOp);
+ ProcessNextOperation();
+ }
+}
+
+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(getter_Copies(moveDestination));
+ bool moveMatches = true;
+ nsCOMPtr <nsIMsgOfflineImapOperation> currentOp = op;
+ do
+ { // loop for all messsages 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(getter_Copies(nextDestination));
+ moveMatches = moveDestination.Equals(nextDestination);
+ }
+ }
+ }
+ }
+ while (currentOp);
+
+ nsCOMPtr<nsIMsgFolder> destFolder;
+ GetExistingFolder(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_ERROR("trying to playing back move to non-existent folder");
+ ClearCurrentOps();
+ ProcessNextOperation();
+ return;
+ }
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_currentFolder);
+ if (imapFolder && DestFolderOnSameServer(destFolder))
+ {
+ imapFolder->ReplayOfflineMoveCopy(matchingFlagKeys.Elements(), matchingFlagKeys.Length(), true, destFolder,
+ this, m_window);
+ }
+ else
+ {
+ nsresult rv;
+ nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ 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, false);
+ }
+ }
+ nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &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 messsages 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;
+ GetExistingFolder(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))
+ {
+ rv = imapFolder->ReplayOfflineMoveCopy(matchingFlagKeys.Elements(), matchingFlagKeys.Length(), false, destFolder,
+ this, m_window);
+ }
+ else
+ {
+ nsCOMPtr<nsIMutableArray> messages(do_CreateInstance(NS_ARRAY_CONTRACTID, &rv));
+ if (messages && NS_SUCCEEDED(rv))
+ {
+ 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, false);
+ }
+ nsCOMPtr<nsIMsgCopyService> copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv);
+ if (copyService)
+ copyService->CopyMessages(m_currentFolder, messages, destFolder, false, this, m_window, false);
+ }
+ }
+}
+
+void nsImapOfflineSync::ProcessEmptyTrash()
+{
+ m_currentFolder->EmptyTrash(m_window, 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)
+ */
+nsresult 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 */))
+ {
+ m_currentFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(m_currentDB));
+ if (m_currentDB)
+ 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 notifcation 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_ERROR("invalid playback op type");
+ }
+ }
+ else
+ currentFolderFinished = true;
+ }
+ else
+ currentFolderFinished = true;
+
+ if (currentFolderFinished)
+ {
+ ClearDB();
+ if (!m_singleFolderToUpdate)
+ {
+ AdvanceToNextFolder();
+ ProcessNextOperation();
+ return NS_OK;
+ }
+ else
+ 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);
+ nsCOMPtr<nsIMsgImapMailFolder> imapFolder(do_QueryInterface(m_singleFolderToUpdate));
+ if (imapFolder)
+ {
+ nsCOMPtr<nsIUrlListener> saveListener = m_listener;
+// m_listener = nullptr;
+// imapFolder->UpdateFolderWithListener(m_window, saveListener);
+ }
+ }
+ }
+ // 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(aMsgWindow, aListener)
+{
+ // pause auto-sync service
+ nsresult rv;
+ nsCOMPtr<nsIAutoSyncManager> autoSyncMgr = do_GetService(NS_AUTOSYNCMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ autoSyncMgr->Pause();
+}
+
+nsImapOfflineDownloader::~nsImapOfflineDownloader()
+{
+}
+
+nsresult nsImapOfflineDownloader::ProcessNextOperation()
+{
+ nsresult rv = NS_OK;
+ if (!m_mailboxupdatesStarted)
+ {
+ m_mailboxupdatesStarted = true;
+ // Update the INBOX first so the updates on the remaining
+ // folders pickup the results of any filter moves.
+ nsCOMPtr<nsIMsgAccountManager> accountManager =
+ do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIArray> servers;
+ rv = accountManager->GetAllServers(getter_AddRefs(servers));
+ if (NS_FAILED(rv)) return rv;
+ }
+ if (!m_mailboxupdatesFinished)
+ {
+ if (AdvanceToNextServer())
+ {
+ nsCOMPtr <nsIMsgFolder> rootMsgFolder;
+ m_currentServer->GetRootFolder(getter_AddRefs(rootMsgFolder));
+ nsCOMPtr<nsIMsgFolder> inbox;
+ if (rootMsgFolder)
+ {
+ 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.
+ }
+ else
+ {
+ m_allServers = nullptr;
+ 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,
+ 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;
+}
+