/* -*- 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 "nsMsgBaseCID.h" #include "nsMsgFolderFlags.h" #include "nsIMsgFolder.h" #include "nsIMsgAccountManager.h" #include "nsIMsgFolder.h" #include "nsIMsgIncomingServer.h" #include "nsIMsgProtocolInfo.h" #include "nsISupports.h" #include "nsIRDFService.h" #include "nsIRDFResource.h" #include "nsRDFCID.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 "nsArrayUtils.h" static NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID); //////////////////////////////////////////////////////////////////////////////////// // 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, nsIUrlListener) nsMsgCopy::nsMsgCopy() { mFile = nullptr; mMode = nsIMsgSend::nsMsgDeliverNow; mSavePref = nullptr; } nsMsgCopy::~nsMsgCopy() { PR_Free(mSavePref); } nsresult nsMsgCopy::StartCopyOperation(nsIMsgIdentity *aUserIdentity, nsIFile *aFile, nsMsgDeliverMode aMode, nsIMsgSend *aMsgSendObj, const char *aSavePref, nsIMsgDBHdr *aMsgToReplace) { nsCOMPtr dstFolder; bool isDraft = false; bool waitForUrl = false; nsresult rv; if (!aMsgSendObj) return NS_ERROR_INVALID_ARG; // Store away the server location... if (aSavePref) mSavePref = PL_strdup(aSavePref); // // Vars for implementation... // // QueueForLater (Outbox) if (aMode == nsIMsgSend::nsMsgQueueForLater || aMode == nsIMsgSend::nsMsgDeliverBackground) { rv = GetUnsentMessagesFolder(aUserIdentity, getter_AddRefs(dstFolder), &waitForUrl); isDraft = false; 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; 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); isDraft = false; 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); isDraft = false; if (!dstFolder || NS_FAILED(rv)) return NS_MSG_COULDNT_OPEN_FCC_FOLDER; } nsCOMPtr msgWindow; if (aMsgSendObj) { nsCOMPtr 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; if (!waitForUrl) { // cache info needed for DoCopy and call DoCopy when OnStopUrl is called. rv = DoCopy(aFile, dstFolder, aMsgToReplace, isDraft, 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, 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 = new CopyListener(); if (!copyListener) return NS_ERROR_OUT_OF_MEMORY; copyListener->SetMsgComposeAndSendObject(aMsgSendObj); nsCOMPtr thread; if (aIsDraft) { nsCOMPtr imapFolder = do_QueryInterface(dstFolder); nsCOMPtr accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &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 copyService = do_GetService(NS_MSGCOPYSERVICE_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); rv = copyService->CopyFileMessage(aDiskFile, dstFolder, aMsgToReplace, aIsDraft, aIsDraft ? 0 : nsMsgMessageFlags::Read, 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; } // 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, 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 parent; (*folder)->GetParent(getter_AddRefs(parent)); if (!parent) { nsCOMPtr 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 server; rv = (*folder)->GetServer(getter_AddRefs(server)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 rdf(do_GetService(kRDFServiceCID, &rv)); if (NS_FAILED(rv)) return rv; // get the corresponding RDF resource // RDF will create the folder resource if it doesn't already exist nsCOMPtr resource; rv = rdf->GetResource(nsDependentCString(aFolderURI), getter_AddRefs(resource)); if (NS_FAILED(rv)) return rv; nsCOMPtr folderResource; folderResource = do_QueryInterface(resource, &rv); if (NS_SUCCEEDED(rv) && folderResource) { // don't check validity of folder - caller will handle creating it nsCOMPtr server; //make sure that folder hierarchy is built so that legitimate parent-child relationship is established rv = folderResource->GetServer(getter_AddRefs(server)); NS_ENSURE_SUCCESS(rv,rv); return server->GetMsgFolderFromURI(folderResource, nsDependentCString(aFolderURI), msgFolder); } else { return NS_ERROR_FAILURE; } } else { uint32_t cnt = 0; uint32_t i; if (!userIdentity) return NS_ERROR_INVALID_ARG; // get the account manager nsCOMPtr accountManager = do_GetService(NS_MSGACCOUNTMANAGER_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; // If any folder will do, go look for one. nsCOMPtr retval; accountManager->GetServersForIdentity(userIdentity, getter_AddRefs(retval)); if (!retval) return NS_ERROR_FAILURE; // 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... rv = retval->GetLength(&cnt); if (NS_FAILED(rv)) return rv; for (i=0; i inServer; inServer = do_QueryElementAt(retval, i, &rv); if(NS_FAILED(rv) || (!inServer)) continue; // // 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 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 url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; rv = url->SetSpec(nsDependentCString(aFolderURI)); 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; }