diff options
Diffstat (limited to '')
-rw-r--r-- | comm/mailnews/compose/src/nsMsgSendLater.cpp | 1406 |
1 files changed, 1406 insertions, 0 deletions
diff --git a/comm/mailnews/compose/src/nsMsgSendLater.cpp b/comm/mailnews/compose/src/nsMsgSendLater.cpp new file mode 100644 index 0000000000..8e3b8b0a5d --- /dev/null +++ b/comm/mailnews/compose/src/nsMsgSendLater.cpp @@ -0,0 +1,1406 @@ +/* -*- 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 "nsMsgSendLater.h" +#include "nsIMsgMailNewsUrl.h" +#include "nsMsgCopy.h" +#include "nsIMsgSend.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "nsIMsgMessageService.h" +#include "nsIMsgAccountManager.h" +#include "nsMsgCompUtils.h" +#include "nsMsgUtils.h" +#include "nsMailHeaders.h" +#include "nsMsgPrompts.h" +#include "nsISmtpUrl.h" +#include "nsIChannel.h" +#include "nsNetUtil.h" +#include "prlog.h" +#include "prmem.h" +#include "nsIMimeConverter.h" +#include "nsComposeStrings.h" +#include "nsIObserverService.h" +#include "nsIMsgLocalMailFolder.h" +#include "nsIMsgDatabase.h" +#include "nsIInputStream.h" +#include "nsIOutputStream.h" +#include "nsIMsgWindow.h" +#include "nsMsgMessageFlags.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/Services.h" + +// Consts for checking and sending mail in milliseconds + +// 1 second from mail into the unsent messages folder to initially trying to +// send it. +const uint32_t kInitialMessageSendTime = 1000; + +NS_IMPL_ISUPPORTS(nsMsgSendLater, nsIMsgSendLater, nsIFolderListener, + nsIRequestObserver, nsIStreamListener, nsIObserver, + nsIUrlListener, nsIMsgShutdownTask) + +nsMsgSendLater::nsMsgSendLater() { + mSendingMessages = false; + mTimerSet = false; + mTotalSentSuccessfully = 0; + mTotalSendCount = 0; + mLeftoverBuffer = nullptr; + + m_to = nullptr; + m_bcc = nullptr; + m_fcc = nullptr; + m_newsgroups = nullptr; + m_newshost = nullptr; + m_headers = nullptr; + m_flags = 0; + m_headersFP = 0; + m_inhead = true; + m_headersPosition = 0; + + m_bytesRead = 0; + m_position = 0; + m_flagsPosition = 0; + m_headersSize = 0; + + mIdentityKey = nullptr; + mAccountKey = nullptr; + + mUserInitiated = false; +} + +nsMsgSendLater::~nsMsgSendLater() { + PR_Free(m_to); + PR_Free(m_fcc); + PR_Free(m_bcc); + PR_Free(m_newsgroups); + PR_Free(m_newshost); + PR_Free(m_headers); + PR_Free(mLeftoverBuffer); + PR_Free(mIdentityKey); + PR_Free(mAccountKey); +} + +nsresult nsMsgSendLater::Init() { + nsresult rv; + nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + bool sendInBackground; + rv = prefs->GetBoolPref("mailnews.sendInBackground", &sendInBackground); + // If we're not sending in the background, don't do anything else + if (NS_FAILED(rv) || !sendInBackground) return NS_OK; + + // We need to know when we're shutting down. + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED); + + rv = observerService->AddObserver(this, "xpcom-shutdown", false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = observerService->AddObserver(this, "quit-application", false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = observerService->AddObserver(this, "msg-shutdown", false); + NS_ENSURE_SUCCESS(rv, rv); + + // Subscribe to the unsent messages folder + // XXX This code should be set up for multiple unsent folders, however we + // don't support that at the moment, so for now just assume one folder. + nsCOMPtr<nsIMsgFolder> folder; + rv = GetUnsentMessagesFolder(nullptr, getter_AddRefs(folder)); + // There doesn't have to be a nsMsgQueueForLater flagged folder. + if (NS_FAILED(rv) || !folder) return NS_OK; + + rv = folder->AddFolderListener(this); + NS_ENSURE_SUCCESS(rv, rv); + + // XXX may want to send messages X seconds after startup if there are any. + + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (aSubject == mTimer && !strcmp(aTopic, "timer-callback")) { + if (mTimer) + mTimer->Cancel(); + else + NS_ERROR("mTimer was null in nsMsgSendLater::Observe"); + + mTimerSet = false; + // If we've already started a send since the timer fired, don't start + // another + if (!mSendingMessages) InternalSendMessages(false, nullptr); + } else if (!strcmp(aTopic, "quit-application")) { + // If the timer is set, cancel it - we're quitting, the shutdown service + // interfaces will sort out sending etc. + if (mTimer) mTimer->Cancel(); + + mTimerSet = false; + } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { + // We're shutting down. Unsubscribe from the unsentFolder notifications + // they aren't any use to us now, we don't want to start sending more + // messages. + nsresult rv; + if (mMessageFolder) { + nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mMessageFolder, &rv); + if (folder) { + rv = folder->RemoveFolderListener(this); + NS_ENSURE_SUCCESS(rv, rv); + folder->ForceDBClosed(); + } + } + + // Now remove ourselves from the observer service as well. + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + NS_ENSURE_TRUE(observerService, NS_ERROR_UNEXPECTED); + + rv = observerService->RemoveObserver(this, "xpcom-shutdown"); + NS_ENSURE_SUCCESS(rv, rv); + + rv = observerService->RemoveObserver(this, "quit-application"); + NS_ENSURE_SUCCESS(rv, rv); + + rv = observerService->RemoveObserver(this, "msg-shutdown"); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::SetStatusFeedback(nsIMsgStatusFeedback* aFeedback) { + mFeedback = aFeedback; + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::GetStatusFeedback(nsIMsgStatusFeedback** aFeedback) { + NS_ENSURE_ARG_POINTER(aFeedback); + NS_IF_ADDREF(*aFeedback = mFeedback); + return NS_OK; +} + +// Stream is done...drive on! +NS_IMETHODIMP +nsMsgSendLater::OnStopRequest(nsIRequest* request, nsresult status) { + nsresult rv; + + // First, this shouldn't happen, but if + // it does, flush the buffer and move on. + if (mLeftoverBuffer) { + DeliverQueuedLine(mLeftoverBuffer, PL_strlen(mLeftoverBuffer)); + } + + if (mOutFile) mOutFile->Close(); + + // See if we succeeded on reading the message from the message store? + // + if (NS_SUCCEEDED(status)) { + // Message is done...send it! + rv = CompleteMailFileSend(); + +#ifdef NS_DEBUG + printf("nsMsgSendLater: Success on getting message...\n"); +#endif + + // If the send operation failed..try the next one... + if (NS_FAILED(rv)) { + rv = StartNextMailFileSend(rv); + if (NS_FAILED(rv)) + EndSendMessages(rv, nullptr, mTotalSendCount, mTotalSentSuccessfully); + } + } else { + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); + if (!channel) return NS_ERROR_FAILURE; + + // extract the prompt object to use for the alert from the url.... + nsCOMPtr<nsIURI> uri; + nsCOMPtr<mozIDOMWindowProxy> domWindow; + if (channel) { + channel->GetURI(getter_AddRefs(uri)); + nsCOMPtr<nsIMsgMailNewsUrl> msgUrl(do_QueryInterface(uri)); + nsCOMPtr<nsIMsgWindow> msgWindow; + if (msgUrl) msgUrl->GetMsgWindow(getter_AddRefs(msgWindow)); + if (msgWindow) msgWindow->GetDomWindow(getter_AddRefs(domWindow)); + } + + nsMsgDisplayMessageByName(domWindow, "errorQueuedDeliveryFailed"); + + // Getting the data failed, but we will still keep trying to send the + // rest... + rv = StartNextMailFileSend(status); + if (NS_FAILED(rv)) + EndSendMessages(rv, nullptr, mTotalSendCount, mTotalSentSuccessfully); + } + + return rv; +} + +char* FindEOL(char* inBuf, char* buf_end) { + char* buf = inBuf; + char* findLoc = nullptr; + + while (buf <= buf_end) + if (*buf == 0) + return buf; + else if ((*buf == '\n') || (*buf == '\r')) { + findLoc = buf; + break; + } else + ++buf; + + if (!findLoc) + return nullptr; + else if ((findLoc + 1) > buf_end) + return buf; + + if ((*findLoc == '\n' && *(findLoc + 1) == '\r') || + (*findLoc == '\r' && *(findLoc + 1) == '\n')) + findLoc++; // possibly a pair. + return findLoc; +} + +nsresult nsMsgSendLater::RebufferLeftovers(char* startBuf, uint32_t aLen) { + PR_FREEIF(mLeftoverBuffer); + mLeftoverBuffer = (char*)PR_Malloc(aLen + 1); + if (!mLeftoverBuffer) return NS_ERROR_OUT_OF_MEMORY; + + memcpy(mLeftoverBuffer, startBuf, aLen); + mLeftoverBuffer[aLen] = '\0'; + return NS_OK; +} + +nsresult nsMsgSendLater::BuildNewBuffer(const char* aBuf, uint32_t aCount, + uint32_t* totalBufSize) { + // Only build a buffer when there are leftovers... + if (!mLeftoverBuffer) { + return NS_ERROR_FAILURE; + } + + int32_t leftoverSize = PL_strlen(mLeftoverBuffer); + char* newBuffer = (char*)PR_Realloc(mLeftoverBuffer, aCount + leftoverSize); + NS_ENSURE_TRUE(newBuffer, NS_ERROR_OUT_OF_MEMORY); + mLeftoverBuffer = newBuffer; + + memcpy(mLeftoverBuffer + leftoverSize, aBuf, aCount); + *totalBufSize = aCount + leftoverSize; + return NS_OK; +} + +// Got data? +NS_IMETHODIMP +nsMsgSendLater::OnDataAvailable(nsIRequest* request, nsIInputStream* inStr, + uint64_t sourceOffset, uint32_t count) { + NS_ENSURE_ARG_POINTER(inStr); + + // This is a little bit tricky since we have to chop random + // buffers into lines and deliver the lines...plus keeping the + // leftovers for next time...some fun, eh? + // + nsresult rv = NS_OK; + char* startBuf; + char* endBuf; + char* lineEnd; + char* newbuf = nullptr; + uint32_t size; + + uint32_t aCount = count; + char* aBuf = (char*)PR_Malloc(aCount + 1); + + inStr->Read(aBuf, count, &aCount); + + // First, create a new work buffer that will + if (NS_FAILED(BuildNewBuffer(aBuf, aCount, &size))) // no leftovers... + { + startBuf = (char*)aBuf; + endBuf = (char*)(aBuf + aCount - 1); + } else // yum, leftovers...new buffer created...sitting in mLeftoverBuffer + { + newbuf = mLeftoverBuffer; + startBuf = newbuf; + endBuf = startBuf + size - 1; + mLeftoverBuffer = nullptr; // null out this + } + + while (startBuf <= endBuf) { + lineEnd = FindEOL(startBuf, endBuf); + if (!lineEnd) { + rv = RebufferLeftovers(startBuf, (endBuf - startBuf) + 1); + break; + } + + rv = DeliverQueuedLine(startBuf, (lineEnd - startBuf) + 1); + if (NS_FAILED(rv)) break; + + startBuf = lineEnd + 1; + } + + PR_Free(newbuf); + PR_Free(aBuf); + return rv; +} + +NS_IMETHODIMP +nsMsgSendLater::OnStartRunningUrl(nsIURI* url) { return NS_OK; } + +NS_IMETHODIMP +nsMsgSendLater::OnStopRunningUrl(nsIURI* url, nsresult aExitCode) { + if (NS_SUCCEEDED(aExitCode)) InternalSendMessages(mUserInitiated, mIdentity); + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::OnStartRequest(nsIRequest* request) { return NS_OK; } + +//////////////////////////////////////////////////////////////////////////////////// +// This is the listener class for the send operation. We have to create this +// class to listen for message send completion and eventually notify the caller +//////////////////////////////////////////////////////////////////////////////////// +NS_IMPL_ISUPPORTS(SendOperationListener, nsIMsgSendListener, + nsIMsgCopyServiceListener) + +SendOperationListener::SendOperationListener(nsMsgSendLater* aSendLater) + : mSendLater(aSendLater) {} + +SendOperationListener::~SendOperationListener(void) {} + +NS_IMETHODIMP +SendOperationListener::OnGetDraftFolderURI(const char* aMsgID, + const nsACString& aFolderURI) { + return NS_OK; +} + +NS_IMETHODIMP +SendOperationListener::OnStartSending(const char* aMsgID, uint32_t aMsgSize) { +#ifdef NS_DEBUG + printf("SendOperationListener::OnStartSending()\n"); +#endif + return NS_OK; +} + +NS_IMETHODIMP +SendOperationListener::OnProgress(const char* aMsgID, uint32_t aProgress, + uint32_t aProgressMax) { +#ifdef NS_DEBUG + printf("SendOperationListener::OnProgress()\n"); +#endif + return NS_OK; +} + +NS_IMETHODIMP +SendOperationListener::OnStatus(const char* aMsgID, const char16_t* aMsg) { +#ifdef NS_DEBUG + printf("SendOperationListener::OnStatus()\n"); +#endif + + return NS_OK; +} + +NS_IMETHODIMP +SendOperationListener::OnSendNotPerformed(const char* aMsgID, + nsresult aStatus) { + return NS_OK; +} + +NS_IMETHODIMP +SendOperationListener::OnStopSending(const char* aMsgID, nsresult aStatus, + const char16_t* aMsg, + nsIFile* returnFile) { + if (mSendLater && !mSendLater->OnSendStepFinished(aStatus)) + mSendLater = nullptr; + + return NS_OK; +} + +NS_IMETHODIMP +SendOperationListener::OnTransportSecurityError( + const char* msgID, nsresult status, nsITransportSecurityInfo* secInfo, + nsACString const& location) { + return NS_OK; +} + +// nsIMsgCopyServiceListener + +NS_IMETHODIMP +SendOperationListener::OnStartCopy(void) { return NS_OK; } + +NS_IMETHODIMP +SendOperationListener::OnProgress(uint32_t aProgress, uint32_t aProgressMax) { + return NS_OK; +} + +NS_IMETHODIMP +SendOperationListener::SetMessageKey(nsMsgKey aKey) { + MOZ_ASSERT_UNREACHABLE("SendOperationListener::SetMessageKey()"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +SendOperationListener::GetMessageId(nsACString& messageId) { + MOZ_ASSERT_UNREACHABLE("SendOperationListener::GetMessageId()"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +SendOperationListener::OnStopCopy(nsresult aStatus) { + if (mSendLater) { + mSendLater->OnCopyStepFinished(aStatus); + mSendLater = nullptr; + } + + return NS_OK; +} + +nsresult nsMsgSendLater::CompleteMailFileSend() { + // get the identity from the key + // if no key, or we fail to find the identity + // use the default identity on the default account + nsCOMPtr<nsIMsgIdentity> identity; + nsresult rv = GetIdentityFromKey(mIdentityKey, getter_AddRefs(identity)); + NS_ENSURE_SUCCESS(rv, rv); + if (!identity) return NS_ERROR_UNEXPECTED; + + // If for some reason the tmp file didn't get created, we've failed here + bool created; + mTempFile->Exists(&created); + if (!created) return NS_ERROR_FAILURE; + + nsCOMPtr<nsIMsgCompFields> compFields = + do_CreateInstance("@mozilla.org/messengercompose/composefields;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgSend> pMsgSend = + do_CreateInstance("@mozilla.org/messengercompose/send;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // Since we have already parsed all of the headers, we are simply going to + // set the composition fields and move on. + nsCString author; + mMessage->GetAuthor(getter_Copies(author)); + + nsMsgCompFields* fields = (nsMsgCompFields*)compFields.get(); + + fields->SetFrom(author.get()); + + if (m_to) { + fields->SetTo(m_to); + } + + if (m_bcc) { + fields->SetBcc(m_bcc); + } + + if (m_fcc) { + fields->SetFcc(m_fcc); + } + + if (m_newsgroups) fields->SetNewsgroups(m_newsgroups); + +#if 0 + // needs cleanup. Is this needed? + if (m_newshost) + fields->SetNewspostUrl(m_newshost); +#endif + + // Create the listener for the send operation... + RefPtr<SendOperationListener> sendListener = new SendOperationListener(this); + + RefPtr<mozilla::dom::Promise> promise; + rv = pMsgSend->SendMessageFile( + identity, mAccountKey, + compFields, // nsIMsgCompFields *fields, + mTempFile, // nsIFile *sendFile, + true, // bool deleteSendFileOnCompletion, + false, // bool digest_p, + nsIMsgSend::nsMsgSendUnsent, // nsMsgDeliverMode mode, + nullptr, // nsIMsgDBHdr *msgToReplace, + sendListener, mFeedback, nullptr, getter_AddRefs(promise)); + return rv; +} + +nsresult nsMsgSendLater::StartNextMailFileSend(nsresult prevStatus) { + if (mTotalSendCount >= (uint32_t)mMessagesToSend.Count()) { + // Notify that this message has finished being sent. + NotifyListenersOnProgress(mTotalSendCount, mMessagesToSend.Count(), 100, + 100); + + // EndSendMessages resets everything for us + EndSendMessages(prevStatus, nullptr, mTotalSendCount, + mTotalSentSuccessfully); + + // XXX Should we be releasing references so that we don't hold onto items + // unnecessarily. + return NS_OK; + } + + // If we've already sent a message, and are sending more, send out a progress + // update with 100% for both send and copy as we must have finished by now. + if (mTotalSendCount > 0) { + NotifyListenersOnProgress(mTotalSendCount, mMessagesToSend.Count(), 100, + 100); + } + + mMessage = mMessagesToSend[mTotalSendCount++]; + + if (!mMessageFolder) return NS_ERROR_UNEXPECTED; + + nsCString messageURI; + nsresult rv; + nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mMessageFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + folder->GetUriForMsg(mMessage, messageURI); + + rv = nsMsgCreateTempFile("nsqmail.tmp", getter_AddRefs(mTempFile)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgMessageService> messageService; + rv = GetMessageServiceFromURI(messageURI, getter_AddRefs(messageService)); + if (NS_FAILED(rv) && !messageService) return NS_ERROR_FACTORY_NOT_LOADED; + + nsCString identityKey; + rv = mMessage->GetStringProperty(HEADER_X_MOZILLA_IDENTITY_KEY, identityKey); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIMsgIdentity> identity; + rv = GetIdentityFromKey(identityKey.get(), getter_AddRefs(identity)); + NS_ENSURE_SUCCESS(rv, rv); + if (!identity) return NS_ERROR_UNEXPECTED; + + // Notify that we're just about to start sending this message + NotifyListenersOnMessageStartSending(mTotalSendCount, mMessagesToSend.Count(), + identity); + + // Setup what we need to parse the data stream correctly + m_inhead = true; + m_headersFP = 0; + m_headersPosition = 0; + m_bytesRead = 0; + m_position = 0; + m_flagsPosition = 0; + m_headersSize = 0; + PR_FREEIF(mLeftoverBuffer); + + // Now, get our stream listener interface and plug it into the LoadMessage + // operation + rv = messageService->LoadMessage(messageURI, + static_cast<nsIStreamListener*>(this), + nullptr, nullptr, false); + + return rv; +} + +NS_IMETHODIMP +nsMsgSendLater::GetUnsentMessagesFolder(nsIMsgIdentity* aIdentity, + nsIMsgFolder** aFolder) { + nsresult rv = NS_OK; + nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mMessageFolder); + if (!folder) { + nsCString uri; + GetFolderURIFromUserPrefs(nsIMsgSend::nsMsgQueueForLater, aIdentity, uri); + rv = LocateMessageFolder(aIdentity, nsIMsgSend::nsMsgQueueForLater, + uri.get(), getter_AddRefs(folder)); + mMessageFolder = do_GetWeakReference(folder); + if (!mMessageFolder) return NS_ERROR_FAILURE; + } + if (folder) folder.forget(aFolder); + return rv; +} + +NS_IMETHODIMP +nsMsgSendLater::HasUnsentMessages(nsIMsgIdentity* aIdentity, bool* aResult) { + NS_ENSURE_ARG_POINTER(aResult); + *aResult = false; + nsresult rv; + + nsCOMPtr<nsIMsgAccountManager> accountManager = + do_GetService("@mozilla.org/messenger/account-manager;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsTArray<RefPtr<nsIMsgAccount>> accounts; + rv = accountManager->GetAccounts(accounts); + NS_ENSURE_SUCCESS(rv, rv); + + if (accounts.IsEmpty()) + return NS_OK; // no account set up -> no unsent messages + + // XXX This code should be set up for multiple unsent folders, however we + // don't support that at the moment, so for now just assume one folder. + if (!mMessageFolder) { + nsCOMPtr<nsIMsgFolder> folder; + rv = GetUnsentMessagesFolder(nullptr, getter_AddRefs(folder)); + // There doesn't have to be a nsMsgQueueForLater flagged folder. + if (NS_FAILED(rv) || !folder) return NS_OK; + } + rv = ReparseDBIfNeeded(nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + int32_t totalMessages; + nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mMessageFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = folder->GetTotalMessages(false, &totalMessages); + NS_ENSURE_SUCCESS(rv, rv); + + *aResult = totalMessages > 0; + return NS_OK; +} + +// +// To really finalize this capability, we need to have the ability to get +// the message from the mail store in a stream for processing. The flow +// would be something like this: +// +// foreach (message in Outbox folder) +// get stream of Nth message +// if (not done with headers) +// Tack on to current buffer of headers +// when done with headers +// BuildHeaders() +// Write Headers to Temp File +// after done with headers +// write rest of message body to temp file +// +// when done with the message +// do send operation +// +// when send is complete +// Copy from Outbox to FCC folder +// Delete from Outbox folder +// +// +NS_IMETHODIMP +nsMsgSendLater::SendUnsentMessages(nsIMsgIdentity* aIdentity) { + return InternalSendMessages(true, aIdentity); +} + +// Returns NS_OK if the db is OK, an error otherwise, e.g., we had to reparse. +nsresult nsMsgSendLater::ReparseDBIfNeeded(nsIUrlListener* aListener) { + // This will kick off a reparse, if needed. So the next time we check if + // there are unsent messages, the db will be up to date. + nsCOMPtr<nsIMsgDatabase> unsentDB; + nsresult rv; + nsCOMPtr<nsIMsgLocalMailFolder> locFolder( + do_QueryReferent(mMessageFolder, &rv)); + NS_ENSURE_SUCCESS(rv, rv); + return locFolder->GetDatabaseWithReparse(aListener, nullptr, + getter_AddRefs(unsentDB)); +} + +nsresult nsMsgSendLater::InternalSendMessages(bool aUserInitiated, + nsIMsgIdentity* aIdentity) { + if (WeAreOffline()) return NS_MSG_ERROR_OFFLINE; + + // Protect against being called whilst we're already sending. + if (mSendingMessages) { + NS_ERROR("nsMsgSendLater is already sending messages"); + return NS_ERROR_FAILURE; + } + + nsresult rv; + + // XXX This code should be set up for multiple unsent folders, however we + // don't support that at the moment, so for now just assume one folder. + if (!mMessageFolder) { + nsCOMPtr<nsIMsgFolder> folder; + rv = GetUnsentMessagesFolder(nullptr, getter_AddRefs(folder)); + if (NS_FAILED(rv) || !folder) return NS_ERROR_FAILURE; + } + nsCOMPtr<nsIMsgDatabase> unsentDB; + // Remember these in case we need to reparse the db. + mUserInitiated = aUserInitiated; + mIdentity = aIdentity; + rv = ReparseDBIfNeeded(this); + NS_ENSURE_SUCCESS(rv, rv); + mIdentity = nullptr; // don't hold onto the identity since we're a service. + + nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mMessageFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr<nsIMsgEnumerator> enumerator; + rv = folder->GetMessages(getter_AddRefs(enumerator)); + NS_ENSURE_SUCCESS(rv, rv); + + // Build mMessagesToSend array. + bool hasMoreElements = false; + while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) && + hasMoreElements) { + nsCOMPtr<nsIMsgDBHdr> messageHeader; + rv = enumerator->GetNext(getter_AddRefs(messageHeader)); + if (NS_SUCCEEDED(rv)) { + if (aUserInitiated) { + // If the user initiated the send, add all messages + mMessagesToSend.AppendObject(messageHeader); + } else { + // Else just send those that are NOT marked as Queued. + uint32_t flags; + rv = messageHeader->GetFlags(&flags); + if (NS_SUCCEEDED(rv) && !(flags & nsMsgMessageFlags::Queued)) + mMessagesToSend.AppendObject(messageHeader); + } + } + } + + // We're now sending messages so its time to signal that and reset our counts. + mSendingMessages = true; + mTotalSentSuccessfully = 0; + mTotalSendCount = 0; + + // Notify the listeners that we are starting a send. + NotifyListenersOnStartSending(mMessagesToSend.Count()); + + return StartNextMailFileSend(NS_OK); +} + +nsresult nsMsgSendLater::SetOrigMsgDisposition() { + if (!mMessage) return NS_ERROR_NULL_POINTER; + + // We're finished sending a queued message. We need to look at mMessage + // and see if we need to set replied/forwarded + // flags for the original message that this message might be a reply to + // or forward of. + nsCString originalMsgURIs; + nsCString queuedDisposition; + mMessage->GetStringProperty(ORIG_URI_PROPERTY, originalMsgURIs); + mMessage->GetStringProperty(QUEUED_DISPOSITION_PROPERTY, queuedDisposition); + if (!queuedDisposition.IsEmpty()) { + nsTArray<nsCString> uriArray; + ParseString(originalMsgURIs, ',', uriArray); + for (uint32_t i = 0; i < uriArray.Length(); i++) { + nsCOMPtr<nsIMsgDBHdr> msgHdr; + nsresult rv = GetMsgDBHdrFromURI(uriArray[i], getter_AddRefs(msgHdr)); + NS_ENSURE_SUCCESS(rv, rv); + if (msgHdr) { + // get the folder for the message resource + nsCOMPtr<nsIMsgFolder> msgFolder; + msgHdr->GetFolder(getter_AddRefs(msgFolder)); + if (msgFolder) { + nsMsgDispositionState dispositionSetting = + nsIMsgFolder::nsMsgDispositionState_None; + if (queuedDisposition.EqualsLiteral("replied")) + dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Replied; + else if (queuedDisposition.EqualsLiteral("forwarded")) + dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Forwarded; + else if (queuedDisposition.EqualsLiteral("redirected")) + dispositionSetting = nsIMsgFolder::nsMsgDispositionState_Redirected; + + msgFolder->AddMessageDispositionState(msgHdr, dispositionSetting); + } + } + } + } + return NS_OK; +} + +nsresult nsMsgSendLater::DeleteCurrentMessage() { + if (!mMessage) { + NS_ERROR("nsMsgSendLater: Attempt to delete an already deleted message"); + return NS_OK; + } + + // Get the composition fields interface + if (!mMessageFolder) return NS_ERROR_UNEXPECTED; + nsresult rv; + nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mMessageFolder, &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = folder->DeleteMessages({&*mMessage}, nullptr, true, false, nullptr, + false /*allowUndo*/); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + + // Null out the message so we don't try and delete it again. + mMessage = nullptr; + + return NS_OK; +} + +// +// This function parses the headers, and also deletes from the header block +// any headers which should not be delivered in mail, regardless of whether +// they were present in the queue file. Such headers include: BCC, FCC, +// Sender, X-Mozilla-Status, X-Mozilla-News-Host, and Content-Length. +// (Content-Length is for the disk file only, and must not be allowed to +// escape onto the network, since it depends on the local linebreak +// representation. Arguably, we could allow Lines to escape, but it's not +// required by NNTP.) +// +nsresult nsMsgSendLater::BuildHeaders() { + char* buf = m_headers; + char* buf_end = buf + m_headersFP; + + PR_FREEIF(m_to); + PR_FREEIF(m_bcc); + PR_FREEIF(m_newsgroups); + PR_FREEIF(m_newshost); + PR_FREEIF(m_fcc); + PR_FREEIF(mIdentityKey); + PR_FREEIF(mAccountKey); + m_flags = 0; + + while (buf < buf_end) { + bool prune_p = false; + bool do_flags_p = false; + char* colon = PL_strchr(buf, ':'); + char* end; + char* value = 0; + char** header = 0; + char* header_start = buf; + + if (!colon) break; + + end = colon; + while (end > buf && (*end == ' ' || *end == '\t')) end--; + + switch (buf[0]) { + case 'B': + case 'b': + if (!PL_strncasecmp("BCC", buf, end - buf)) { + header = &m_bcc; + } + break; + case 'C': + case 'c': + if (!PL_strncasecmp("CC", buf, end - buf)) + header = &m_to; + else if (!PL_strncasecmp(HEADER_CONTENT_LENGTH, buf, end - buf)) + prune_p = true; + break; + case 'F': + case 'f': + if (!PL_strncasecmp("FCC", buf, end - buf)) { + header = &m_fcc; + prune_p = true; + } + break; + case 'L': + case 'l': + if (!PL_strncasecmp("Lines", buf, end - buf)) prune_p = true; + break; + case 'N': + case 'n': + if (!PL_strncasecmp("Newsgroups", buf, end - buf)) + header = &m_newsgroups; + break; + case 'S': + case 's': + if (!PL_strncasecmp("Sender", buf, end - buf)) prune_p = true; + break; + case 'T': + case 't': + if (!PL_strncasecmp("To", buf, end - buf)) header = &m_to; + break; + case 'X': + case 'x': { + if (buf + strlen(HEADER_X_MOZILLA_STATUS2) == end && + !PL_strncasecmp(HEADER_X_MOZILLA_STATUS2, buf, end - buf)) + prune_p = true; + else if (buf + strlen(HEADER_X_MOZILLA_STATUS) == end && + !PL_strncasecmp(HEADER_X_MOZILLA_STATUS, buf, end - buf)) + prune_p = do_flags_p = true; + else if (!PL_strncasecmp(HEADER_X_MOZILLA_DRAFT_INFO, buf, end - buf)) + prune_p = true; + else if (!PL_strncasecmp(HEADER_X_MOZILLA_KEYWORDS, buf, end - buf)) + prune_p = true; + else if (!PL_strncasecmp(HEADER_X_MOZILLA_NEWSHOST, buf, end - buf)) { + prune_p = true; + header = &m_newshost; + } else if (!PL_strncasecmp(HEADER_X_MOZILLA_IDENTITY_KEY, buf, + end - buf)) { + prune_p = true; + header = &mIdentityKey; + } else if (!PL_strncasecmp(HEADER_X_MOZILLA_ACCOUNT_KEY, buf, + end - buf)) { + prune_p = true; + header = &mAccountKey; + } + break; + } + } + + buf = colon + 1; + while (*buf == ' ' || *buf == '\t') buf++; + + value = buf; + + SEARCH_NEWLINE: + while (*buf != 0 && *buf != '\r' && *buf != '\n') buf++; + + if (buf + 1 >= buf_end) + ; + // If "\r\n " or "\r\n\t" is next, that doesn't terminate the header. + else if (buf + 2 < buf_end && (buf[0] == '\r' && buf[1] == '\n') && + (buf[2] == ' ' || buf[2] == '\t')) { + buf += 3; + goto SEARCH_NEWLINE; + } + // If "\r " or "\r\t" or "\n " or "\n\t" is next, that doesn't terminate + // the header either. + else if ((buf[0] == '\r' || buf[0] == '\n') && + (buf[1] == ' ' || buf[1] == '\t')) { + buf += 2; + goto SEARCH_NEWLINE; + } + + if (header) { + int L = buf - value; + if (*header) { + char* newh = (char*)PR_Realloc((*header), PL_strlen(*header) + L + 10); + if (!newh) return NS_ERROR_OUT_OF_MEMORY; + *header = newh; + newh = (*header) + PL_strlen(*header); + *newh++ = ','; + *newh++ = ' '; + memcpy(newh, value, L); + newh[L] = 0; + } else { + *header = (char*)PR_Malloc(L + 1); + if (!*header) return NS_ERROR_OUT_OF_MEMORY; + memcpy((*header), value, L); + (*header)[L] = 0; + } + } else if (do_flags_p) { + char* s = value; + PR_ASSERT(*s != ' ' && *s != '\t'); + NS_ASSERTION(MsgIsHex(s, 4), "Expected 4 hex digits for flags."); + m_flags = MsgUnhex(s, 4); + } + + if (*buf == '\r' || *buf == '\n') { + if (*buf == '\r' && buf[1] == '\n') buf++; + buf++; + } + + if (prune_p) { + char* to = header_start; + char* from = buf; + while (from < buf_end) *to++ = *from++; + buf = header_start; + buf_end = to; + m_headersFP = buf_end - m_headers; + } + } + + m_headers[m_headersFP++] = '\r'; + m_headers[m_headersFP++] = '\n'; + + // Now we have parsed out all of the headers we need and we + // can proceed. + return NS_OK; +} + +nsresult DoGrowBuffer(int32_t desired_size, int32_t element_size, + int32_t quantum, char** buffer, int32_t* size) { + if (*size <= desired_size) { + char* new_buf; + int32_t increment = desired_size - *size; + if (increment < quantum) // always grow by a minimum of N bytes + increment = quantum; + + new_buf = + (*buffer ? (char*)PR_Realloc(*buffer, (*size + increment) * + (element_size / sizeof(char))) + : (char*)PR_Malloc((*size + increment) * + (element_size / sizeof(char)))); + if (!new_buf) return NS_ERROR_OUT_OF_MEMORY; + *buffer = new_buf; + *size += increment; + } + return NS_OK; +} + +#define do_grow_headers(desired_size) \ + (((desired_size) >= m_headersSize) \ + ? DoGrowBuffer((desired_size), sizeof(char), 1024, &m_headers, \ + &m_headersSize) \ + : NS_OK) + +nsresult nsMsgSendLater::DeliverQueuedLine(const char* line, int32_t length) { + int32_t flength = length; + + m_bytesRead += length; + + // convert existing newline to CRLF + // Don't need this because the calling routine is taking care of it. + // if (length > 0 && (line[length-1] == '\r' || + // (line[length-1] == '\n' && (length < 2 || line[length-2] != '\r')))) + // { + // line[length-1] = '\r'; + // line[length++] = '\n'; + // } + // + // + // We are going to check if we are looking at a "From - " line. If so, + // then just eat it and return NS_OK + // + if (!PL_strncasecmp(line, "From - ", 7)) return NS_OK; + + if (m_inhead) { + if (m_headersPosition == 0) { + // This line is the first line in a header block. + // Remember its position. + m_headersPosition = m_position; + + // Also, since we're now processing the headers, clear out the + // slots which we will parse data into, so that the values that + // were used the last time around do not persist. + + // We must do that here, and not in the previous clause of this + // `else' (the "I've just seen a `From ' line clause") because + // that clause happens before delivery of the previous message is + // complete, whereas this clause happens after the previous msg + // has been delivered. If we did this up there, then only the + // last message in the folder would ever be able to be both + // mailed and posted (or fcc'ed.) + PR_FREEIF(m_to); + PR_FREEIF(m_bcc); + PR_FREEIF(m_newsgroups); + PR_FREEIF(m_newshost); + PR_FREEIF(m_fcc); + PR_FREEIF(mIdentityKey); + } + + if (line[0] == '\r' || line[0] == '\n' || line[0] == 0) { + // End of headers. Now parse them; open the temp file; + // and write the appropriate subset of the headers out. + m_inhead = false; + + nsresult rv = MsgNewBufferedFileOutputStream(getter_AddRefs(mOutFile), + mTempFile, -1, 00600); + if (NS_FAILED(rv)) return NS_MSG_ERROR_WRITING_FILE; + + nsresult status = BuildHeaders(); + if (NS_FAILED(status)) return status; + + uint32_t n; + rv = mOutFile->Write(m_headers, m_headersFP, &n); + if (NS_FAILED(rv) || n != (uint32_t)m_headersFP) + return NS_MSG_ERROR_WRITING_FILE; + } else { + // Otherwise, this line belongs to a header. So append it to the + // header data. + + if (!PL_strncasecmp(line, HEADER_X_MOZILLA_STATUS, + PL_strlen(HEADER_X_MOZILLA_STATUS))) + // Notice the position of the flags. + m_flagsPosition = m_position; + else if (m_headersFP == 0) + m_flagsPosition = 0; + + nsresult status = do_grow_headers(length + m_headersFP + 10); + if (NS_FAILED(status)) return status; + + memcpy(m_headers + m_headersFP, line, length); + m_headersFP += length; + } + } else { + // This is a body line. Write it to the file. + PR_ASSERT(mOutFile); + if (mOutFile) { + uint32_t wrote; + nsresult rv = mOutFile->Write(line, length, &wrote); + if (NS_FAILED(rv) || wrote < (uint32_t)length) + return NS_MSG_ERROR_WRITING_FILE; + } + } + + m_position += flength; + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::AddListener(nsIMsgSendLaterListener* aListener) { + NS_ENSURE_ARG_POINTER(aListener); + mListenerArray.AppendElement(aListener); + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::RemoveListener(nsIMsgSendLaterListener* aListener) { + NS_ENSURE_ARG_POINTER(aListener); + return mListenerArray.RemoveElement(aListener) ? NS_OK : NS_ERROR_INVALID_ARG; +} + +NS_IMETHODIMP +nsMsgSendLater::GetSendingMessages(bool* aResult) { + NS_ENSURE_ARG_POINTER(aResult); + *aResult = mSendingMessages; + return NS_OK; +} + +#define NOTIFY_LISTENERS(propertyfunc_, params_) \ + PR_BEGIN_MACRO \ + nsTObserverArray<nsCOMPtr<nsIMsgSendLaterListener>>::ForwardIterator iter( \ + mListenerArray); \ + nsCOMPtr<nsIMsgSendLaterListener> listener; \ + while (iter.HasMore()) { \ + listener = iter.GetNext(); \ + listener->propertyfunc_ params_; \ + } \ + PR_END_MACRO + +void nsMsgSendLater::NotifyListenersOnStartSending( + uint32_t aTotalMessageCount) { + NOTIFY_LISTENERS(OnStartSending, (aTotalMessageCount)); +} + +void nsMsgSendLater::NotifyListenersOnMessageStartSending( + uint32_t aCurrentMessage, uint32_t aTotalMessage, + nsIMsgIdentity* aIdentity) { + NOTIFY_LISTENERS(OnMessageStartSending, + (aCurrentMessage, aTotalMessage, mMessage, aIdentity)); +} + +void nsMsgSendLater::NotifyListenersOnProgress(uint32_t aCurrentMessage, + uint32_t aTotalMessage, + uint32_t aSendPercent, + uint32_t aCopyPercent) { + NOTIFY_LISTENERS(OnMessageSendProgress, (aCurrentMessage, aTotalMessage, + aSendPercent, aCopyPercent)); +} + +void nsMsgSendLater::NotifyListenersOnMessageSendError(uint32_t aCurrentMessage, + nsresult aStatus, + const char16_t* aMsg) { + NOTIFY_LISTENERS(OnMessageSendError, + (aCurrentMessage, mMessage, aStatus, aMsg)); +} + +/** + * This function is called to end sending of messages, it resets the send later + * system and notifies the relevant parties that we have finished. + */ +void nsMsgSendLater::EndSendMessages(nsresult aStatus, const char16_t* aMsg, + uint32_t aTotalTried, + uint32_t aSuccessful) { + // Catch-all, we may have had an issue sending, so we may not be calling + // StartNextMailFileSend to fully finish the sending. Therefore set + // mSendingMessages to false here so that we don't think we're still trying + // to send messages + mSendingMessages = false; + + // Clear out our array of messages. + mMessagesToSend.Clear(); + + nsresult rv; + nsCOMPtr<nsIMsgFolder> folder = do_QueryReferent(mMessageFolder, &rv); + NS_ENSURE_SUCCESS(rv, ); + // We don't need to keep hold of the database now we've finished sending. + (void)folder->SetMsgDatabase(nullptr); + + // or temp file or output stream + mTempFile = nullptr; + mOutFile = nullptr; + + NOTIFY_LISTENERS(OnStopSending, (aStatus, aMsg, aTotalTried, aSuccessful)); + + // If we've got a shutdown listener, notify it that we've finished. + if (mShutdownListener) { + mShutdownListener->OnStopRunningUrl(nullptr, NS_OK); + mShutdownListener = nullptr; + } +} + +/** + * Called when the send part of sending a message is finished. This will set up + * for the next step or "end" depending on the status. + * + * @param aStatus The success or fail result of the send step. + * @return True if the copy process will continue, false otherwise. + */ +bool nsMsgSendLater::OnSendStepFinished(nsresult aStatus) { + if (NS_SUCCEEDED(aStatus)) { + SetOrigMsgDisposition(); + DeleteCurrentMessage(); + + // Send finished, so that is now 100%, copy to proceed... + NotifyListenersOnProgress(mTotalSendCount, mMessagesToSend.Count(), 100, 0); + + ++mTotalSentSuccessfully; + return true; + } else { + // XXX we don't currently get a message string from the send service. + NotifyListenersOnMessageSendError(mTotalSendCount, aStatus, nullptr); + nsresult rv = StartNextMailFileSend(aStatus); + // if this is the last message we're sending, we should report + // the status failure. + if (NS_FAILED(rv)) + EndSendMessages(rv, nullptr, mTotalSendCount, mTotalSentSuccessfully); + } + return false; +} + +/** + * Called when the copy part of sending a message is finished. This will send + * the next message or handle failure as appropriate. + * + * @param aStatus The success or fail result of the copy step. + */ +void nsMsgSendLater::OnCopyStepFinished(nsresult aStatus) { + // Regardless of the success of the copy we will still keep trying + // to send the rest... + nsresult rv = StartNextMailFileSend(aStatus); + if (NS_FAILED(rv)) + EndSendMessages(rv, nullptr, mTotalSendCount, mTotalSentSuccessfully); +} + +// XXX todo +// maybe this should just live in the account manager? +nsresult nsMsgSendLater::GetIdentityFromKey(const char* aKey, + nsIMsgIdentity** aIdentity) { + NS_ENSURE_ARG_POINTER(aIdentity); + + nsresult rv; + nsCOMPtr<nsIMsgAccountManager> accountManager = + do_GetService("@mozilla.org/messenger/account-manager;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + if (aKey) { + nsTArray<RefPtr<nsIMsgIdentity>> identities; + if (NS_SUCCEEDED(accountManager->GetAllIdentities(identities))) { + for (auto lookupIdentity : identities) { + nsCString key; + lookupIdentity->GetKey(key); + if (key.Equals(aKey)) { + lookupIdentity.forget(aIdentity); + return NS_OK; + } + } + } + } + + // If no aKey, or we failed to find the identity from the key + // use the identity from the default account. + nsCOMPtr<nsIMsgAccount> defaultAccount; + rv = accountManager->GetDefaultAccount(getter_AddRefs(defaultAccount)); + NS_ENSURE_SUCCESS(rv, rv); + + if (defaultAccount) + rv = defaultAccount->GetDefaultIdentity(aIdentity); + else + *aIdentity = nullptr; + + return rv; +} + +nsresult nsMsgSendLater::StartTimer() { + // No need to trigger if timer is already set + if (mTimerSet) return NS_OK; + + // XXX only trigger for non-queued headers + + // Items from this function return NS_OK because the callee won't care about + // the result anyway. + nsresult rv; + if (!mTimer) { + mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); + NS_ENSURE_SUCCESS(rv, NS_OK); + } + + rv = mTimer->Init(static_cast<nsIObserver*>(this), kInitialMessageSendTime, + nsITimer::TYPE_ONE_SHOT); + NS_ENSURE_SUCCESS(rv, NS_OK); + + mTimerSet = true; + + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::OnFolderAdded(nsIMsgFolder* /*parent*/, + nsIMsgFolder* /*child*/) { + return StartTimer(); +} + +NS_IMETHODIMP +nsMsgSendLater::OnMessageAdded(nsIMsgFolder* /*parent*/, nsIMsgDBHdr* /*msg*/) { + return StartTimer(); +} + +NS_IMETHODIMP +nsMsgSendLater::OnFolderRemoved(nsIMsgFolder* /*parent*/, + nsIMsgFolder* /*child*/) { + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::OnMessageRemoved(nsIMsgFolder* /*parent*/, + nsIMsgDBHdr* /*msg*/) { + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::OnFolderPropertyChanged(nsIMsgFolder* aFolder, + const nsACString& aProperty, + const nsACString& aOldValue, + const nsACString& aNewValue) { + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::OnFolderIntPropertyChanged(nsIMsgFolder* aFolder, + const nsACString& aProperty, + int64_t aOldValue, + int64_t aNewValue) { + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::OnFolderBoolPropertyChanged(nsIMsgFolder* aFolder, + const nsACString& aProperty, + bool aOldValue, bool aNewValue) { + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::OnFolderUnicharPropertyChanged(nsIMsgFolder* aFolder, + const nsACString& aProperty, + const nsAString& aOldValue, + const nsAString& aNewValue) { + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::OnFolderPropertyFlagChanged(nsIMsgDBHdr* aMsg, + const nsACString& aProperty, + uint32_t aOldValue, + uint32_t aNewValue) { + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::OnFolderEvent(nsIMsgFolder* aFolder, const nsACString& aEvent) { + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::GetNeedsToRunTask(bool* aResult) { + NS_ENSURE_ARG_POINTER(aResult); + *aResult = mSendingMessages; + return NS_OK; +} + +NS_IMETHODIMP +nsMsgSendLater::DoShutdownTask(nsIUrlListener* aListener, nsIMsgWindow* aWindow, + bool* aResult) { + if (mTimer) mTimer->Cancel(); + // If we're already sending messages, nothing to do, but save the shutdown + // listener until we've finished. + if (mSendingMessages) { + mShutdownListener = aListener; + return NS_OK; + } + // Else we have pending messages, we need to throw up a dialog to find out + // if to send them or not. + + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsMsgSendLater::GetCurrentTaskName(nsAString& aResult) { + // XXX Bug 440794 will localize this, left as non-localized whilst we decide + // on the actual strings and try out the UI. + aResult = u"Sending Messages"_ns; + return NS_OK; +} |