Bug 1929610 - Implement storage support around EWS's message fetching. r=#thunderbird-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D228378
This commit is contained in:
Родитель
32f75c6e4a
Коммит
c5d6eb0c67
|
@ -1,304 +0,0 @@
|
|||
/* 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 "EwsMessageChannel.h"
|
||||
|
||||
#include "IEwsClient.h"
|
||||
#include "IEwsIncomingServer.h"
|
||||
#include "nsIMailChannel.h"
|
||||
#include "nsIMsgAccountManager.h"
|
||||
#include "nsIMsgIncomingServer.h"
|
||||
#include "nsIStreamConverterService.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIURIMutator.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS(EwsMessageChannel, nsIMailChannel, nsIChannel, nsIRequest)
|
||||
|
||||
EwsMessageChannel::EwsMessageChannel(nsIURI* uri, bool shouldConvert)
|
||||
: m_isPending(true),
|
||||
m_status(NS_OK),
|
||||
m_loadFlags(nsIRequest::LOAD_NORMAL),
|
||||
m_uri(uri) {
|
||||
if (shouldConvert) {
|
||||
m_contentType.AssignLiteral(TEXT_HTML);
|
||||
} else {
|
||||
m_contentType.AssignLiteral(MESSAGE_RFC822);
|
||||
}
|
||||
|
||||
m_charset.AssignLiteral("UTF-8");
|
||||
}
|
||||
|
||||
EwsMessageChannel::~EwsMessageChannel() = default;
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetName(nsACString& aName) {
|
||||
NS_WARNING("GetName");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::IsPending(bool* _retval) {
|
||||
*_retval = m_isPending;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetStatus(nsresult* aStatus) {
|
||||
*aStatus = m_status;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::Cancel(nsresult aStatus) {
|
||||
NS_WARNING("Cancel");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::Suspend(void) {
|
||||
NS_WARNING("Suspend");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::Resume(void) {
|
||||
NS_WARNING("Resume");
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
|
||||
NS_IF_ADDREF(*aLoadGroup = m_loadGroup);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
|
||||
m_loadGroup = aLoadGroup;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
|
||||
*aLoadFlags = m_loadFlags;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
|
||||
m_loadFlags = aLoadFlags;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetTRRMode(nsIRequest::TRRMode* _retval) {
|
||||
return GetTRRModeImpl(_retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetTRRMode(nsIRequest::TRRMode mode) {
|
||||
return SetTRRModeImpl(mode);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::CancelWithReason(nsresult aStatus,
|
||||
const nsACString& aReason) {
|
||||
return CancelWithReasonImpl(aStatus, aReason);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetCanceledReason(
|
||||
nsACString& aCanceledReason) {
|
||||
return GetCanceledReasonImpl(aCanceledReason);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetCanceledReason(
|
||||
const nsACString& aCanceledReason) {
|
||||
return SetCanceledReasonImpl(aCanceledReason);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetOriginalURI(nsIURI** aOriginalURI) {
|
||||
NS_IF_ADDREF(*aOriginalURI = m_uri);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetOriginalURI(nsIURI* aOriginalURI) {
|
||||
// There's no meaningful "original URI" for these requests.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetURI(nsIURI** aURI) {
|
||||
NS_IF_ADDREF(*aURI = m_uri);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetOwner(nsISupports** aOwner) {
|
||||
NS_IF_ADDREF(*aOwner = m_owner);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetOwner(nsISupports* aOwner) {
|
||||
m_owner = aOwner;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetNotificationCallbacks(
|
||||
nsIInterfaceRequestor** aNotificationCallbacks) {
|
||||
NS_IF_ADDREF(*aNotificationCallbacks = m_notificationCallbacks);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetNotificationCallbacks(
|
||||
nsIInterfaceRequestor* aNotificationCallbacks) {
|
||||
m_notificationCallbacks = aNotificationCallbacks;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetSecurityInfo(
|
||||
nsITransportSecurityInfo** aSecurityInfo) {
|
||||
NS_WARNING("GetSecurityInfo");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetContentType(nsACString& aContentType) {
|
||||
aContentType.Assign(m_contentType);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetContentType(
|
||||
const nsACString& aContentType) {
|
||||
nsresult rv =
|
||||
NS_ParseResponseContentType(aContentType, m_contentType, m_charset);
|
||||
|
||||
if (NS_FAILED(rv) || m_contentType.IsEmpty()) {
|
||||
m_contentType.AssignLiteral(MESSAGE_RFC822);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) || m_charset.IsEmpty()) {
|
||||
m_charset.AssignLiteral("UTF-8");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetContentCharset(
|
||||
nsACString& aContentCharset) {
|
||||
aContentCharset.Assign(m_charset);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetContentCharset(
|
||||
const nsACString& aContentCharset) {
|
||||
m_charset.Assign(aContentCharset);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetContentLength(int64_t* aContentLength) {
|
||||
NS_WARNING("GetContentLength");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetContentLength(int64_t aContentLength) {
|
||||
NS_WARNING("SetContentLength");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::Open(nsIInputStream** _retval) {
|
||||
return NS_ImplementChannelOpen(this, _retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::AsyncOpen(nsIStreamListener* aListener) {
|
||||
nsAutoCString path;
|
||||
nsresult rv = m_uri->GetFilePath(path);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Trim the leading '/' to get the EWS ID alone.
|
||||
auto ewsId = Substring(path, 1);
|
||||
|
||||
nsCOMPtr<nsIMsgAccountManager> accountManager =
|
||||
do_GetService("@mozilla.org/messenger/account-manager;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// `FindServerByURI()` expects that the URI passed in has a scheme matching
|
||||
// the value returned by an incoming server's `GetType()` method. In our case,
|
||||
// that should be `ews`.
|
||||
nsCOMPtr<nsIURI> serverUri;
|
||||
rv = NS_MutateURI(m_uri).SetScheme("ews"_ns).SetPathQueryRef(ewsId).Finalize(
|
||||
serverUri);
|
||||
|
||||
nsCOMPtr<nsIMsgIncomingServer> server;
|
||||
rv = accountManager->FindServerByURI(serverUri, getter_AddRefs(server));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<IEwsIncomingServer> ewsServer = do_QueryInterface(server, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
RefPtr<IEwsClient> client;
|
||||
rv = ewsServer->GetEwsClient(getter_AddRefs(client));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// If the consumer requests HTML, we want to render the message (from its raw
|
||||
// Internet Message Format text) for display. Otherwise, we will return the
|
||||
// raw Internet Message Format (RFC 822/2822/5322).
|
||||
RefPtr<nsIStreamListener> listenerToUse = aListener;
|
||||
if (m_contentType.Equals(TEXT_HTML)) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIStreamConverterService> converter =
|
||||
do_GetService("@mozilla.org/streamConverters;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Wrap the consumer-provided listener in a stream converter and use the
|
||||
// listener it creates for further operations.
|
||||
rv = converter->AsyncConvertData(MESSAGE_RFC822, ANY_WILDCARD, aListener,
|
||||
static_cast<nsIChannel*>(this),
|
||||
getter_AddRefs(listenerToUse));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
m_status = client->GetMessage(ewsId, this, listenerToUse);
|
||||
NS_ENSURE_SUCCESS(m_status, m_status);
|
||||
|
||||
m_isPending = false;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetCanceled(bool* aCanceled) {
|
||||
NS_WARNING("GetCanceled");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetContentDisposition(
|
||||
uint32_t* aContentDisposition) {
|
||||
*aContentDisposition = nsIChannel::DISPOSITION_INLINE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetContentDisposition(
|
||||
uint32_t aContentDisposition) {
|
||||
NS_WARNING("SetContentDisposition");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetContentDispositionFilename(
|
||||
nsAString& aContentDispositionFilename) {
|
||||
NS_WARNING("GetContentDispositionFilename");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetContentDispositionFilename(
|
||||
const nsAString& aContentDispositionFilename) {
|
||||
NS_WARNING("SetContentDispositionFilename");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetContentDispositionHeader(
|
||||
nsACString& aContentDispositionHeader) {
|
||||
NS_WARNING("GetContentDispositionHeader");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
|
||||
NS_IF_ADDREF(*aLoadInfo = m_loadInfo);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
|
||||
m_loadInfo = aLoadInfo;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsMessageChannel::GetIsDocument(bool* aIsDocument) {
|
||||
NS_WARNING("GetIsDocument");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __COMM_MAILNEWS_PROTOCOLS_EWS_MESSAGE_CHANNEL_H
|
||||
#define __COMM_MAILNEWS_PROTOCOLS_EWS_MESSAGE_CHANNEL_H
|
||||
|
||||
#include "nsIChannel.h"
|
||||
#include "nsMailChannel.h"
|
||||
|
||||
// A channel for loading email messages from Exchange Web Services.
|
||||
//
|
||||
// URIs are expected to be of the form `{scheme}://{host_string}/{message_id}`,
|
||||
// where `host_string` matches the host string of the associated account and
|
||||
// `message_id` is the EWS identifier for the requested message.
|
||||
//
|
||||
// If `text/html` is the requested content type, the channel will return the
|
||||
// message as a rendered HTML document. Otherwise, it will return the message in
|
||||
// its raw Internet Message Format form.
|
||||
class EwsMessageChannel : public nsMailChannel, public nsIChannel {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUEST
|
||||
NS_DECL_NSICHANNEL
|
||||
|
||||
// Constructs a new message channel.
|
||||
//
|
||||
// If `shouldConvert` is `true`, the content type of the channel will be set
|
||||
// to `text/html` and the channel will return a rendered HTML document fit for
|
||||
// display.
|
||||
explicit EwsMessageChannel(nsIURI* uri, bool shouldConvert = true);
|
||||
|
||||
protected:
|
||||
virtual ~EwsMessageChannel();
|
||||
|
||||
private:
|
||||
bool m_isPending;
|
||||
RefPtr<nsILoadGroup> m_loadGroup;
|
||||
nsresult m_status;
|
||||
nsLoadFlags m_loadFlags;
|
||||
RefPtr<nsIInterfaceRequestor> m_notificationCallbacks;
|
||||
RefPtr<nsIURI> m_uri;
|
||||
RefPtr<nsISupports> m_owner;
|
||||
nsCString m_contentType;
|
||||
nsCString m_charset;
|
||||
RefPtr<nsILoadInfo> m_loadInfo;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,392 @@
|
|||
/* 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 "EwsOfflineMessageChannel.h"
|
||||
|
||||
#include "IEwsClient.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIInputStreamPump.h"
|
||||
#include "nsIMailChannel.h"
|
||||
#include "nsIMsgFolder.h"
|
||||
#include "nsIMsgHdr.h"
|
||||
#include "nsIMsgIncomingServer.h"
|
||||
#include "nsIMsgMessageService.h"
|
||||
#include "nsIStreamConverterService.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIURIMutator.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
/**
|
||||
* A stream listener that proxies method calls to another stream listener, while
|
||||
* substituting the request argument with the provided channel.
|
||||
*
|
||||
* `EwsOfflineMessageChannel` can be called from an `nsIDocShell` to render the
|
||||
* message. The stream listener that `nsIDocShell` calls `AsyncOpen` with
|
||||
* expects the request used in method calls to be channel-like (i.e. it can be
|
||||
* QI'd as an `nsIChannel`). Additionally, we want to use `nsIInputStreamPump`
|
||||
* to pump the data from the message content's input stream (which we get from
|
||||
* the message store) into the provided stream listener. However, the default
|
||||
* `nsIInputStreamPump` implementation calls the stream listener methods with
|
||||
* itself as the request argument, but only implements `nsIRequest` (and not
|
||||
* `nsIChannel`), causing the operation to fail.
|
||||
*
|
||||
* Therefore we need this "proxy" listener to forward the method calls to the
|
||||
* listener `AsyncOpen` is originally provided with, while subsituting the
|
||||
* request arguments with an actual channel.
|
||||
*/
|
||||
class EwsDisplayProxyListener : public nsIStreamListener {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
|
||||
EwsDisplayProxyListener(nsIStreamListener* destination, nsIChannel* channel)
|
||||
: mDestination(destination), mChannel(channel) {};
|
||||
|
||||
protected:
|
||||
virtual ~EwsDisplayProxyListener();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIStreamListener> mDestination;
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(EwsDisplayProxyListener, nsIStreamListener)
|
||||
|
||||
EwsDisplayProxyListener::~EwsDisplayProxyListener() = default;
|
||||
|
||||
NS_IMETHODIMP EwsDisplayProxyListener::OnStartRequest(nsIRequest* request) {
|
||||
return mDestination->OnStartRequest(mChannel);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsDisplayProxyListener::OnStopRequest(nsIRequest* request,
|
||||
nsresult aStatus) {
|
||||
return mDestination->OnStopRequest(mChannel, aStatus);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsDisplayProxyListener::OnDataAvailable(
|
||||
nsIRequest* request, nsIInputStream* aInStream, uint64_t aSourceOffset,
|
||||
uint32_t aCount) {
|
||||
return mDestination->OnDataAvailable(mChannel, aInStream, aSourceOffset,
|
||||
aCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* nsIChannel/nsIRequest impl for EwsOfflineMessageChannel
|
||||
*/
|
||||
|
||||
NS_IMPL_ISUPPORTS(EwsOfflineMessageChannel, nsIMailChannel, nsIChannel,
|
||||
nsIRequest)
|
||||
|
||||
EwsOfflineMessageChannel::EwsOfflineMessageChannel(nsIURI* uri)
|
||||
: mURI(uri), mPump(nullptr), mLoadFlags(nsIRequest::LOAD_NORMAL) {
|
||||
mContentType.AssignLiteral(MESSAGE_RFC822);
|
||||
mCharset.AssignLiteral("UTF-8");
|
||||
}
|
||||
|
||||
EwsOfflineMessageChannel::~EwsOfflineMessageChannel() = default;
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetName(nsACString& aName) {
|
||||
if (mURI) {
|
||||
return mURI->GetSpec(aName);
|
||||
}
|
||||
aName.Truncate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::IsPending(bool* aPending) {
|
||||
if (mPump) {
|
||||
*aPending = false;
|
||||
} else {
|
||||
*aPending = true;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetStatus(nsresult* aStatus) {
|
||||
if (!mPump) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return mPump->GetStatus(aStatus);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::Cancel(nsresult aStatus) {
|
||||
if (!mPump) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return mPump->Cancel(aStatus);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::Suspend(void) {
|
||||
if (!mPump) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return mPump->Suspend();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::Resume(void) {
|
||||
if (!mPump) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return mPump->Resume();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetLoadGroup(
|
||||
nsILoadGroup** aLoadGroup) {
|
||||
NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
|
||||
mLoadGroup = aLoadGroup;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
|
||||
*aLoadFlags = mLoadFlags;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
|
||||
mLoadFlags = aLoadFlags;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetTRRMode(nsIRequest::TRRMode* mode) {
|
||||
return GetTRRModeImpl(mode);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetTRRMode(nsIRequest::TRRMode mode) {
|
||||
return SetTRRModeImpl(mode);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::CancelWithReason(
|
||||
nsresult aStatus, const nsACString& aReason) {
|
||||
if (!mPump) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return mPump->CancelWithReason(aStatus, aReason);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetCanceledReason(
|
||||
nsACString& aCanceledReason) {
|
||||
if (!mPump) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return mPump->GetCanceledReason(aCanceledReason);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetCanceledReason(
|
||||
const nsACString& aCanceledReason) {
|
||||
if (!mPump) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
return mPump->SetCanceledReason(aCanceledReason);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetOriginalURI(nsIURI** aOriginalURI) {
|
||||
NS_IF_ADDREF(*aOriginalURI = mURI);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetOriginalURI(nsIURI* aOriginalURI) {
|
||||
// There's no meaningful "original URI" for these requests.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetURI(nsIURI** aURI) {
|
||||
NS_IF_ADDREF(*aURI = mURI);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetOwner(nsISupports** aOwner) {
|
||||
NS_IF_ADDREF(*aOwner = mOwner);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetOwner(nsISupports* aOwner) {
|
||||
mOwner = aOwner;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetNotificationCallbacks(
|
||||
nsIInterfaceRequestor** aNotificationCallbacks) {
|
||||
NS_IF_ADDREF(*aNotificationCallbacks = mNotificationCallbacks);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetNotificationCallbacks(
|
||||
nsIInterfaceRequestor* aNotificationCallbacks) {
|
||||
mNotificationCallbacks = aNotificationCallbacks;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetSecurityInfo(
|
||||
nsITransportSecurityInfo** aSecurityInfo) {
|
||||
// Security info does not make sense here since we're only pulling messages
|
||||
// from storage.
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetContentType(
|
||||
nsACString& aContentType) {
|
||||
aContentType.Assign(mContentType);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetContentType(
|
||||
const nsACString& aContentType) {
|
||||
nsresult rv =
|
||||
NS_ParseResponseContentType(aContentType, mContentType, mCharset);
|
||||
|
||||
if (NS_FAILED(rv) || mContentType.IsEmpty()) {
|
||||
mContentType.AssignLiteral(MESSAGE_RFC822);
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) || mCharset.IsEmpty()) {
|
||||
mCharset.AssignLiteral("UTF-8");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetContentCharset(
|
||||
nsACString& aContentCharset) {
|
||||
aContentCharset.Assign(mCharset);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetContentCharset(
|
||||
const nsACString& aContentCharset) {
|
||||
mCharset.Assign(aContentCharset);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetContentLength(
|
||||
int64_t* aContentLength) {
|
||||
NS_WARNING("GetContentLength");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetContentLength(
|
||||
int64_t aContentLength) {
|
||||
NS_WARNING("SetContentLength");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::Open(nsIInputStream** _retval) {
|
||||
return NS_ImplementChannelOpen(this, _retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::AsyncOpen(
|
||||
nsIStreamListener* aListener) {
|
||||
// Get the header and folder matching the URI.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIMsgMessageService> msgService =
|
||||
do_GetService("@mozilla.org/messenger/messageservice;1?type=ews", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString spec;
|
||||
rv = mURI->GetSpec(spec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIMsgDBHdr> hdr;
|
||||
rv = msgService->MessageURIToMsgHdr(spec, getter_AddRefs(hdr));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIMsgFolder> folder;
|
||||
rv = hdr->GetFolder(getter_AddRefs(folder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Make sure the message exists in the offline store.
|
||||
nsMsgKey msgKey;
|
||||
rv = hdr->GetMessageKey(&msgKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool hasOffline;
|
||||
rv = folder->HasMsgOffline(msgKey, &hasOffline);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!hasOffline) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
// Stream the message from the store into the stream listener. This is also
|
||||
// where we instantiate and initialize `mPump`.
|
||||
nsCOMPtr<nsIInputStream> msgStream;
|
||||
rv = folder->GetMsgInputStream(hdr, getter_AddRefs(msgStream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = NS_NewInputStreamPump(getter_AddRefs(mPump), msgStream.forget());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We don't need to use our RFC822->HTML converter here because `nsDocShell`
|
||||
// will run it for us.
|
||||
nsCOMPtr<nsIStreamListener> listener =
|
||||
new EwsDisplayProxyListener(aListener, this);
|
||||
return mPump->AsyncRead(listener);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetCanceled(bool* aCanceled) {
|
||||
nsCString canceledReason;
|
||||
nsresult rv = mPump->GetCanceledReason(canceledReason);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
*aCanceled = canceledReason.IsEmpty();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetContentDisposition(
|
||||
uint32_t* aContentDisposition) {
|
||||
*aContentDisposition = nsIChannel::DISPOSITION_INLINE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetContentDisposition(
|
||||
uint32_t aContentDisposition) {
|
||||
NS_WARNING("SetContentDisposition");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetContentDispositionFilename(
|
||||
nsAString& aContentDispositionFilename) {
|
||||
NS_WARNING("GetContentDispositionFilename");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetContentDispositionFilename(
|
||||
const nsAString& aContentDispositionFilename) {
|
||||
NS_WARNING("SetContentDispositionFilename");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetContentDispositionHeader(
|
||||
nsACString& aContentDispositionHeader) {
|
||||
NS_WARNING("GetContentDispositionHeader");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
|
||||
NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
|
||||
mLoadInfo = aLoadInfo;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsOfflineMessageChannel::GetIsDocument(bool* aIsDocument) {
|
||||
NS_WARNING("GetIsDocument");
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef __COMM_MAILNEWS_PROTOCOLS_EWS_MESSAGE_CHANNEL_H
|
||||
#define __COMM_MAILNEWS_PROTOCOLS_EWS_MESSAGE_CHANNEL_H
|
||||
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIInputStreamPump.h"
|
||||
#include "nsMailChannel.h"
|
||||
|
||||
/**
|
||||
* A channel for streaming an EWS message out of the relevant message store.
|
||||
*
|
||||
* The message URI is expected either of these two forms:
|
||||
* * ews-message://{user}@{server}/{Path/To/Folder}#{MessageKey}
|
||||
* * x-moz-ews://{user}@{server}/{Path/To/Folder}/{MessageKey}
|
||||
*
|
||||
* Note that no specific encoding is applied to the folder path (besides the
|
||||
* standard URL path encoding).
|
||||
*
|
||||
* Translating this URI into a message header (which we can use to stream the
|
||||
* message's content from the message store) is done through calling
|
||||
* `EwsService::MessageURIToMsgHdr`.
|
||||
*
|
||||
* Design considerations on the form of "x-moz-ews" URIs: it includes the
|
||||
* message key in the path to follow RESTful semantics. The message is the
|
||||
* resource the URI is pointing to, "located" to the path preceding its key
|
||||
* (i.e. the path to the folder). An alternative form could have been setting
|
||||
* the key as a query parameter, however we seem to use query parameters to
|
||||
* control how the message is fetched and/or rendered/streamed, not which
|
||||
* message the operation is about, so it would be inconsistent with the rest of
|
||||
* the code.
|
||||
*/
|
||||
class EwsOfflineMessageChannel : public nsMailChannel, public nsIChannel {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUEST
|
||||
NS_DECL_NSICHANNEL
|
||||
|
||||
explicit EwsOfflineMessageChannel(nsIURI* uri);
|
||||
|
||||
protected:
|
||||
virtual ~EwsOfflineMessageChannel();
|
||||
|
||||
private:
|
||||
// The URI for the message which content we want to stream.
|
||||
nsCOMPtr<nsIURI> mURI;
|
||||
|
||||
// The pump we're using to stream the message content. `nsIInputStreamPump`
|
||||
// inherits from `nsIRequest`, so we use it to handle some methods (such as
|
||||
// `Cancel`, `Suspend`, `GetStatus`, etc) we need to implement as part of
|
||||
// implementing the latter interface.
|
||||
nsCOMPtr<nsIInputStreamPump> mPump;
|
||||
|
||||
// These attributes mostly exist to allow a basic implementation of most
|
||||
// `nsIChannel` methods. The content type (`mContentType`) will default to
|
||||
// "message/rfc822" and the content charset (`mCharset`) to "UTF-8".
|
||||
nsCString mContentType;
|
||||
nsCString mCharset;
|
||||
RefPtr<nsILoadGroup> mLoadGroup;
|
||||
nsLoadFlags mLoadFlags;
|
||||
nsCOMPtr<nsIInterfaceRequestor> mNotificationCallbacks;
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsCOMPtr<nsILoadInfo> mLoadInfo;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#include "EwsProtocolHandler.h"
|
||||
|
||||
#include "EwsMessageChannel.h"
|
||||
#include "EwsOfflineMessageChannel.h"
|
||||
#include "nsIMsgIncomingServer.h"
|
||||
|
||||
NS_IMPL_ISUPPORTS(EwsProtocolHandler, nsIProtocolHandler)
|
||||
|
@ -22,7 +22,7 @@ NS_IMETHODIMP EwsProtocolHandler::GetScheme(nsACString& aScheme) {
|
|||
NS_IMETHODIMP EwsProtocolHandler::NewChannel(nsIURI* aURI,
|
||||
nsILoadInfo* aLoadinfo,
|
||||
nsIChannel** _retval) {
|
||||
RefPtr<EwsMessageChannel> channel = new EwsMessageChannel(aURI);
|
||||
RefPtr<EwsOfflineMessageChannel> channel = new EwsOfflineMessageChannel(aURI);
|
||||
|
||||
nsresult rv = channel->SetLoadInfo(aLoadinfo);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
|
|
@ -4,19 +4,180 @@
|
|||
|
||||
#include "EwsService.h"
|
||||
|
||||
#include "EwsMessageChannel.h"
|
||||
#include "EwsOfflineMessageChannel.h"
|
||||
#include "IEwsIncomingServer.h"
|
||||
#include "IEwsClient.h"
|
||||
#include "nsIMsgAccountManager.h"
|
||||
#include "nsIMsgDatabase.h"
|
||||
#include "nsIMsgHdr.h"
|
||||
#include "nsIMsgFolder.h"
|
||||
#include "nsIMsgMailNewsUrl.h"
|
||||
#include "nsIMsgPluggableStore.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIURIMutator.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDocShellLoadState.h"
|
||||
#include "nsMsgMessageFlags.h"
|
||||
#include "nsMsgUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
#define ID_PROPERTY "ewsId"
|
||||
|
||||
// Displays the given URI via the given docshell.
|
||||
nsresult DisplayMessage(nsIURI* messageURI, nsIDocShell* docShell);
|
||||
|
||||
/**
|
||||
* A listener for a message download, which writes the message's content into
|
||||
* the relevant message store.
|
||||
*
|
||||
* Once the message has been downloaded and written into the store, this
|
||||
* listener will also use the provided docshell or stream listener, if any, to
|
||||
* display or stream the message's content from the store (using
|
||||
* `EwsOfflineMessageChannel`). If both a docshell and a stream listener are
|
||||
* provided, only the docshell is used.
|
||||
*/
|
||||
class MessageFetchListener : public IEWSMessageFetchCallbacks {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_IEWSMESSAGEFETCHCALLBACKS
|
||||
|
||||
MessageFetchListener(nsIURI* messageURI, nsIMsgDBHdr* hdr,
|
||||
nsIDocShell* docShell, nsIStreamListener* streamListener)
|
||||
: mMessageURI(messageURI),
|
||||
mHdr(hdr),
|
||||
mDisplayDocShell(docShell),
|
||||
mStreamListener(streamListener) {};
|
||||
|
||||
protected:
|
||||
virtual ~MessageFetchListener();
|
||||
|
||||
private:
|
||||
// The `ews-message` URI referring to the message to fetch.
|
||||
nsCOMPtr<nsIURI> mMessageURI;
|
||||
|
||||
// The header for the message to fetch.
|
||||
nsCOMPtr<nsIMsgDBHdr> mHdr;
|
||||
|
||||
// The message database for the message header, for committing the offline
|
||||
// flag and message size once the message content has been downloaded.
|
||||
nsCOMPtr<nsIMsgDatabase> mDB;
|
||||
|
||||
// The offline store in which to write the message content.
|
||||
nsCOMPtr<nsIMsgPluggableStore> mStore;
|
||||
|
||||
// The output stream in which to write the message content as it is being
|
||||
// downloaded.
|
||||
nsCOMPtr<nsIOutputStream> mStoreOutStream;
|
||||
|
||||
// The size of the message in the offline store, updated as the content is
|
||||
// being downloaded. Once the download finishes, this size is written to the
|
||||
// message header and committed to the message database.
|
||||
uint64_t mOfflineSize = 0;
|
||||
|
||||
// If provided, the message URI is loaded using this docshell (via
|
||||
// `DisplayMessage`) once the full message content has been written to the
|
||||
// message store. This triggers the docshell to stream the message content
|
||||
// (and convert it to HTML on the fly) via `EwsOfflineMessageChannel`.
|
||||
nsCOMPtr<nsIDocShell> mDisplayDocShell;
|
||||
|
||||
// If provided (and no docshell was provided), the message content is streamed
|
||||
// through this stream listener (via `EwsOfflineMessageChannel`) once the it
|
||||
// has been fully written to the message store.
|
||||
nsCOMPtr<nsIStreamListener> mStreamListener;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(MessageFetchListener, IEWSMessageFetchCallbacks)
|
||||
|
||||
MessageFetchListener::~MessageFetchListener() = default;
|
||||
|
||||
NS_IMETHODIMP MessageFetchListener::OnFetchStart() {
|
||||
// Instantiate the attributes we'll need to write the message and pass it on
|
||||
// to the right consumer.
|
||||
nsCOMPtr<nsIMsgFolder> folder;
|
||||
nsresult rv = mHdr->GetFolder(getter_AddRefs(folder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = folder->GetMsgDatabase(getter_AddRefs(mDB));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = folder->GetMsgStore(getter_AddRefs(mStore));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = folder->GetOfflineStoreOutputStream(mHdr,
|
||||
getter_AddRefs(mStoreOutStream));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP MessageFetchListener::OnDataAvailable(
|
||||
nsIInputStream* aInputStream, uint32_t aCount) {
|
||||
NS_ENSURE_ARG_POINTER(mStoreOutStream);
|
||||
|
||||
// Copy the message from the provided stream to the output stream provided by
|
||||
// the store.
|
||||
uint64_t bytesCopied;
|
||||
nsresult rv = SyncCopyStream(aInputStream, mStoreOutStream, bytesCopied);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mOfflineSize += bytesCopied;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP MessageFetchListener::OnFetchStop(nsresult status) {
|
||||
NS_ENSURE_ARG_POINTER(mStore);
|
||||
NS_ENSURE_ARG_POINTER(mStoreOutStream);
|
||||
NS_ENSURE_ARG_POINTER(mDB);
|
||||
|
||||
nsresult rv;
|
||||
if (NS_SUCCEEDED(status)) {
|
||||
rv = mStore->FinishNewMessage(mStoreOutStream, mHdr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Mark the message as downloaded in the database record and record its
|
||||
// size.
|
||||
uint32_t unused;
|
||||
rv = mHdr->OrFlags(nsMsgMessageFlags::Offline, &unused);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// In the future, we should use the size provided by the server, see
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1930127. In the meantime, we
|
||||
// update it here since some areas of the code seem to use `messageSize` as
|
||||
// the offline message size (see
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1930003).
|
||||
rv = mHdr->SetMessageSize(mOfflineSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mHdr->SetOfflineMessageSize(mOfflineSize);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Commit the changes to the folder's database.
|
||||
rv = mDB->Commit(nsMsgDBCommitType::kLargeCommit);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
// Fetch has failed, discard the new message in the store.
|
||||
rv = mStore->DiscardNewMessage(mStoreOutStream, mHdr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
if (mDisplayDocShell) {
|
||||
// If a docshell was provided, use it to display the message.
|
||||
return DisplayMessage(mMessageURI, mDisplayDocShell);
|
||||
}
|
||||
|
||||
if (mStreamListener) {
|
||||
// If a stream listener was provided, use it to stream the message from the
|
||||
// offline store.
|
||||
nsCOMPtr<nsIChannel> channel = new EwsOfflineMessageChannel(mMessageURI);
|
||||
return channel->AsyncOpen(mStreamListener);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(EwsService, nsIMsgMessageService)
|
||||
|
||||
EwsService::EwsService() = default;
|
||||
|
@ -29,15 +190,7 @@ NS_IMETHODIMP EwsService::CopyMessage(const nsACString& aSrcURI,
|
|||
nsIUrlListener* aUrlListener,
|
||||
nsIMsgWindow* aMsgWindow) {
|
||||
NS_ENSURE_ARG_POINTER(aCopyListener);
|
||||
|
||||
// The message service interface gives us URIs as strings, but we want
|
||||
// structured, queryable/transformable data.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NewURIForChannel(aSrcURI, getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
RefPtr<EwsMessageChannel> channel = new EwsMessageChannel(uri, false);
|
||||
return channel->AsyncOpen(aCopyListener);
|
||||
return GetMessageContent(aSrcURI, nullptr, aCopyListener);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsService::CopyMessages(
|
||||
|
@ -53,17 +206,8 @@ NS_IMETHODIMP EwsService::LoadMessage(const nsACString& aMessageURI,
|
|||
nsIMsgWindow* aMsgWindow,
|
||||
nsIUrlListener* aUrlListener,
|
||||
bool aAutodetectCharset) {
|
||||
// The message service interface gives us URIs as strings, but we want
|
||||
// structured, queryable/transformable data.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NewURIForChannel(aMessageURI, getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(uri);
|
||||
loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
|
||||
loadState->SetFirstParty(false);
|
||||
loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
|
||||
return aDisplayConsumer->LoadURI(loadState, false);
|
||||
NS_ENSURE_ARG_POINTER(aDisplayConsumer);
|
||||
return GetMessageContent(aMessageURI, aDisplayConsumer, nullptr);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP EwsService::SaveMessageToDisk(const nsACString& aMessageURI,
|
||||
|
@ -121,21 +265,65 @@ NS_IMETHODIMP EwsService::MessageURIToMsgHdr(const nsACString& uri,
|
|||
return MsgHdrFromUri(uriObj, _retval);
|
||||
}
|
||||
|
||||
// Retrieves the `nsIMsgDBHdr` associated with an internal `ews-message://` URI.
|
||||
nsresult EwsService::MsgHdrFromUri(nsIURI* uri, nsIMsgDBHdr** _retval) {
|
||||
//
|
||||
nsresult EwsService::MsgKeyStringFromMessageURI(nsIURI* uri,
|
||||
nsACString& msgKey) {
|
||||
// We expect the provided URI to be of the following form:
|
||||
// `ews-message://{username}@{host}/{folder_path}#{msg_key}`.
|
||||
// Note that `ews-message` is not a registered scheme and URIs which use it
|
||||
// cannot be loaded through the standard URI loading mechanisms.
|
||||
nsCString keyStr;
|
||||
nsresult rv = uri->GetRef(keyStr);
|
||||
nsresult rv = uri->GetRef(msgKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (keyStr.IsEmpty()) {
|
||||
if (msgKey.IsEmpty()) {
|
||||
NS_ERROR("message URI has no message key ref");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult EwsService::MsgKeyStringFromChannelURI(nsIURI* uri, nsACString& msgKey,
|
||||
nsACString& folderURIPath) {
|
||||
nsresult rv = uri->GetFilePath(folderURIPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Iterate over each slash-separated word.
|
||||
for (const auto& word : folderURIPath.Split('/')) {
|
||||
// The last word is our message key.
|
||||
msgKey.Assign(word);
|
||||
}
|
||||
|
||||
// Cut the message key out of the path. We want to cut the length of the key +
|
||||
// 1, to also remove the `/` character between the folder and the key.
|
||||
auto keyStartIndex = folderURIPath.Length() - msgKey.Length() - 1;
|
||||
auto keyLengthInURI = msgKey.Length() + 1;
|
||||
folderURIPath.Cut(keyStartIndex, keyLengthInURI);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult EwsService::MsgHdrFromUri(nsIURI* uri, nsIMsgDBHdr** _retval) {
|
||||
nsCString keyStr;
|
||||
nsCString folderURIPath;
|
||||
|
||||
// Extract the message key and folder path from the URI depending on its
|
||||
// scheme.
|
||||
nsCString scheme;
|
||||
nsresult rv = uri->GetScheme(scheme);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (scheme.EqualsLiteral("ews-message")) {
|
||||
rv = MsgKeyStringFromMessageURI(uri, keyStr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = uri->GetFilePath(folderURIPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else if (scheme.EqualsLiteral("x-moz-ews")) {
|
||||
rv = MsgKeyStringFromChannelURI(uri, keyStr, folderURIPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
nsMsgKey key =
|
||||
msgKeyFromInt(ParseUint64Str(PromiseFlatCString(keyStr).get()));
|
||||
|
||||
|
@ -143,10 +331,16 @@ nsresult EwsService::MsgHdrFromUri(nsIURI* uri, nsIMsgDBHdr** _retval) {
|
|||
// must match one it has in its database. Folders are created with an `ews`
|
||||
// scheme, and we need to remove the message key from the ref.
|
||||
RefPtr<nsIURI> folderUri;
|
||||
rv = NS_MutateURI(uri).SetScheme("ews"_ns).SetRef(""_ns).Finalize(
|
||||
getter_AddRefs(folderUri));
|
||||
rv = NS_MutateURI(uri)
|
||||
.SetScheme("ews"_ns)
|
||||
.SetFilePath(folderURIPath)
|
||||
.SetQuery(""_ns)
|
||||
.SetRef(""_ns)
|
||||
.Finalize(getter_AddRefs(folderUri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Look up the folder at this URI and use it to retrieve the rgith message
|
||||
// header.
|
||||
nsCString folderSpec;
|
||||
rv = folderUri->GetSpec(folderSpec);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -158,38 +352,126 @@ nsresult EwsService::MsgHdrFromUri(nsIURI* uri, nsIMsgDBHdr** _retval) {
|
|||
return folder->GetMessageHeader(key, _retval);
|
||||
}
|
||||
|
||||
// Creates a URI that can be used to retrieve a message via an
|
||||
// `EwsMessageChannel` from the provided spec string.
|
||||
//
|
||||
// The URI spec is assumed to always use the `ews-message` scheme.
|
||||
nsresult EwsService::NewURIForChannel(const nsACString& spec,
|
||||
nsIURI** channelUri) {
|
||||
nsCOMPtr<nsIURI> hdrUri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(hdrUri), spec);
|
||||
nsresult EwsService::GetMessageContent(const nsACString& messageURI,
|
||||
nsIDocShell* displayDocShell,
|
||||
nsIStreamListener* streamListener) {
|
||||
// Get the matching nsIMsgDBHdr for the message URI.
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), messageURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIMsgDBHdr> hdr;
|
||||
rv = MsgHdrFromUri(hdrUri, getter_AddRefs(hdr));
|
||||
rv = MsgHdrFromUri(uri, getter_AddRefs(hdr));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString ewsId;
|
||||
rv = hdr->GetStringProperty(ID_PROPERTY, ewsId);
|
||||
// Check if the folder the message is in has a local ("offline") copy of the
|
||||
// message's content.
|
||||
nsCOMPtr<nsIMsgFolder> folder;
|
||||
rv = hdr->GetFolder(getter_AddRefs(folder));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (ewsId.IsEmpty()) {
|
||||
NS_ERROR(nsPrintfCString("message %s in EWS folder has no EWS ID",
|
||||
nsPromiseFlatCString(spec).get())
|
||||
.get());
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
nsMsgKey msgKey;
|
||||
rv = hdr->GetMessageKey(&msgKey);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool offlineAvailable;
|
||||
rv = folder->HasMsgOffline(msgKey, &offlineAvailable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (offlineAvailable) {
|
||||
// If the message content is available locally, serve it to the docshell or
|
||||
// stream listener if one is provided.
|
||||
if (displayDocShell) {
|
||||
return DisplayMessage(uri, displayDocShell);
|
||||
}
|
||||
|
||||
if (streamListener) {
|
||||
RefPtr<EwsOfflineMessageChannel> channel =
|
||||
new EwsOfflineMessageChannel(uri);
|
||||
return channel->AsyncOpen(streamListener);
|
||||
}
|
||||
} else {
|
||||
// Otherwise, download the message from the server.
|
||||
return DownloadMessage(uri, hdr, displayDocShell, streamListener);
|
||||
}
|
||||
|
||||
// We want to provide the channel with a URI containing sufficient information
|
||||
// to identify the associated incoming mail account in addition to the message
|
||||
// ID. We expect that the URI passed to this method is an `ews-message://` URI
|
||||
// with username, hostname, and any distinguishing port, so we retain that
|
||||
// data in producing a loadable URI.
|
||||
return NS_MutateURI(hdrUri)
|
||||
.SetScheme("x-moz-ews"_ns)
|
||||
.SetPathQueryRef(ewsId)
|
||||
.Finalize(channelUri);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult EwsService::DownloadMessage(nsIURI* messageURI, nsIMsgDBHdr* hdr,
|
||||
nsIDocShell* displayDocShell,
|
||||
nsIStreamListener* streamListener) {
|
||||
// Retrieve the EWS ID of the message we want to download.
|
||||
nsCString ewsId;
|
||||
nsresult rv = hdr->GetStringProperty(ID_PROPERTY, ewsId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Look up the incoming server for this message, from which we can get an EWS
|
||||
// client.
|
||||
nsCOMPtr<nsIMsgAccountManager> accountManager =
|
||||
do_GetService("@mozilla.org/messenger/account-manager;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// `FindServerByURI()` expects that the URI passed in has a scheme matching
|
||||
// the value returned by an incoming server's `GetType()` method. In our case,
|
||||
// that should be `ews`.
|
||||
nsCOMPtr<nsIURI> serverUri;
|
||||
rv = NS_MutateURI(messageURI).SetScheme("ews"_ns).Finalize(serverUri);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIMsgIncomingServer> server;
|
||||
rv = accountManager->FindServerByURI(serverUri, getter_AddRefs(server));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Get an EWS client from the incoming server, and start downloading the
|
||||
// message content.
|
||||
nsCOMPtr<IEwsIncomingServer> ewsServer = do_QueryInterface(server, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<IEwsClient> client;
|
||||
rv = ewsServer->GetEwsClient(getter_AddRefs(client));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
RefPtr<MessageFetchListener> listener = new MessageFetchListener(
|
||||
messageURI, hdr, displayDocShell, streamListener);
|
||||
return client->GetMessage(ewsId, listener);
|
||||
}
|
||||
|
||||
nsresult DisplayMessage(nsIURI* messageURI, nsIDocShell* docShell) {
|
||||
NS_ENSURE_ARG_POINTER(messageURI);
|
||||
NS_ENSURE_ARG_POINTER(docShell);
|
||||
|
||||
// At this point, the path to the message URI is expected to look like
|
||||
// /Path/To/Folder#MessageKey. With this format, if the user switches between
|
||||
// messages in the same folder, the docshell believes we're still in the same
|
||||
// document (because only the fragment/ref changed), and skip creating a
|
||||
// channel for any message except the first one. So we need to transform the
|
||||
// path into /Path/To/Folder/MessageKey.
|
||||
nsCString ref;
|
||||
nsresult rv = messageURI->GetRef(ref);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCString path;
|
||||
rv = messageURI->GetFilePath(path);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
path.Append("/");
|
||||
path.Append(ref);
|
||||
|
||||
// "x-moz-ews" is the scheme we use for URIs that must be used for channels
|
||||
// opened via a protocol handler consumer (such as a docshell or the I/O
|
||||
// service). These channels are expected to serve the raw content RFC822
|
||||
// content of the message referred to by the URI.
|
||||
nsCOMPtr<nsIURI> channelURI;
|
||||
rv = NS_MutateURI(messageURI)
|
||||
.SetScheme("x-moz-ews"_ns)
|
||||
.SetPathQueryRef(path)
|
||||
.Finalize(channelURI);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Load the message through the provided docshell.
|
||||
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(channelURI);
|
||||
loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE);
|
||||
loadState->SetFirstParty(false);
|
||||
loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
|
||||
return docShell->LoadURI(loadState, false);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#define __COMM_MAILNEWS_PROTOCOLS_EWS_SERVICE_H
|
||||
|
||||
#include "nsIMsgMessageService.h"
|
||||
#include "nsIMsgHdr.h"
|
||||
|
||||
class EwsService : public nsIMsgMessageService {
|
||||
public:
|
||||
|
@ -18,8 +19,45 @@ class EwsService : public nsIMsgMessageService {
|
|||
virtual ~EwsService();
|
||||
|
||||
private:
|
||||
// Extracts the message key as a string from a message URI. Message URIs are
|
||||
// expected in the form:
|
||||
// ews-message://{user}@{server}/{Path/To/Folder}#{MessageKey}
|
||||
nsresult MsgKeyStringFromMessageURI(nsIURI* uri, nsACString& msgKey);
|
||||
|
||||
// Extracts the message key as a string from a URI used by an EWS message
|
||||
// channel. Such URIs are expected in the form:
|
||||
// x-moz-ews://{user}@{server}/{Path/To/Folder}/{MessageKey}
|
||||
// This method also returns the URI path to the folder, i.e. the path from the
|
||||
// original URI without the message key.
|
||||
nsresult MsgKeyStringFromChannelURI(nsIURI* uri, nsACString& msgKey,
|
||||
nsACString& folderURIPath);
|
||||
|
||||
// Retrieves the message header matching the provided URI.
|
||||
//
|
||||
// The URI is expected to be either a message URI or one used by an EWS
|
||||
// message channel, see the documentation for `MsgKeyStringFromMessageURI` and
|
||||
// `MsgKeyStringFromChannelURI` respectively for the expected form of each
|
||||
// supported URI.
|
||||
nsresult MsgHdrFromUri(nsIURI* uri, nsIMsgDBHdr** _retval);
|
||||
nsresult NewURIForChannel(const nsACString& spec, nsIURI** channelUri);
|
||||
|
||||
// Retrieves the content of the message referenced by the provided message
|
||||
// URI. If the message content does not already exist in the offline store, it
|
||||
// is downloaded, stored, and then served.
|
||||
//
|
||||
// If `displayDocShell` is not null, then it is used to render the message.
|
||||
// Otherwise, if `streamListener` is not null, the message content is streamed
|
||||
// to it.
|
||||
nsresult GetMessageContent(const nsACString& messageURI,
|
||||
nsIDocShell* displayDocShell,
|
||||
nsIStreamListener* streamListener);
|
||||
|
||||
// Downloads the content of the message referenced by the given message URI.
|
||||
// Once the message content has been downloaded, it is stored to the relevant
|
||||
// offline store, and passed onto the provided docshell or stream listener
|
||||
// similarly to `GetMessageContent`.
|
||||
nsresult DownloadMessage(nsIURI* messageURI, nsIMsgDBHdr* hdr,
|
||||
nsIDocShell* displayDocShell,
|
||||
nsIStreamListener* streamListener);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -17,6 +17,7 @@ interface nsIMsgCopyServiceListener;
|
|||
interface IEwsFolderCallbacks;
|
||||
interface IEwsMessageCallbacks;
|
||||
interface IEwsMessageDeleteCallbacks;
|
||||
interface IEWSMessageFetchCallbacks;
|
||||
|
||||
[uuid(4a117361-653b-48a5-9ddb-588482ef9dbb)]
|
||||
interface IEwsClient : nsISupports
|
||||
|
@ -30,7 +31,7 @@ interface IEwsClient : nsISupports
|
|||
|
||||
void syncFolderHierarchy(in IEwsFolderCallbacks callbacks, in AUTF8String syncStateToken);
|
||||
void syncMessagesForFolder(in IEwsMessageCallbacks callbacks, in AUTF8String folderId, in AUTF8String syncStateToken);
|
||||
void getMessage(in AUTF8String id, in nsIRequest request, in nsIStreamListener listener);
|
||||
void getMessage(in AUTF8String id, in IEWSMessageFetchCallbacks callbacks);
|
||||
void changeReadStatus(in Array<AUTF8String> messageIds, in boolean readStatus);
|
||||
|
||||
/**
|
||||
|
@ -84,3 +85,19 @@ interface IEwsMessageDeleteCallbacks : nsISupports
|
|||
void onRemoteDeleteSuccessful();
|
||||
void onError(in IEwsClient_Error err, in AUTF8String desc);
|
||||
};
|
||||
|
||||
/**
|
||||
* A listener used when downloading message content.
|
||||
*
|
||||
* Its shape is loosely based on `nsIStreamListener`, which cannot be used in
|
||||
* this instance because we don't always have a request/channel that can be used
|
||||
* in method calls when fetching a message's content (and using `nullptr`
|
||||
* everywhere is quite ugly and potentially unsafe).
|
||||
*/
|
||||
[uuid(027150b1-d127-41a9-8945-18f9374755b3)]
|
||||
interface IEWSMessageFetchCallbacks : nsISupports
|
||||
{
|
||||
void onFetchStart();
|
||||
void onDataAvailable(in nsIInputStream aInputStream, in unsigned long aCount);
|
||||
void onFetchStop(in nsresult status);
|
||||
};
|
||||
|
|
|
@ -25,7 +25,10 @@ Classes = [
|
|||
},
|
||||
{
|
||||
"cid": "{c3d44b68-11f1-421f-8f4b-706df13df64a}",
|
||||
"contract_ids": ["@mozilla.org/messenger/messageservice;1?type=ews-message"],
|
||||
"contract_ids": [
|
||||
"@mozilla.org/messenger/messageservice;1?type=ews-message",
|
||||
"@mozilla.org/messenger/messageservice;1?type=ews",
|
||||
],
|
||||
"type": "EwsService",
|
||||
"headers": ["/comm/mailnews/protocols/ews/src/EwsService.h"],
|
||||
},
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
SOURCES += [
|
||||
"EwsFolder.cpp",
|
||||
"EwsIncomingServer.cpp",
|
||||
"EwsMessageChannel.cpp",
|
||||
"EwsOfflineMessageChannel.cpp",
|
||||
"EwsProtocolHandler.cpp",
|
||||
"EwsProtocolInfo.cpp",
|
||||
"EwsService.cpp",
|
||||
|
|
|
@ -35,9 +35,9 @@ use uuid::Uuid;
|
|||
use xpcom::{
|
||||
getter_addrefs,
|
||||
interfaces::{
|
||||
nsIMsgCopyServiceListener, nsIMsgDBHdr, nsIMsgOutgoingListener, nsIRequest,
|
||||
nsIStreamListener, nsIStringInputStream, nsIURI, nsMsgFolderFlagType, nsMsgFolderFlags,
|
||||
nsMsgKey, nsMsgMessageFlags, IEwsClient, IEwsFolderCallbacks, IEwsMessageCallbacks,
|
||||
nsIMsgCopyServiceListener, nsIMsgDBHdr, nsIMsgOutgoingListener, nsIStringInputStream,
|
||||
nsIURI, nsMsgFolderFlagType, nsMsgFolderFlags, nsMsgKey, nsMsgMessageFlags,
|
||||
IEWSMessageFetchCallbacks, IEwsClient, IEwsFolderCallbacks, IEwsMessageCallbacks,
|
||||
IEwsMessageDeleteCallbacks,
|
||||
},
|
||||
RefPtr,
|
||||
|
@ -422,16 +422,13 @@ impl XpComEwsClient {
|
|||
pub(crate) async fn get_message(
|
||||
self,
|
||||
id: String,
|
||||
request: RefPtr<nsIRequest>,
|
||||
listener: RefPtr<nsIStreamListener>,
|
||||
callbacks: RefPtr<IEWSMessageFetchCallbacks>,
|
||||
) {
|
||||
unsafe { listener.OnStartRequest(&*request) };
|
||||
unsafe { callbacks.OnFetchStart() };
|
||||
|
||||
// Call an inner function to perform the operation in order to allow us
|
||||
// to handle errors while letting the inner function simply propagate.
|
||||
let result = self
|
||||
.get_message_inner(id.clone(), &request, &listener)
|
||||
.await;
|
||||
let result = self.get_message_inner(id.clone(), &callbacks).await;
|
||||
|
||||
let status = match result {
|
||||
Ok(_) => nserror::NS_OK,
|
||||
|
@ -442,14 +439,13 @@ impl XpComEwsClient {
|
|||
}
|
||||
};
|
||||
|
||||
unsafe { listener.OnStopRequest(&*request, status) };
|
||||
unsafe { callbacks.OnFetchStop(status) };
|
||||
}
|
||||
|
||||
async fn get_message_inner(
|
||||
self,
|
||||
id: String,
|
||||
request: &nsIRequest,
|
||||
listener: &nsIStreamListener,
|
||||
callbacks: &IEWSMessageFetchCallbacks,
|
||||
) -> Result<(), XpComEwsError> {
|
||||
let items = self.get_items([id], &[], true).await?;
|
||||
if items.len() != 1 {
|
||||
|
@ -505,8 +501,7 @@ impl XpComEwsClient {
|
|||
// before the stream is dropped.
|
||||
unsafe { stream.SetData(mime_content.as_ptr() as *const c_char, len) }.to_result()?;
|
||||
|
||||
unsafe { listener.OnDataAvailable(&*request, &*stream.coerce(), 0, len as u32) }
|
||||
.to_result()?;
|
||||
unsafe { callbacks.OnDataAvailable(&*stream.coerce(), len as u32) }.to_result()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ use thin_vec::ThinVec;
|
|||
use url::Url;
|
||||
use xpcom::{
|
||||
interfaces::{
|
||||
nsIInputStream, nsIMsgCopyServiceListener, nsIMsgIncomingServer, nsIRequest,
|
||||
nsIStreamListener, IEwsFolderCallbacks, IEwsMessageCallbacks, IEwsMessageDeleteCallbacks,
|
||||
nsIInputStream, nsIMsgCopyServiceListener, nsIMsgIncomingServer, IEWSMessageFetchCallbacks,
|
||||
IEwsFolderCallbacks, IEwsMessageCallbacks, IEwsMessageDeleteCallbacks,
|
||||
},
|
||||
nsIID, xpcom_method, RefPtr,
|
||||
};
|
||||
|
@ -162,12 +162,11 @@ impl XpcomEwsBridge {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
xpcom_method!(get_message => GetMessage(id: *const nsACString, request: *const nsIRequest, listener: *const nsIStreamListener));
|
||||
xpcom_method!(get_message => GetMessage(id: *const nsACString, callbacks: *const IEWSMessageFetchCallbacks));
|
||||
fn get_message(
|
||||
&self,
|
||||
id: &nsACString,
|
||||
request: &nsIRequest,
|
||||
listener: &nsIStreamListener,
|
||||
callbacks: &IEWSMessageFetchCallbacks,
|
||||
) -> Result<(), nsresult> {
|
||||
let client = self.try_new_client()?;
|
||||
|
||||
|
@ -175,11 +174,7 @@ impl XpcomEwsBridge {
|
|||
// this scope, so spawn it as a detached `moz_task`.
|
||||
moz_task::spawn_local(
|
||||
"get_message",
|
||||
client.get_message(
|
||||
id.to_utf8().into(),
|
||||
RefPtr::new(request),
|
||||
RefPtr::new(listener),
|
||||
),
|
||||
client.get_message(id.to_utf8().into(), RefPtr::new(callbacks)),
|
||||
)
|
||||
.detach();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче