/* 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/. */ #ifndef COMM_MAILNEWS_PROTOCOLS_EXCHANGE_SRC_EXCHANGE_MESSAGECOPYHANDLER_H_ #define COMM_MAILNEWS_PROTOCOLS_EXCHANGE_SRC_EXCHANGE_MESSAGECOPYHANDLER_H_ #include "IExchangeClient.h" #include "ExchangeFolder.h" #include "nscore.h" #include "nsICopyMessageListener.h" #include "nsIMsgCopyServiceListener.h" #include "nsIMsgFolder.h" #include "nsIMsgHdr.h" #include "nsIMsgWindow.h" #include "nsTArray.h" /** * A handler for a single message copy/move operation, the source for which can * be either a file or a folder. * * An instance of `MessageCopyHandler` is created for each copy/move operation, * but a single copy/move operation can target multiple messages. * * It iterates over each message in the queue sequentially and * 1. streams it from the relevant message service * 2. creates a new item on the server for the message * 3. creates a local copy of the message * 4. triggers step 1 for the next message (if there is one) * * The current process of copying or moving a message from a folder is the * following (assuming no failures, and excepting signaling start/stop/progress * updates to the source folder and the copy listener): * 1. A consumer requests an array of messages to be copied to an Exchange * folder (`ExchangeFolder::CopyMessages()`). * 2. The Exchange folder class instantiates a copy handler, and instructs it to * start the operation (`MessageCopyHandler::StartCopyingNextMessage()`). * 3. The copy handler instructs the message service relevant to the first * message in line to stream said message's content to the handler * (`nsIMsgMessageService::CopyMessage()`). * 4. Once the full message content has been streamed, the message service then * signals to the copy handler that it has finished streaming the message's * content (`MessageCopyHandler::EndCopy()`). * 5. The copy handler instructs its Exchange client to begin creating an item * for the message on the Exchange server (`IExchangeClient::CreateMessage()`). * It is called with an instance of `MessageCreateCallbacks`, which performs * database operations and notifies the copy handler about the operation's * progress. * 6. Once the item has been created on the Exchange server, the Exchange client * instructs its callbacks instance to save the message's content to the * relevant local database and message store * (`MessageCreateCallbacks::OnRemoteCreateSuccessful()`). * 7. The Exchange client then notifies the copy handler (through its callbacks) * that the new message has been successfully created, both on the Exchange * server and locally (`MessageCopyHandler::OnCreateFinished()`). * 8. If the operation is a move, the copy handler deletes the source message on * the source folder. * 9. The copy handler repeats this process from steps 3 onwards until every * message in the original array has been copied or moved. * 10. Once the operation has completed, or if there has been a failure during * the operation, the copy handler notifies the source folder and the global * copy service about the end and final status of the operation * (`MessageCopyHandler::OnCopyCompleted()`). * * When copying from a file, the same process is followed, apart from a few * changes: * * Step 3 is skipped, as the copy handler already holds a copy of the * message's content (in the file). * * Step 8 is skipped, as we're always dealing with a copy operation when the * source is a file. * * Step 9 is skipped, as we're always dealing with a single message when the * source is a file. */ class ExchangeMessageCopyHandler : public nsICopyMessageListener { public: NS_DECL_ISUPPORTS NS_DECL_NSICOPYMESSAGELISTENER /** * Constructs a handler which will copy/move the messages specified in * `headers` from the source folder into the destination Exchange folder. */ ExchangeMessageCopyHandler(nsIMsgFolder* srcFolder, ExchangeFolder* dstFolder, const nsTArray>& headers, bool isMove, nsIMsgWindow* window, nsCString dstFolderId, IExchangeClient* client, nsIMsgCopyServiceListener* copyServiceListener) : mDstFolder(dstFolder), mHeaders(headers.Clone()), mIsMove(isMove), mIsDraft(false), mWindow(window), mSrcFolder(mozilla::Some(srcFolder)), mSrcFile(mozilla::Nothing()), mDstFolderId(std::move(dstFolderId)), mClient(client), mCopyServiceListener(copyServiceListener), mCurIndex(0) {} /** * Constructs a handler which will copy the message contained in `srcFile` * into the destination Exchange folder. */ ExchangeMessageCopyHandler(nsIFile* srcFile, ExchangeFolder* dstFolder, bool isDraft, nsIMsgWindow* window, nsCString dstFolderId, IExchangeClient* client, nsIMsgCopyServiceListener* copyServiceListener) : mDstFolder(dstFolder), mHeaders({}), mIsMove(false), mIsDraft(isDraft), mWindow(window), mSrcFolder(mozilla::Nothing()), mSrcFile(mozilla::Some(srcFile)), mDstFolderId(std::move(dstFolderId)), mClient(client), mCopyServiceListener(copyServiceListener), mCurIndex(0) {} /** * Signals that the whole operation has finished with the provided status * code. * * If consumers determine that the copy operation cannot or should not be * started, they should call this method to ensure the operation is properly * dequeued from the global copy service. */ nsresult OnCopyCompleted(nsresult status); /** * Start copying the next message in the operation's queue. * * Consumers should call his method to initiate the copy operation, after * which the handler itself will call it for any subsequent messages. */ nsresult StartCopyingNextMessage(); protected: virtual ~ExchangeMessageCopyHandler() = default; /** * Helper, called to indicate that the current message has been created in * the destination folder, both locally and on the server, or that * creation failed with the provided status. */ nsresult OnCreateFinished(nsresult status, nsIMsgDBHdr* newHdr); private: // Parameters of the copy/move operation. RefPtr mDstFolder; const nsTArray> mHeaders; bool mIsMove; bool mIsDraft; RefPtr mWindow; // The new database entries created for the messages, used for notifying // listeners at the end of the copy/move operation. nsTArray> mDstHdr; // The source from which to copy/move. This can either be a folder (when // copying/moving messages from one folder to another), or a file (when e.g. // saving a message draft or storing a copy of a sent message to the "Sent" // folder). mozilla::Maybe> mSrcFolder; mozilla::Maybe> mSrcFile; // The Exchange client and folder ID to use when creating the message item on // the remote server. nsCString mDstFolderId; RefPtr mClient; // The listener to inform of the status of the copy/move operation. RefPtr mCopyServiceListener; // The index into `mHeaders` of the message currently being copied/move. size_t mCurIndex; // A buffer containing the full message content. // // This isn't great; ideally we'd stream the message to the client as it's // provided to `CopyData()`, but the current architecture of the Exchange code // does not allow this because we require the whole message to be available // before we can serialize it. nsCString mBuffer; }; #endif // COMM_MAILNEWS_PROTOCOLS_EXCHANGE_SRC_EXCHANGE_MESSAGECOPYHANDLER_H_