diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mailnews/compose/src/nsMsgCopy.cpp | |
parent | Initial commit. (diff) | |
download | thunderbird-upstream.tar.xz thunderbird-upstream.zip |
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/mailnews/compose/src/nsMsgCopy.cpp')
-rw-r--r-- | comm/mailnews/compose/src/nsMsgCopy.cpp | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/comm/mailnews/compose/src/nsMsgCopy.cpp b/comm/mailnews/compose/src/nsMsgCopy.cpp new file mode 100644 index 0000000000..0cccc108f9 --- /dev/null +++ b/comm/mailnews/compose/src/nsMsgCopy.cpp @@ -0,0 +1,471 @@ +/* -*- 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 "nsMsgCopy.h" + +#include "nsCOMPtr.h" +#include "nsISupports.h" +#include "nsIThread.h" +#include "nscore.h" +#include "mozilla/Assertions.h" +#include "mozilla/Likely.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/RefCountType.h" +#include "mozilla/RefPtr.h" +#include "nsMsgFolderFlags.h" +#include "nsMsgMessageFlags.h" +#include "nsIMsgFolder.h" +#include "nsIMsgAccountManager.h" +#include "nsIMsgFolder.h" +#include "nsIMsgIncomingServer.h" +#include "nsIMsgProtocolInfo.h" +#include "nsISupports.h" +#include "nsIURL.h" +#include "nsNetCID.h" +#include "nsMsgCompUtils.h" +#include "prcmon.h" +#include "nsIMsgImapMailFolder.h" +#include "nsThreadUtils.h" +#include "nsIMsgWindow.h" +#include "nsIMsgProgress.h" +#include "nsComposeStrings.h" +#include "prmem.h" +#include "nsServiceManagerUtils.h" +#include "nsComponentManagerUtils.h" +#include "nsMsgUtils.h" +#include "nsIURIMutator.h" + +//////////////////////////////////////////////////////////////////////////////////// +// This is the listener class for the copy operation. We have to create this +// class to listen for message copy completion and eventually notify the caller +//////////////////////////////////////////////////////////////////////////////////// +NS_IMPL_ISUPPORTS(CopyListener, nsIMsgCopyServiceListener) + +CopyListener::CopyListener(void) { mCopyInProgress = false; } + +CopyListener::~CopyListener(void) {} + +nsresult CopyListener::OnStartCopy() { +#ifdef NS_DEBUG + printf("CopyListener::OnStartCopy()\n"); +#endif + + if (mComposeAndSend) mComposeAndSend->NotifyListenerOnStartCopy(); + return NS_OK; +} + +nsresult CopyListener::OnProgress(uint32_t aProgress, uint32_t aProgressMax) { +#ifdef NS_DEBUG + printf("CopyListener::OnProgress() %d of %d\n", aProgress, aProgressMax); +#endif + + if (mComposeAndSend) + mComposeAndSend->NotifyListenerOnProgressCopy(aProgress, aProgressMax); + + return NS_OK; +} + +nsresult CopyListener::SetMessageKey(nsMsgKey aMessageKey) { + if (mComposeAndSend) mComposeAndSend->SetMessageKey(aMessageKey); + return NS_OK; +} + +NS_IMETHODIMP +CopyListener::GetMessageId(nsACString& aMessageId) { + if (mComposeAndSend) mComposeAndSend->GetMessageId(aMessageId); + return NS_OK; +} + +nsresult CopyListener::OnStopCopy(nsresult aStatus) { + if (NS_SUCCEEDED(aStatus)) { +#ifdef NS_DEBUG + printf("CopyListener: SUCCESSFUL ON THE COPY OPERATION!\n"); +#endif + } else { +#ifdef NS_DEBUG + printf("CopyListener: COPY OPERATION FAILED!\n"); +#endif + } + + if (mCopyInProgress) { + PR_CEnterMonitor(this); + PR_CNotifyAll(this); + mCopyInProgress = false; + PR_CExitMonitor(this); + } + if (mComposeAndSend) mComposeAndSend->NotifyListenerOnStopCopy(aStatus); + + return NS_OK; +} + +nsresult CopyListener::SetMsgComposeAndSendObject(nsIMsgSend* obj) { + if (obj) mComposeAndSend = obj; + + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////////// +// END END END END END END END END END END END END END END END +// This is the listener class for the copy operation. We have to create this +// class to listen for message copy completion and eventually notify the caller +//////////////////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS(nsMsgCopy, nsIMsgCopy, nsIUrlListener) + +nsMsgCopy::nsMsgCopy() { + mFile = nullptr; + mMode = nsIMsgSend::nsMsgDeliverNow; + mSavePref = nullptr; + mIsDraft = false; + mMsgFlags = nsMsgMessageFlags::Read; +} + +nsMsgCopy::~nsMsgCopy() { PR_Free(mSavePref); } + +NS_IMETHODIMP +nsMsgCopy::StartCopyOperation(nsIMsgIdentity* aUserIdentity, nsIFile* aFile, + nsMsgDeliverMode aMode, nsIMsgSend* aMsgSendObj, + const nsACString& aSavePref, + nsIMsgDBHdr* aMsgToReplace) { + nsCOMPtr<nsIMsgFolder> dstFolder; + bool isDraft = false; + uint32_t msgFlags = nsMsgMessageFlags::Read; + bool waitForUrl = false; + nsresult rv; + + if (!aMsgSendObj) return NS_ERROR_INVALID_ARG; + + // Store away the server location... + if (!aSavePref.IsEmpty()) mSavePref = ToNewCString(aSavePref); + + // + // Vars for implementation... + // + + // QueueForLater (Outbox) + if (aMode == nsIMsgSend::nsMsgQueueForLater || + aMode == nsIMsgSend::nsMsgDeliverBackground) { + rv = GetUnsentMessagesFolder(aUserIdentity, getter_AddRefs(dstFolder), + &waitForUrl); + isDraft = false; + // Do not mark outgoing messages as read. + msgFlags = 0; + if (!dstFolder || NS_FAILED(rv)) { + return NS_MSG_UNABLE_TO_SEND_LATER; + } + } else if (aMode == nsIMsgSend::nsMsgSaveAsDraft) // SaveAsDraft (Drafts) + { + rv = GetDraftsFolder(aUserIdentity, getter_AddRefs(dstFolder), &waitForUrl); + isDraft = true; + // Do not mark drafts as read. + msgFlags = 0; + if (!dstFolder || NS_FAILED(rv)) return NS_MSG_UNABLE_TO_SAVE_DRAFT; + } else if (aMode == + nsIMsgSend::nsMsgSaveAsTemplate) // SaveAsTemplate (Templates) + { + rv = GetTemplatesFolder(aUserIdentity, getter_AddRefs(dstFolder), + &waitForUrl); + // Mark saved templates as read. + isDraft = false; + msgFlags = nsMsgMessageFlags::Read; + if (!dstFolder || NS_FAILED(rv)) return NS_MSG_UNABLE_TO_SAVE_TEMPLATE; + } else // SaveInSentFolder (Sent) - nsMsgDeliverNow or nsMsgSendUnsent + { + rv = GetSentFolder(aUserIdentity, getter_AddRefs(dstFolder), &waitForUrl); + // Mark send messages as read. + isDraft = false; + msgFlags = nsMsgMessageFlags::Read; + if (!dstFolder || NS_FAILED(rv)) return NS_MSG_COULDNT_OPEN_FCC_FOLDER; + } + + nsCOMPtr<nsIMsgWindow> msgWindow; + + if (aMsgSendObj) { + nsCOMPtr<nsIMsgProgress> progress; + aMsgSendObj->GetProgress(getter_AddRefs(progress)); + if (progress) progress->GetMsgWindow(getter_AddRefs(msgWindow)); + } + + mMode = aMode; + mFile = aFile; + mDstFolder = dstFolder; + mMsgToReplace = aMsgToReplace; + mIsDraft = isDraft; + mMsgSendObj = aMsgSendObj; + mMsgFlags = msgFlags; + if (!waitForUrl) { + // cache info needed for DoCopy and call DoCopy when OnStopUrl is called. + rv = DoCopy(aFile, dstFolder, aMsgToReplace, isDraft, msgFlags, msgWindow, + aMsgSendObj); + // N.B. "this" may be deleted when this call returns. + } + return rv; +} + +nsresult nsMsgCopy::DoCopy(nsIFile* aDiskFile, nsIMsgFolder* dstFolder, + nsIMsgDBHdr* aMsgToReplace, bool aIsDraft, + uint32_t aMsgFlags, nsIMsgWindow* msgWindow, + nsIMsgSend* aMsgSendObj) { + nsresult rv = NS_OK; + + // Check sanity + if ((!aDiskFile) || (!dstFolder)) return NS_ERROR_INVALID_ARG; + + // Call copyservice with dstFolder, disk file, and txnManager + if (NS_SUCCEEDED(rv)) { + RefPtr<CopyListener> copyListener = new CopyListener(); + if (!copyListener) return NS_ERROR_OUT_OF_MEMORY; + + copyListener->SetMsgComposeAndSendObject(aMsgSendObj); + nsCOMPtr<nsIThread> thread; + + if (aIsDraft) { + nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(dstFolder); + nsCOMPtr<nsIMsgAccountManager> accountManager = + do_GetService("@mozilla.org/messenger/account-manager;1", &rv); + if (NS_FAILED(rv)) return rv; + bool shutdownInProgress = false; + rv = accountManager->GetShutdownInProgress(&shutdownInProgress); + + if (NS_SUCCEEDED(rv) && shutdownInProgress && imapFolder) { + // set the following only when we were in the middle of shutdown + // process + copyListener->mCopyInProgress = true; + thread = do_GetCurrentThread(); + } + } + nsCOMPtr<nsIMsgCopyService> copyService = + do_GetService("@mozilla.org/messenger/messagecopyservice;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = copyService->CopyFileMessage(aDiskFile, dstFolder, aMsgToReplace, + aIsDraft, aMsgFlags, EmptyCString(), + copyListener, msgWindow); + // copyListener->mCopyInProgress can only be set when we are in the + // middle of the shutdown process + while (copyListener->mCopyInProgress) { + PR_CEnterMonitor(copyListener); + PR_CWait(copyListener, PR_MicrosecondsToInterval(1000UL)); + PR_CExitMonitor(copyListener); + if (thread) NS_ProcessPendingEvents(thread); + } + } + + return rv; +} + +NS_IMETHODIMP +nsMsgCopy::GetDstFolder(nsIMsgFolder** aDstFolder) { + NS_ENSURE_ARG_POINTER(aDstFolder); + NS_IF_ADDREF(*aDstFolder = mDstFolder); + return NS_OK; +} + +// nsIUrlListener methods +NS_IMETHODIMP +nsMsgCopy::OnStartRunningUrl(nsIURI* aUrl) { return NS_OK; } + +NS_IMETHODIMP +nsMsgCopy::OnStopRunningUrl(nsIURI* aUrl, nsresult aExitCode) { + nsresult rv = aExitCode; + if (NS_SUCCEEDED(aExitCode)) { + rv = DoCopy(mFile, mDstFolder, mMsgToReplace, mIsDraft, mMsgFlags, nullptr, + mMsgSendObj); + } + return rv; +} + +nsresult nsMsgCopy::GetUnsentMessagesFolder(nsIMsgIdentity* userIdentity, + nsIMsgFolder** folder, + bool* waitForUrl) { + nsresult ret = LocateMessageFolder( + userIdentity, nsIMsgSend::nsMsgQueueForLater, mSavePref, folder); + if (*folder) (*folder)->SetFlag(nsMsgFolderFlags::Queue); + CreateIfMissing(folder, waitForUrl); + return ret; +} + +nsresult nsMsgCopy::GetDraftsFolder(nsIMsgIdentity* userIdentity, + nsIMsgFolder** folder, bool* waitForUrl) { + nsresult ret = LocateMessageFolder(userIdentity, nsIMsgSend::nsMsgSaveAsDraft, + mSavePref, folder); + if (*folder) (*folder)->SetFlag(nsMsgFolderFlags::Drafts); + CreateIfMissing(folder, waitForUrl); + return ret; +} + +nsresult nsMsgCopy::GetTemplatesFolder(nsIMsgIdentity* userIdentity, + nsIMsgFolder** folder, + bool* waitForUrl) { + nsresult ret = LocateMessageFolder( + userIdentity, nsIMsgSend::nsMsgSaveAsTemplate, mSavePref, folder); + if (*folder) (*folder)->SetFlag(nsMsgFolderFlags::Templates); + CreateIfMissing(folder, waitForUrl); + return ret; +} + +nsresult nsMsgCopy::GetSentFolder(nsIMsgIdentity* userIdentity, + nsIMsgFolder** folder, bool* waitForUrl) { + nsresult ret = LocateMessageFolder(userIdentity, nsIMsgSend::nsMsgDeliverNow, + mSavePref, folder); + if (*folder) { + // If mSavePref is the same as the identity's fcc folder, set the sent flag. + nsCString identityFccUri; + userIdentity->GetFccFolder(identityFccUri); + if (identityFccUri.Equals(mSavePref)) + (*folder)->SetFlag(nsMsgFolderFlags::SentMail); + } + CreateIfMissing(folder, waitForUrl); + return ret; +} + +nsresult nsMsgCopy::CreateIfMissing(nsIMsgFolder** folder, bool* waitForUrl) { + nsresult rv = NS_OK; + if (folder && *folder) { + nsCOMPtr<nsIMsgFolder> parent; + (*folder)->GetParent(getter_AddRefs(parent)); + if (!parent) { + nsCOMPtr<nsIFile> folderPath; + // for local folders, path is to the berkeley mailbox. + // for imap folders, path needs to have .msf appended to the name + (*folder)->GetFilePath(getter_AddRefs(folderPath)); + + nsCOMPtr<nsIMsgIncomingServer> server; + rv = (*folder)->GetServer(getter_AddRefs(server)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgProtocolInfo> protocolInfo; + rv = server->GetProtocolInfo(getter_AddRefs(protocolInfo)); + NS_ENSURE_SUCCESS(rv, rv); + + bool isAsyncFolder; + rv = protocolInfo->GetFoldersCreatedAsync(&isAsyncFolder); + NS_ENSURE_SUCCESS(rv, rv); + + // if we can't get the path from the folder, then try to create the + // storage. for imap, it doesn't matter if the .msf file exists - it still + // might not exist on the server, so we should try to create it + bool exists = false; + if (!isAsyncFolder && folderPath) folderPath->Exists(&exists); + if (!exists) { + (*folder)->CreateStorageIfMissing(this); + if (isAsyncFolder) *waitForUrl = true; + + rv = NS_OK; + } + } + } + return rv; +} +//////////////////////////////////////////////////////////////////////////////////// +// Utility Functions for MsgFolders +//////////////////////////////////////////////////////////////////////////////////// +nsresult LocateMessageFolder(nsIMsgIdentity* userIdentity, + nsMsgDeliverMode aFolderType, + const char* aFolderURI, nsIMsgFolder** msgFolder) { + nsresult rv = NS_OK; + + if (!msgFolder) return NS_ERROR_NULL_POINTER; + *msgFolder = nullptr; + + if (!aFolderURI || !*aFolderURI) return NS_ERROR_INVALID_ARG; + + // as long as it doesn't start with anyfolder:// + if (PL_strncasecmp(ANY_SERVER, aFolderURI, strlen(aFolderURI)) != 0) { + nsCOMPtr<nsIMsgFolder> folder; + rv = GetOrCreateFolder(nsDependentCString(aFolderURI), + getter_AddRefs(folder)); + NS_ENSURE_SUCCESS(rv, rv); + // Don't check validity of folder - caller will handle creating it. + nsCOMPtr<nsIMsgIncomingServer> server; + // make sure that folder hierarchy is built so that legitimate parent-child + // relationship is established. + rv = folder->GetServer(getter_AddRefs(server)); + NS_ENSURE_SUCCESS(rv, rv); + return server->GetMsgFolderFromURI(folder, nsDependentCString(aFolderURI), + msgFolder); + } else { + if (!userIdentity) return NS_ERROR_INVALID_ARG; + + // get the account manager + nsCOMPtr<nsIMsgAccountManager> accountManager = + do_GetService("@mozilla.org/messenger/account-manager;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // If any folder will do, go look for one. + nsTArray<RefPtr<nsIMsgIncomingServer>> servers; + rv = accountManager->GetServersForIdentity(userIdentity, servers); + NS_ENSURE_SUCCESS(rv, rv); + + // Ok, we have to look through the servers and try to find the server that + // has a valid folder of the type that interests us... + for (auto inServer : servers) { + // Now that we have the server...we need to get the named message folder + + // If aFolderURI is passed in, then the user has chosen a specific + // mail folder to save the message, but if it is null, just find the + // first one and make that work. The folder is specified as a URI, like + // the following: + // + // mailbox://nobody@Local Folders/Sent + // imap://rhp@nsmail-2/Drafts + // newsgroup://news.mozilla.org/netscape.test + // + nsCString serverURI; + rv = inServer->GetServerURI(serverURI); + if (NS_FAILED(rv) || serverURI.IsEmpty()) continue; + + nsCOMPtr<nsIMsgFolder> rootFolder; + rv = inServer->GetRootFolder(getter_AddRefs(rootFolder)); + + if (NS_FAILED(rv) || (!rootFolder)) continue; + + // use the defaults by getting the folder by flags + if (aFolderType == nsIMsgSend::nsMsgQueueForLater || + aFolderType == nsIMsgSend::nsMsgDeliverBackground) { + // QueueForLater (Outbox) + rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Queue, msgFolder); + } else if (aFolderType == + nsIMsgSend::nsMsgSaveAsDraft) // SaveAsDraft (Drafts) + { + rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Drafts, msgFolder); + } else if (aFolderType == + nsIMsgSend::nsMsgSaveAsTemplate) // SaveAsTemplate (Templates) + { + rootFolder->GetFolderWithFlags(nsMsgFolderFlags::Templates, msgFolder); + } else // SaveInSentFolder (Sent) - nsMsgDeliverNow or nsMsgSendUnsent + { + rootFolder->GetFolderWithFlags(nsMsgFolderFlags::SentMail, msgFolder); + } + + if (*msgFolder) { + return NS_OK; + } + } + } + return NS_ERROR_FAILURE; +} + +// +// Figure out if a folder is local or not and return a boolean to +// say so. +// +nsresult MessageFolderIsLocal(nsIMsgIdentity* userIdentity, + nsMsgDeliverMode aFolderType, + const char* aFolderURI, bool* aResult) { + nsresult rv; + + if (!aFolderURI) return NS_ERROR_NULL_POINTER; + + nsCOMPtr<nsIURL> url; + rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) + .SetSpec(nsDependentCString(aFolderURI)) + .Finalize(url); + if (NS_FAILED(rv)) return rv; + + /* mailbox:/ means its local (on disk) */ + rv = url->SchemeIs("mailbox", aResult); + if (NS_FAILED(rv)) return rv; + return NS_OK; +} |