diff options
Diffstat (limited to 'mailnews/local/src/nsLocalUndoTxn.cpp')
-rw-r--r-- | mailnews/local/src/nsLocalUndoTxn.cpp | 560 |
1 files changed, 560 insertions, 0 deletions
diff --git a/mailnews/local/src/nsLocalUndoTxn.cpp b/mailnews/local/src/nsLocalUndoTxn.cpp new file mode 100644 index 000000000..a53ac992e --- /dev/null +++ b/mailnews/local/src/nsLocalUndoTxn.cpp @@ -0,0 +1,560 @@ +/* -*- 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" +#include "nsIMsgHdr.h" +#include "nsLocalUndoTxn.h" +#include "nsImapCore.h" +#include "nsMsgImapCID.h" +#include "nsIImapService.h" +#include "nsIUrlListener.h" +#include "nsIMsgLocalMailFolder.h" +#include "nsIMsgMailSession.h" +#include "nsIMsgFolderNotificationService.h" +#include "nsThreadUtils.h" +#include "nsIMsgDatabase.h" +#include "nsIMutableArray.h" +#include "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" +#include "nsMsgUtils.h" + +NS_IMPL_ISUPPORTS_INHERITED(nsLocalMoveCopyMsgTxn, nsMsgTxn, nsIFolderListener) + +nsLocalMoveCopyMsgTxn::nsLocalMoveCopyMsgTxn() : m_srcIsImap4(false), + m_canUndelete(false) +{ +} + +nsLocalMoveCopyMsgTxn::~nsLocalMoveCopyMsgTxn() +{ +} + +nsresult +nsLocalMoveCopyMsgTxn::Init(nsIMsgFolder* srcFolder, nsIMsgFolder* dstFolder, + bool isMove) +{ + nsresult rv; + rv = SetSrcFolder(srcFolder); + NS_ENSURE_SUCCESS(rv, rv); + rv = SetDstFolder(dstFolder); + NS_ENSURE_SUCCESS(rv, rv); + m_isMove = isMove; + + mUndoFolderListener = nullptr; + + nsCString protocolType; + rv = srcFolder->GetURI(protocolType); + protocolType.SetLength(protocolType.FindChar(':')); + if (MsgLowerCaseEqualsLiteral(protocolType, "imap")) + m_srcIsImap4 = true; + return nsMsgTxn::Init(); +} +nsresult +nsLocalMoveCopyMsgTxn::GetSrcIsImap(bool *isImap) +{ + *isImap = m_srcIsImap4; + return NS_OK; +} +nsresult +nsLocalMoveCopyMsgTxn::SetSrcFolder(nsIMsgFolder* srcFolder) +{ + nsresult rv = NS_ERROR_NULL_POINTER; + if (srcFolder) + m_srcFolder = do_GetWeakReference(srcFolder, &rv); + return rv; +} + +nsresult +nsLocalMoveCopyMsgTxn::SetDstFolder(nsIMsgFolder* dstFolder) +{ + nsresult rv = NS_ERROR_NULL_POINTER; + if (dstFolder) + m_dstFolder = do_GetWeakReference(dstFolder, &rv); + return rv; +} + +nsresult +nsLocalMoveCopyMsgTxn::AddSrcKey(nsMsgKey aKey) +{ + m_srcKeyArray.AppendElement(aKey); + return NS_OK; +} + +nsresult +nsLocalMoveCopyMsgTxn::AddSrcStatusOffset(uint32_t aStatusOffset) +{ + m_srcStatusOffsetArray.AppendElement(aStatusOffset); + return NS_OK; +} + + +nsresult +nsLocalMoveCopyMsgTxn::AddDstKey(nsMsgKey aKey) +{ + m_dstKeyArray.AppendElement(aKey); + return NS_OK; +} + +nsresult +nsLocalMoveCopyMsgTxn::AddDstMsgSize(uint32_t msgSize) +{ + m_dstSizeArray.AppendElement(msgSize); + return NS_OK; +} + +nsresult +nsLocalMoveCopyMsgTxn::UndoImapDeleteFlag(nsIMsgFolder* folder, + nsTArray<nsMsgKey>& keyArray, + bool deleteFlag) +{ + nsresult rv = NS_ERROR_FAILURE; + if (m_srcIsImap4) + { + nsCOMPtr<nsIImapService> imapService = do_GetService(NS_IMAPSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIUrlListener> urlListener; + nsCString msgIds; + uint32_t i, count = keyArray.Length(); + urlListener = do_QueryInterface(folder, &rv); + for (i=0; i < count; i++) + { + if (!msgIds.IsEmpty()) + msgIds.Append(','); + msgIds.AppendInt((int32_t) keyArray[i]); + } + // This is to make sure that we are in the selected state + // when executing the imap url; we don't want to load the + // folder so use lite select to do the trick + rv = imapService->LiteSelectFolder(folder, + urlListener, nullptr, nullptr); + if (!deleteFlag) + rv =imapService->AddMessageFlags(folder, + urlListener, nullptr, + msgIds, + kImapMsgDeletedFlag, + true); + else + rv = imapService->SubtractMessageFlags(folder, + urlListener, nullptr, + msgIds, + kImapMsgDeletedFlag, + true); + if (NS_SUCCEEDED(rv) && m_msgWindow) + folder->UpdateFolder(m_msgWindow); + rv = NS_OK; // always return NS_OK to indicate that the src is imap + } + else + rv = NS_ERROR_FAILURE; + return rv; +} + +NS_IMETHODIMP +nsLocalMoveCopyMsgTxn::UndoTransaction() +{ + nsresult rv; + nsCOMPtr<nsIMsgDatabase> dstDB; + + nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIMsgLocalMailFolder> dstlocalMailFolder = do_QueryReferent(m_dstFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + dstlocalMailFolder->GetDatabaseWOReparse(getter_AddRefs(dstDB)); + + if (!dstDB) + { + // This will listen for the db reparse finishing, and the corresponding + // FolderLoadedNotification. When it gets that, it will then call + // UndoTransactionInternal. + mUndoFolderListener = new nsLocalUndoFolderListener(this, dstFolder); + if (!mUndoFolderListener) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(mUndoFolderListener); + + nsCOMPtr<nsIMsgMailSession> mailSession = + do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + rv = mailSession->AddFolderListener(mUndoFolderListener, nsIFolderListener::event); + NS_ENSURE_SUCCESS(rv,rv); + + rv = dstFolder->GetMsgDatabase(getter_AddRefs(dstDB)); + NS_ENSURE_SUCCESS(rv,rv); + } + else + rv = UndoTransactionInternal(); + return rv; +} + +nsresult +nsLocalMoveCopyMsgTxn::UndoTransactionInternal() +{ + nsresult rv = NS_ERROR_FAILURE; + + if (mUndoFolderListener) + { + nsCOMPtr<nsIMsgMailSession> mailSession = + do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + rv = mailSession->RemoveFolderListener(mUndoFolderListener); + NS_ENSURE_SUCCESS(rv,rv); + + NS_RELEASE(mUndoFolderListener); + mUndoFolderListener = nullptr; + } + + nsCOMPtr<nsIMsgDatabase> srcDB; + nsCOMPtr<nsIMsgDatabase> dstDB; + nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + 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; + + // protect against a bogus undo txn without any source keys + // see bug #179856 for details + NS_ASSERTION(count, "no source keys"); + if (!count) + return NS_ERROR_UNEXPECTED; + + if (m_isMove) + { + if (m_srcIsImap4) + { + bool deleteFlag = true; //message has been deleted -we are trying to undo it + CheckForToggleDelete(srcFolder, m_srcKeyArray[0], &deleteFlag); //there could have been a toggle. + rv = UndoImapDeleteFlag(srcFolder, m_srcKeyArray, deleteFlag); + } + else if (m_canUndelete) + { + nsCOMPtr<nsIMutableArray> srcMessages = + do_CreateInstance(NS_ARRAY_CONTRACTID); + nsCOMPtr<nsIMutableArray> dstMessages = + do_CreateInstance(NS_ARRAY_CONTRACTID); + + srcDB->StartBatch(); + for (i = 0; i < count; i++) + { + rv = dstDB->GetMsgHdrForKey(m_dstKeyArray[i], + getter_AddRefs(oldHdr)); + NS_ASSERTION(oldHdr, "fatal ... cannot get old msg header\n"); + if (NS_SUCCEEDED(rv) && oldHdr) + { + rv = srcDB->CopyHdrFromExistingHdr(m_srcKeyArray[i], + oldHdr, true, + getter_AddRefs(newHdr)); + NS_ASSERTION(newHdr, + "fatal ... cannot create new msg header\n"); + if (NS_SUCCEEDED(rv) && newHdr) + { + newHdr->SetStatusOffset(m_srcStatusOffsetArray[i]); + srcDB->UndoDelete(newHdr); + srcMessages->AppendElement(newHdr, false); + // (we want to keep these two lists in sync) + dstMessages->AppendElement(oldHdr, false); + } + } + } + srcDB->EndBatch(); + + nsCOMPtr<nsIMsgFolderNotificationService> + notifier(do_GetService(NS_MSGNOTIFICATIONSERVICE_CONTRACTID)); + if (notifier) + { + // Remember that we're actually moving things back from the destination + // to the source! + notifier->NotifyMsgsMoveCopyCompleted(true, dstMessages, + srcFolder, srcMessages); + } + + nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(srcFolder); + if (localFolder) + localFolder->MarkMsgsOnPop3Server(srcMessages, POP3_NONE /*deleteMsgs*/); + } + else // undoing a move means moving the messages back. + { + nsCOMPtr<nsIMutableArray> dstMessages = + do_CreateInstance(NS_ARRAY_CONTRACTID); + m_numHdrsCopied = 0; + m_srcKeyArray.Clear(); + for (i = 0; i < count; i++) + { + // GetMsgHdrForKey is not a test for whether the key exists, so check. + bool hasKey = false; + dstDB->ContainsKey(m_dstKeyArray[i], &hasKey); + nsCOMPtr<nsIMsgDBHdr> dstHdr; + if (hasKey) + dstDB->GetMsgHdrForKey(m_dstKeyArray[i], getter_AddRefs(dstHdr)); + if (dstHdr) + { + nsCString messageId; + dstHdr->GetMessageId(getter_Copies(messageId)); + dstMessages->AppendElement(dstHdr, false); + m_copiedMsgIds.AppendElement(messageId); + } + else + { + NS_WARNING("Cannot get old msg header"); + } + } + if (m_copiedMsgIds.Length()) + { + srcFolder->AddFolderListener(this); + m_undoing = true; + return srcFolder->CopyMessages(dstFolder, dstMessages, + true, nullptr, nullptr, false, + false); + } + else + { + // Nothing to do, probably because original messages were deleted. + NS_WARNING("Undo did not find any messages to move"); + } + } + srcDB->SetSummaryValid(true); + } + + dstDB->DeleteMessages(m_dstKeyArray.Length(), m_dstKeyArray.Elements(), nullptr); + dstDB->SetSummaryValid(true); + + return rv; +} + +NS_IMETHODIMP +nsLocalMoveCopyMsgTxn::RedoTransaction() +{ + nsresult rv; + nsCOMPtr<nsIMsgDatabase> srcDB; + nsCOMPtr<nsIMsgDatabase> dstDB; + + nsCOMPtr<nsIMsgFolder> srcFolder = do_QueryReferent(m_srcFolder, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + nsCOMPtr<nsIMsgFolder> dstFolder = do_QueryReferent(m_dstFolder, &rv); + NS_ENSURE_SUCCESS(rv,rv); + + 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; + + nsCOMPtr<nsIMutableArray> srcMessages = do_CreateInstance(NS_ARRAY_CONTRACTID); + nsCOMPtr <nsISupports> msgSupports; + + for (i=0; i<count; i++) + { + rv = srcDB->GetMsgHdrForKey(m_srcKeyArray[i], + getter_AddRefs(oldHdr)); + NS_ASSERTION(oldHdr, "fatal ... cannot get old msg header\n"); + + if (NS_SUCCEEDED(rv) && oldHdr) + { + msgSupports =do_QueryInterface(oldHdr); + srcMessages->AppendElement(msgSupports, false); + + if (m_canUndelete) + { + rv = dstDB->CopyHdrFromExistingHdr(m_dstKeyArray[i], + oldHdr, true, + getter_AddRefs(newHdr)); + NS_ASSERTION(newHdr, "fatal ... cannot get new msg header\n"); + if (NS_SUCCEEDED(rv) && newHdr) + { + if (i < m_dstSizeArray.Length()) + rv = newHdr->SetMessageSize(m_dstSizeArray[i]); + dstDB->UndoDelete(newHdr); + } + } + } + } + dstDB->SetSummaryValid(true); + + if (m_isMove) + { + if (m_srcIsImap4) + { + // 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; + + bool deleteFlag = false; //message is un-deleted- we are trying to redo + CheckForToggleDelete(srcFolder, m_srcKeyArray[0], &deleteFlag); // there could have been a toggle + rv = UndoImapDeleteFlag(srcFolder, m_srcKeyArray, deleteFlag); + } + else if (m_canUndelete) + { + nsCOMPtr <nsIMsgLocalMailFolder> localFolder = do_QueryInterface(srcFolder); + if (localFolder) + localFolder->MarkMsgsOnPop3Server(srcMessages, POP3_DELETE /*deleteMsgs*/); + + rv = srcDB->DeleteMessages(m_srcKeyArray.Length(), m_srcKeyArray.Elements(), nullptr); + srcDB->SetSummaryValid(true); + } + else + { + nsCOMPtr<nsIMsgDBHdr> srcHdr; + m_numHdrsCopied = 0; + m_dstKeyArray.Clear(); + for (i = 0; i < count; i++) + { + srcDB->GetMsgHdrForKey(m_srcKeyArray[i], getter_AddRefs(srcHdr)); + NS_ASSERTION(srcHdr, "fatal ... cannot get old msg header\n"); + if (srcHdr) + { + nsCString messageId; + srcHdr->GetMessageId(getter_Copies(messageId)); + m_copiedMsgIds.AppendElement(messageId); + } + } + dstFolder->AddFolderListener(this); + m_undoing = false; + return dstFolder->CopyMessages(srcFolder, srcMessages, true, nullptr, + nullptr, false, false); + } + } + + return rv; +} + +NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemAdded(nsIMsgFolder *parentItem, nsISupports *item) +{ + nsCOMPtr<nsIMsgDBHdr> msgHdr(do_QueryInterface(item)); + if (msgHdr) + { + nsresult rv; + nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(m_undoing ? m_srcFolder : + m_dstFolder, &rv); + NS_ENSURE_SUCCESS(rv,rv); + nsCString messageId; + msgHdr->GetMessageId(getter_Copies(messageId)); + if (m_copiedMsgIds.Contains(messageId)) + { + nsMsgKey msgKey; + msgHdr->GetMessageKey(&msgKey); + if (m_undoing) + m_srcKeyArray.AppendElement(msgKey); + else + m_dstKeyArray.AppendElement(msgKey); + if (++m_numHdrsCopied == m_copiedMsgIds.Length()) + { + folder->RemoveFolderListener(this); + m_copiedMsgIds.Clear(); + } + } + } + return NS_OK; +} + +NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemRemoved(nsIMsgFolder *parentItem, nsISupports *item) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemPropertyChanged(nsIMsgFolder *item, nsIAtom *property, const char *oldValue, const char *newValue) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemIntPropertyChanged(nsIMsgFolder *item, nsIAtom *property, int64_t oldValue, int64_t newValue) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemBoolPropertyChanged(nsIMsgFolder *item, nsIAtom *property, bool oldValue, bool newValue) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemUnicharPropertyChanged(nsIMsgFolder *item, nsIAtom *property, const char16_t *oldValue, const char16_t *newValue) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property, uint32_t oldFlag, uint32_t newFlag) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalMoveCopyMsgTxn::OnItemEvent(nsIMsgFolder *aItem, nsIAtom *aEvent) +{ + return NS_OK; +} + +NS_IMPL_ISUPPORTS(nsLocalUndoFolderListener, nsIFolderListener) + +nsLocalUndoFolderListener::nsLocalUndoFolderListener(nsLocalMoveCopyMsgTxn *aTxn, nsIMsgFolder *aFolder) +{ + mTxn = aTxn; + mFolder = aFolder; +} + +nsLocalUndoFolderListener::~nsLocalUndoFolderListener() +{ +} + +NS_IMETHODIMP nsLocalUndoFolderListener::OnItemAdded(nsIMsgFolder *parentItem, nsISupports *item) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalUndoFolderListener::OnItemRemoved(nsIMsgFolder *parentItem, nsISupports *item) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalUndoFolderListener::OnItemPropertyChanged(nsIMsgFolder *item, nsIAtom *property, const char *oldValue, const char *newValue) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalUndoFolderListener::OnItemIntPropertyChanged(nsIMsgFolder *item, nsIAtom *property, int64_t oldValue, int64_t newValue) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalUndoFolderListener::OnItemBoolPropertyChanged(nsIMsgFolder *item, nsIAtom *property, bool oldValue, bool newValue) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalUndoFolderListener::OnItemUnicharPropertyChanged(nsIMsgFolder *item, nsIAtom *property, const char16_t *oldValue, const char16_t *newValue) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalUndoFolderListener::OnItemPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property, uint32_t oldFlag, uint32_t newFlag) +{ + return NS_OK; +} + +NS_IMETHODIMP nsLocalUndoFolderListener::OnItemEvent(nsIMsgFolder *aItem, nsIAtom *aEvent) +{ + if (mTxn && mFolder && aItem == mFolder) { + bool isEqual = false; + aEvent->ScriptableEquals(NS_LITERAL_STRING("FolderLoaded"), &isEqual); + if (isEqual) + return mTxn->UndoTransactionInternal(); + } + + return NS_ERROR_FAILURE; +} |