diff options
Diffstat (limited to 'mailnews/imap/src/nsImapUndoTxn.cpp')
-rw-r--r-- | mailnews/imap/src/nsImapUndoTxn.cpp | 751 |
1 files changed, 751 insertions, 0 deletions
diff --git a/mailnews/imap/src/nsImapUndoTxn.cpp b/mailnews/imap/src/nsImapUndoTxn.cpp new file mode 100644 index 000000000..8d51dab35 --- /dev/null +++ b/mailnews/imap/src/nsImapUndoTxn.cpp @@ -0,0 +1,751 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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" // for precompiled headers +#include "nsMsgImapCID.h" +#include "nsIMsgHdr.h" +#include "nsImapUndoTxn.h" +#include "nsIIMAPHostSessionList.h" +#include "nsIMsgIncomingServer.h" +#include "nsImapMailFolder.h" +#include "nsIDBFolderInfo.h" +#include "nsIMsgDatabase.h" +#include "nsMsgUtils.h" +#include "nsThreadUtils.h" +#include "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" + +nsImapMoveCopyMsgTxn::nsImapMoveCopyMsgTxn() : + m_idsAreUids(false), m_isMove(false), m_srcIsPop3(false) +{ +} + +nsresult +nsImapMoveCopyMsgTxn::Init(nsIMsgFolder* srcFolder, nsTArray<nsMsgKey>* srcKeyArray, + const char* srcMsgIdString, nsIMsgFolder* dstFolder, + bool idsAreUids, bool isMove) +{ + m_srcMsgIdString = srcMsgIdString; + m_idsAreUids = idsAreUids; + m_isMove = isMove; + m_srcFolder = do_GetWeakReference(srcFolder); + m_dstFolder = do_GetWeakReference(dstFolder); + m_srcKeyArray = *srcKeyArray; + m_dupKeyArray = *srcKeyArray; + nsCString uri; + nsresult rv = srcFolder->GetURI(uri); + nsCString protocolType(uri); + protocolType.SetLength(protocolType.FindChar(':')); + nsCOMPtr<nsIMsgDatabase> srcDB; + rv = srcFolder->GetMsgDatabase(getter_AddRefs(srcDB)); + NS_ENSURE_SUCCESS(rv, rv); + uint32_t i, count = m_srcKeyArray.Length(); + nsCOMPtr<nsIMsgDBHdr> srcHdr; + nsCOMPtr<nsIMsgDBHdr> copySrcHdr; + nsCString messageId; + + for (i = 0; i < count; i++) + { + rv = srcDB->GetMsgHdrForKey(m_srcKeyArray[i], + getter_AddRefs(srcHdr)); + if (NS_SUCCEEDED(rv)) + { + // ** jt -- only do this for mailbox protocol + if (MsgLowerCaseEqualsLiteral(protocolType, "mailbox")) + { + m_srcIsPop3 = true; + uint32_t msgSize; + rv = srcHdr->GetMessageSize(&msgSize); + if (NS_SUCCEEDED(rv)) + m_srcSizeArray.AppendElement(msgSize); + if (isMove) + { + rv = srcDB->CopyHdrFromExistingHdr(nsMsgKey_None, srcHdr, false, + getter_AddRefs(copySrcHdr)); + nsMsgKey pseudoKey = nsMsgKey_None; + if (NS_SUCCEEDED(rv)) + { + copySrcHdr->GetMessageKey(&pseudoKey); + m_srcHdrs.AppendObject(copySrcHdr); + } + m_dupKeyArray[i] = pseudoKey; + } + } + srcHdr->GetMessageId(getter_Copies(messageId)); + m_srcMessageIds.AppendElement(messageId); + } + } + return nsMsgTxn::Init(); +} + +nsImapMoveCopyMsgTxn::~nsImapMoveCopyMsgTxn() +{ +} + +NS_IMPL_ISUPPORTS_INHERITED(nsImapMoveCopyMsgTxn, nsMsgTxn, nsIUrlListener) + +NS_IMETHODIMP +nsImapMoveCopyMsgTxn::UndoTransaction(void) +{ + nsresult rv; + nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + bool finishInOnStopRunningUrl = false; + + if (m_isMove || !m_dstFolder) + { + if (m_srcIsPop3) + { + rv = UndoMailboxDelete(); + NS_ENSURE_SUCCESS(rv, rv); + } + else + { + nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv); + if (NS_FAILED(rv) || !srcFolder) + return rv; + nsCOMPtr<nsIUrlListener> srcListener = do_QueryInterface(srcFolder, &rv); + if (NS_FAILED(rv)) + return rv; + m_onStopListener = do_GetWeakReference(srcListener); + + // ** make sure we are in the selected state; use lite select + // folder so we won't hit performance hard + rv = imapService->LiteSelectFolder(srcFolder, srcListener, nullptr, nullptr); + if (NS_FAILED(rv)) + return rv; + bool deletedMsgs = true; //default is true unless imapDelete model + nsMsgImapDeleteModel deleteModel; + rv = GetImapDeleteModel(srcFolder, &deleteModel); + + // protect against a bogus undo txn without any source keys + // see bug #179856 for details + NS_ASSERTION(!m_srcKeyArray.IsEmpty(), "no source keys"); + if (m_srcKeyArray.IsEmpty()) + return NS_ERROR_UNEXPECTED; + + if (!m_srcMsgIdString.IsEmpty()) + { + if (NS_SUCCEEDED(rv) && deleteModel == nsMsgImapDeleteModels::IMAPDelete) + CheckForToggleDelete(srcFolder, m_srcKeyArray[0], &deletedMsgs); + + if (deletedMsgs) + rv = imapService->SubtractMessageFlags(srcFolder, + this, nullptr, + m_srcMsgIdString, + kImapMsgDeletedFlag, + m_idsAreUids); + else + rv = imapService->AddMessageFlags(srcFolder, + srcListener, nullptr, + m_srcMsgIdString, + kImapMsgDeletedFlag, + m_idsAreUids); + if (NS_FAILED(rv)) + return rv; + + finishInOnStopRunningUrl = true; + if (deleteModel != nsMsgImapDeleteModels::IMAPDelete) + rv = imapService->GetHeaders(srcFolder, srcListener, nullptr, + m_srcMsgIdString, true); + } + } + } + if (!finishInOnStopRunningUrl && !m_dstMsgIdString.IsEmpty()) + { + nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv); + if (NS_FAILED(rv) || !dstFolder) + return rv; + + nsCOMPtr<nsIUrlListener> dstListener; + + dstListener = do_QueryInterface(dstFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + // ** make sure we are in the selected state; use lite select folder + // so we won't potentially download a bunch of headers. + rv = imapService->LiteSelectFolder(dstFolder, + dstListener, nullptr, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + rv = imapService->AddMessageFlags(dstFolder, dstListener, + nullptr, m_dstMsgIdString, + kImapMsgDeletedFlag, m_idsAreUids); + } + return rv; +} + +NS_IMETHODIMP +nsImapMoveCopyMsgTxn::RedoTransaction(void) +{ + nsresult rv; + nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + if (m_isMove || !m_dstFolder) + { + if (m_srcIsPop3) + { + rv = RedoMailboxDelete(); + if (NS_FAILED(rv)) return rv; + } + else if (!m_srcMsgIdString.IsEmpty()) + { + nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv); + if (NS_FAILED(rv) || !srcFolder) + return rv; + nsCOMPtr<nsIUrlListener> srcListener = do_QueryInterface(srcFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + bool deletedMsgs = false; //default will be false unless imapDeleteModel; + nsMsgImapDeleteModel deleteModel; + rv = GetImapDeleteModel(srcFolder, &deleteModel); + + // protect against a bogus undo txn without any source keys + // see bug #179856 for details + NS_ASSERTION(!m_srcKeyArray.IsEmpty(), "no source keys"); + if (m_srcKeyArray.IsEmpty()) + return NS_ERROR_UNEXPECTED; + + if (NS_SUCCEEDED(rv) && deleteModel == nsMsgImapDeleteModels::IMAPDelete) + rv = CheckForToggleDelete(srcFolder, m_srcKeyArray[0], &deletedMsgs); + + // Make sure we are in the selected state; use lite select + // folder so performance won't suffer. + rv = imapService->LiteSelectFolder(srcFolder, srcListener, nullptr, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + if (deletedMsgs) + { + rv = imapService->SubtractMessageFlags(srcFolder, + srcListener, nullptr, + m_srcMsgIdString, + kImapMsgDeletedFlag, + m_idsAreUids); + } + else + { + rv = imapService->AddMessageFlags(srcFolder, + srcListener, nullptr, m_srcMsgIdString, + kImapMsgDeletedFlag, m_idsAreUids); + } + } + } + if (!m_dstMsgIdString.IsEmpty()) + { + nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv); + if (NS_FAILED(rv) || !dstFolder) return rv; + + nsCOMPtr<nsIUrlListener> dstListener; + + dstListener = do_QueryInterface(dstFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + // ** make sure we are in the selected state; use lite select + // folder so we won't hit performance hard + rv = imapService->LiteSelectFolder(dstFolder, dstListener, nullptr, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + rv = imapService->SubtractMessageFlags(dstFolder, + dstListener, nullptr, + m_dstMsgIdString, + kImapMsgDeletedFlag, + m_idsAreUids); + NS_ENSURE_SUCCESS(rv, rv); + nsMsgImapDeleteModel deleteModel; + rv = GetImapDeleteModel(dstFolder, &deleteModel); + if (NS_FAILED(rv) || deleteModel == nsMsgImapDeleteModels::MoveToTrash) + { + rv = imapService->GetHeaders(dstFolder, dstListener, + nullptr, m_dstMsgIdString, true); + } + } + return rv; +} + +nsresult +nsImapMoveCopyMsgTxn::SetCopyResponseUid(const char* aMsgIdString) +{ + if (!aMsgIdString) return NS_ERROR_NULL_POINTER; + m_dstMsgIdString = aMsgIdString; + if (m_dstMsgIdString.Last() == ']') + { + int32_t len = m_dstMsgIdString.Length(); + m_dstMsgIdString.SetLength(len - 1); + } + return NS_OK; +} + +nsresult +nsImapMoveCopyMsgTxn::GetSrcKeyArray(nsTArray<nsMsgKey>& srcKeyArray) +{ + srcKeyArray = m_srcKeyArray; + return NS_OK; +} + +nsresult +nsImapMoveCopyMsgTxn::AddDstKey(nsMsgKey aKey) +{ + if (!m_dstMsgIdString.IsEmpty()) + m_dstMsgIdString.Append(","); + m_dstMsgIdString.AppendInt((int32_t) aKey); + return NS_OK; +} + +nsresult +nsImapMoveCopyMsgTxn::UndoMailboxDelete() +{ + nsresult rv = NS_ERROR_FAILURE; + // ** jt -- only do this for mailbox protocol + if (m_srcIsPop3) + { + nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv); + if (NS_FAILED(rv) || !srcFolder) return rv; + + nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv); + if (NS_FAILED(rv) || !dstFolder) return rv; + + nsCOMPtr<nsIMsgDatabase> srcDB; + nsCOMPtr<nsIMsgDatabase> dstDB; + rv = srcFolder->GetMsgDatabase(getter_AddRefs(srcDB)); + if (NS_FAILED(rv)) return rv; + rv = dstFolder->GetMsgDatabase(getter_AddRefs(dstDB)); + if (NS_FAILED(rv)) return rv; + + uint32_t count = m_srcKeyArray.Length(); + uint32_t i; + nsCOMPtr<nsIMsgDBHdr> oldHdr; + nsCOMPtr<nsIMsgDBHdr> newHdr; + for (i = 0; i < count; i++) + { + oldHdr = m_srcHdrs[i]; + NS_ASSERTION(oldHdr, "fatal ... cannot get old msg header\n"); + rv = srcDB->CopyHdrFromExistingHdr(m_srcKeyArray[i], + oldHdr,true, + getter_AddRefs(newHdr)); + NS_ASSERTION(newHdr, "fatal ... cannot create new header\n"); + + if (NS_SUCCEEDED(rv) && newHdr) + { + if (i < m_srcSizeArray.Length()) + newHdr->SetMessageSize(m_srcSizeArray[i]); + srcDB->UndoDelete(newHdr); + } + } + srcDB->SetSummaryValid(true); + return NS_OK; // always return NS_OK + } + else + { + rv = NS_ERROR_FAILURE; + } + return rv; +} + + +nsresult +nsImapMoveCopyMsgTxn::RedoMailboxDelete() +{ + nsresult rv = NS_ERROR_FAILURE; + if (m_srcIsPop3) + { + nsCOMPtr<nsIMsgDatabase> srcDB; + nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv); + if (NS_FAILED(rv) || !srcFolder) return rv; + rv = srcFolder->GetMsgDatabase(getter_AddRefs(srcDB)); + if (NS_SUCCEEDED(rv)) + { + srcDB->DeleteMessages(m_srcKeyArray.Length(), m_srcKeyArray.Elements(), nullptr); + srcDB->SetSummaryValid(true); + } + return NS_OK; // always return NS_OK + } + else + { + rv = NS_ERROR_FAILURE; + } + return rv; +} + +nsresult nsImapMoveCopyMsgTxn::GetImapDeleteModel(nsIMsgFolder *aFolder, nsMsgImapDeleteModel *aDeleteModel) +{ + nsresult rv; + nsCOMPtr<nsIMsgIncomingServer> server; + if (!aFolder) + return NS_ERROR_NULL_POINTER; + rv = aFolder->GetServer(getter_AddRefs(server)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIImapIncomingServer> imapServer = do_QueryInterface(server, &rv); + if (NS_SUCCEEDED(rv) && imapServer) + rv = imapServer->GetDeleteModel(aDeleteModel); + return rv; +} + +NS_IMETHODIMP nsImapMoveCopyMsgTxn::OnStartRunningUrl(nsIURI *aUrl) +{ + return NS_OK; +} + +NS_IMETHODIMP nsImapMoveCopyMsgTxn::OnStopRunningUrl(nsIURI *aUrl, nsresult aExitCode) +{ + nsCOMPtr<nsIUrlListener> urlListener = do_QueryReferent(m_onStopListener); + if (urlListener) + urlListener->OnStopRunningUrl(aUrl, aExitCode); + + nsCOMPtr<nsIImapUrl> imapUrl = do_QueryInterface(aUrl); + if (imapUrl) + { + nsresult rv; + nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsImapAction imapAction; + imapUrl->GetImapAction(&imapAction); + nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + if (imapAction == nsIImapUrl::nsImapSubtractMsgFlags) + { + int32_t extraStatus; + imapUrl->GetExtraStatus(&extraStatus); + if (extraStatus != nsIImapUrl::ImapStatusNone) + { + // If subtracting the deleted flag didn't work, try + // moving the message back from the target folder to the src folder + if (!m_dstMsgIdString.IsEmpty()) + imapService->OnlineMessageCopy(dstFolder, + m_dstMsgIdString, + srcFolder, + true, + true, + nullptr, /* listener */ + nullptr, + nullptr, + nullptr); + else + { + // server doesn't support COPYUID, so we're going to update the dest + // folder, and when that's done, use the db to find the messages + // to move back, looking them up by message-id. + nsCOMPtr<nsIMsgImapMailFolder> imapDest = do_QueryInterface(dstFolder); + if (imapDest) + imapDest->UpdateFolderWithListener(nullptr, this); + } + } + else if (!m_dstMsgIdString.IsEmpty()) + { + nsCOMPtr<nsIUrlListener> dstListener; + + dstListener = do_QueryInterface(dstFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + // ** make sure we are in the selected state; use lite select folder + // so we won't potentially download a bunch of headers. + rv = imapService->LiteSelectFolder(dstFolder, dstListener, nullptr, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + rv = imapService->AddMessageFlags(dstFolder, dstListener, + nullptr, m_dstMsgIdString, + kImapMsgDeletedFlag, m_idsAreUids); + } + } + else if (imapAction == nsIImapUrl::nsImapSelectFolder) + { + // Now we should have the headers from the dest folder. + // Look them up and move them back to the source folder. + uint32_t count = m_srcMessageIds.Length(); + uint32_t i; + nsCString messageId; + nsTArray<nsMsgKey> dstKeys; + nsCOMPtr<nsIMsgDatabase> destDB; + nsCOMPtr<nsIMsgDBHdr> dstHdr; + + rv = dstFolder->GetMsgDatabase(getter_AddRefs(destDB)); + NS_ENSURE_SUCCESS(rv, rv); + for (i = 0; i < count; i++) + { + rv = destDB->GetMsgHdrForMessageID(m_srcMessageIds[i].get(), getter_AddRefs(dstHdr)); + if (NS_SUCCEEDED(rv) && dstHdr) + { + nsMsgKey dstKey; + dstHdr->GetMessageKey(&dstKey); + dstKeys.AppendElement(dstKey); + } + } + if (dstKeys.Length()) + { + nsAutoCString uids; + nsImapMailFolder::AllocateUidStringFromKeys(dstKeys.Elements(), dstKeys.Length(), uids); + rv = imapService->OnlineMessageCopy(dstFolder, uids, srcFolder, + true, true, nullptr, + nullptr, nullptr, nullptr); + } + } + } + return NS_OK; +} + +nsImapOfflineTxn::nsImapOfflineTxn(nsIMsgFolder* srcFolder, nsTArray<nsMsgKey>* srcKeyArray, + const char *srcMsgIdString, nsIMsgFolder* dstFolder, + bool isMove, nsOfflineImapOperationType opType, + nsCOMArray<nsIMsgDBHdr> &srcHdrs) +{ + Init(srcFolder, srcKeyArray, srcMsgIdString, dstFolder, true, + isMove); + + m_opType = opType; + m_flags = 0; + m_addFlags = false; + if (opType == nsIMsgOfflineImapOperation::kDeletedMsg) + { + nsCOMPtr<nsIMsgDatabase> srcDB; + nsCOMPtr<nsIDBFolderInfo> folderInfo; + + nsresult rv = srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(srcDB)); + if (NS_SUCCEEDED(rv) && srcDB) + { + nsMsgKey pseudoKey; + nsCOMPtr <nsIMsgDBHdr> copySrcHdr; + + // Imap protocols have conflated key/UUID so we cannot use + // auto key with them. + nsCString protocolType; + srcFolder->GetURI(protocolType); + protocolType.SetLength(protocolType.FindChar(':')); + for (int32_t i = 0; i < srcHdrs.Count(); i++) + { + if (protocolType.EqualsLiteral("imap")) + { + srcDB->GetNextPseudoMsgKey(&pseudoKey); + pseudoKey--; + } + else + { + pseudoKey = nsMsgKey_None; + } + rv = srcDB->CopyHdrFromExistingHdr(pseudoKey, srcHdrs[i], false, getter_AddRefs(copySrcHdr)); + if (NS_SUCCEEDED(rv)) + { + copySrcHdr->GetMessageKey(&pseudoKey); + m_srcHdrs.AppendObject(copySrcHdr); + } + m_dupKeyArray[i] = pseudoKey; + } + } + } + else + m_srcHdrs.AppendObjects(srcHdrs); +} + +nsImapOfflineTxn::~nsImapOfflineTxn() +{ +} + +// Open the database and find the key for the offline operation that we want to +// undo, then remove it from the database, we also hold on to this +// data for a redo operation. +NS_IMETHODIMP nsImapOfflineTxn::UndoTransaction(void) +{ + nsresult rv; + + nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv); + if (NS_FAILED(rv) || !srcFolder) + return rv; + nsCOMPtr <nsIMsgOfflineImapOperation> op; + nsCOMPtr <nsIDBFolderInfo> folderInfo; + nsCOMPtr <nsIMsgDatabase> srcDB; + nsCOMPtr <nsIMsgDatabase> destDB; + + rv = srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(srcDB)); + NS_ENSURE_SUCCESS(rv, rv); + + switch (m_opType) + { + case nsIMsgOfflineImapOperation::kMsgMoved: + case nsIMsgOfflineImapOperation::kMsgCopy: + case nsIMsgOfflineImapOperation::kAddedHeader: + case nsIMsgOfflineImapOperation::kFlagsChanged: + case nsIMsgOfflineImapOperation::kDeletedMsg: + { + if (m_srcHdrs.IsEmpty()) + { + NS_ASSERTION(false, "No msg header to apply undo."); + break; + } + nsCOMPtr<nsIMsgDBHdr> firstHdr = m_srcHdrs[0]; + nsMsgKey hdrKey; + firstHdr->GetMessageKey(&hdrKey); + rv = srcDB->GetOfflineOpForKey(hdrKey, false, getter_AddRefs(op)); + bool offlineOpPlayedBack = true; + if (NS_SUCCEEDED(rv) && op) + { + op->GetPlayingBack(&offlineOpPlayedBack); + srcDB->RemoveOfflineOp(op); + op = nullptr; + } + if (!WeAreOffline() && offlineOpPlayedBack) + { + // couldn't find offline op - it must have been played back already + // so we should undo the transaction online. + return nsImapMoveCopyMsgTxn::UndoTransaction(); + } + + if (!firstHdr) + break; + nsMsgKey msgKey; + if (m_opType == nsIMsgOfflineImapOperation::kAddedHeader) + { + for (int32_t i = 0; i < m_srcHdrs.Count(); i++) + { + m_srcHdrs[i]->GetMessageKey(&msgKey); + nsCOMPtr<nsIMsgDBHdr> mailHdr; + rv = srcDB->GetMsgHdrForKey(msgKey, getter_AddRefs(mailHdr)); + if (mailHdr) + srcDB->DeleteHeader(mailHdr, nullptr, false, false); + } + srcDB->Commit(true); + } + else if (m_opType == nsIMsgOfflineImapOperation::kDeletedMsg) + { + for (int32_t i = 0; i < m_srcHdrs.Count(); i++) + { + nsCOMPtr<nsIMsgDBHdr> undeletedHdr = m_srcHdrs[i]; + m_srcHdrs[i]->GetMessageKey(&msgKey); + if (undeletedHdr) + { + nsCOMPtr<nsIMsgDBHdr> newHdr; + srcDB->CopyHdrFromExistingHdr (msgKey, undeletedHdr, true, getter_AddRefs(newHdr)); + } + } + srcDB->Close(true); + srcFolder->SummaryChanged(); + } + break; + } + case nsIMsgOfflineImapOperation::kMsgMarkedDeleted: + { + nsMsgKey msgKey; + for (int32_t i = 0; i < m_srcHdrs.Count(); i++) + { + m_srcHdrs[i]->GetMessageKey(&msgKey); + srcDB->MarkImapDeleted(msgKey, false, nullptr); + } + } + break; + default: + break; + } + srcDB->Close(true); + srcFolder->SummaryChanged(); + return NS_OK; +} + +NS_IMETHODIMP nsImapOfflineTxn::RedoTransaction(void) +{ + nsresult rv; + + nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv); + if (NS_FAILED(rv) || !srcFolder) + return rv; + nsCOMPtr <nsIMsgOfflineImapOperation> op; + nsCOMPtr <nsIDBFolderInfo> folderInfo; + nsCOMPtr <nsIMsgDatabase> srcDB; + nsCOMPtr <nsIMsgDatabase> destDB; + rv = srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(srcDB)); + NS_ENSURE_SUCCESS(rv, rv); + + switch (m_opType) + { + case nsIMsgOfflineImapOperation::kMsgMoved: + case nsIMsgOfflineImapOperation::kMsgCopy: + for (int32_t i = 0; i < m_srcHdrs.Count(); i++) + { + nsMsgKey hdrKey; + m_srcHdrs[i]->GetMessageKey(&hdrKey); + rv = srcDB->GetOfflineOpForKey(hdrKey, false, getter_AddRefs(op)); + if (NS_SUCCEEDED(rv) && op) + { + nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv); + if (dstFolder) + { + nsCString folderURI; + dstFolder->GetURI(folderURI); + + if (m_opType == nsIMsgOfflineImapOperation::kMsgMoved) + op->SetDestinationFolderURI(folderURI.get()); // offline move + if (m_opType == nsIMsgOfflineImapOperation::kMsgCopy) + { + op->SetOperation(nsIMsgOfflineImapOperation::kMsgMoved); + op->AddMessageCopyOperation(folderURI.get()); // offline copy + } + dstFolder->SummaryChanged(); + } + } + else if (!WeAreOffline()) + { + // couldn't find offline op - it must have been played back already + // so we should redo the transaction online. + return nsImapMoveCopyMsgTxn::RedoTransaction(); + } + } + break; + case nsIMsgOfflineImapOperation::kAddedHeader: + { + nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv); + rv = srcFolder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(destDB)); + NS_ENSURE_SUCCESS(rv, rv); + for (int32_t i = 0; i < m_srcHdrs.Count(); i++) + { + nsCOMPtr<nsIMsgDBHdr> restoreHdr; + nsMsgKey msgKey; + m_srcHdrs[i]->GetMessageKey(&msgKey); + destDB->CopyHdrFromExistingHdr (msgKey, m_srcHdrs[i], true, getter_AddRefs(restoreHdr)); + rv = destDB->GetOfflineOpForKey(msgKey, true, getter_AddRefs(op)); + if (NS_SUCCEEDED(rv) && op) + { + nsCString folderURI; + srcFolder->GetURI(folderURI); + op->SetSourceFolderURI(folderURI.get()); + } + } + dstFolder->SummaryChanged(); + destDB->Close(true); + } + break; + case nsIMsgOfflineImapOperation::kDeletedMsg: + for (int32_t i = 0; i < m_srcHdrs.Count(); i++) + { + nsMsgKey msgKey; + m_srcHdrs[i]->GetMessageKey(&msgKey); + srcDB->DeleteMessage(msgKey, nullptr, true); + } + break; + case nsIMsgOfflineImapOperation::kMsgMarkedDeleted: + for (int32_t i = 0; i < m_srcHdrs.Count(); i++) + { + nsMsgKey msgKey; + m_srcHdrs[i]->GetMessageKey(&msgKey); + srcDB->MarkImapDeleted(msgKey, true, nullptr); + } + break; + case nsIMsgOfflineImapOperation::kFlagsChanged: + for (int32_t i = 0; i < m_srcHdrs.Count(); i++) + { + nsMsgKey msgKey; + m_srcHdrs[i]->GetMessageKey(&msgKey); + rv = srcDB->GetOfflineOpForKey(msgKey, true, getter_AddRefs(op)); + if (NS_SUCCEEDED(rv) && op) + { + imapMessageFlagsType newMsgFlags; + op->GetNewFlags(&newMsgFlags); + if (m_addFlags) + op->SetFlagOperation(newMsgFlags | m_flags); + else + op->SetFlagOperation(newMsgFlags & ~m_flags); + } + } + break; + default: + break; + } + srcDB->Close(true); + srcDB = nullptr; + srcFolder->SummaryChanged(); + return NS_OK; +} |