summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/compose/src/nsMsgSendLater.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/compose/src/nsMsgSendLater.cpp')
-rw-r--r--comm/mailnews/compose/src/nsMsgSendLater.cpp1406
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;
+}