summaryrefslogtreecommitdiff
path: root/mailnews/compose/src/nsURLFetcher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/compose/src/nsURLFetcher.cpp')
-rw-r--r--mailnews/compose/src/nsURLFetcher.cpp526
1 files changed, 526 insertions, 0 deletions
diff --git a/mailnews/compose/src/nsURLFetcher.cpp b/mailnews/compose/src/nsURLFetcher.cpp
new file mode 100644
index 0000000000..b564ab9a4f
--- /dev/null
+++ b/mailnews/compose/src/nsURLFetcher.cpp
@@ -0,0 +1,526 @@
+/* -*- 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 "nsURLFetcher.h"
+
+#include "msgCore.h" // for pre-compiled headers
+#include "nsCOMPtr.h"
+#include "nsNullPrincipal.h"
+#include <stdio.h>
+#include "nscore.h"
+#include "nsIFactory.h"
+#include "nsISupports.h"
+#include "prmem.h"
+#include "plstr.h"
+#include "nsIComponentManager.h"
+#include "nsStringGlue.h"
+#include "nsIIOService.h"
+#include "nsIChannel.h"
+#include "nsNetUtil.h"
+#include "nsMimeTypes.h"
+#include "nsIHttpChannel.h"
+#include "nsIWebProgress.h"
+#include "nsMsgAttachmentHandler.h"
+#include "nsMsgSend.h"
+#include "nsISeekableStream.h"
+#include "nsIStreamConverterService.h"
+#include "nsIMsgProgress.h"
+#include "nsMsgUtils.h"
+
+NS_IMPL_ISUPPORTS(nsURLFetcher,
+ nsIURLFetcher,
+ nsIStreamListener,
+ nsIRequestObserver,
+ nsIURIContentListener,
+ nsIInterfaceRequestor,
+ nsIWebProgressListener,
+ nsISupportsWeakReference)
+
+
+/*
+ * Inherited methods for nsMimeConverter
+ */
+nsURLFetcher::nsURLFetcher()
+{
+ // Init member variables...
+ mTotalWritten = 0;
+ mBuffer = nullptr;
+ mBufferSize = 0;
+ mStillRunning = true;
+ mCallback = nullptr;
+ mOnStopRequestProcessed = false;
+ mIsFile=false;
+ nsURLFetcherStreamConsumer *consumer = new nsURLFetcherStreamConsumer(this);
+ mConverter = do_QueryInterface(consumer);
+}
+
+nsURLFetcher::~nsURLFetcher()
+{
+ mStillRunning = false;
+
+ PR_FREEIF(mBuffer);
+ // Remove the DocShell as a listener of the old WebProgress...
+ if (mLoadCookie)
+ {
+ nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));
+
+ if (webProgress)
+ webProgress->RemoveProgressListener(this);
+ }
+}
+
+NS_IMETHODIMP nsURLFetcher::GetInterface(const nsIID & aIID, void * *aInstancePtr)
+{
+ NS_ENSURE_ARG_POINTER(aInstancePtr);
+ return QueryInterface(aIID, aInstancePtr);
+}
+
+// nsIURIContentListener support
+NS_IMETHODIMP
+nsURLFetcher::OnStartURIOpen(nsIURI* aURI, bool* aAbortOpen)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::IsPreferred(const char * aContentType,
+ char ** aDesiredContentType,
+ bool * aCanHandleContent)
+
+{
+ return CanHandleContent(aContentType, true, aDesiredContentType,
+ aCanHandleContent);
+}
+
+NS_IMETHODIMP
+nsURLFetcher::CanHandleContent(const char * aContentType,
+ bool aIsContentPreferred,
+ char ** aDesiredContentType,
+ bool * aCanHandleContent)
+
+{
+ if (!mIsFile && PL_strcasecmp(aContentType, MESSAGE_RFC822) == 0)
+ *aDesiredContentType = strdup("text/html");
+
+ // since we explicilty loaded the url, we always want to handle it!
+ *aCanHandleContent = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::DoContent(const nsACString& aContentType,
+ bool aIsContentPreferred,
+ nsIRequest *request,
+ nsIStreamListener ** aContentHandler,
+ bool * aAbortProcess)
+{
+ nsresult rv = NS_OK;
+
+ if (aAbortProcess)
+ *aAbortProcess = false;
+ QueryInterface(NS_GET_IID(nsIStreamListener), (void **) aContentHandler);
+
+ /*
+ Check the content-type to see if we need to insert a converter
+ */
+ if (PL_strcasecmp(PromiseFlatCString(aContentType).get(), UNKNOWN_CONTENT_TYPE) == 0 ||
+ PL_strcasecmp(PromiseFlatCString(aContentType).get(), MULTIPART_MIXED_REPLACE) == 0 ||
+ PL_strcasecmp(PromiseFlatCString(aContentType).get(), MULTIPART_MIXED) == 0 ||
+ PL_strcasecmp(PromiseFlatCString(aContentType).get(), MULTIPART_BYTERANGES) == 0)
+ {
+ rv = InsertConverter(PromiseFlatCString(aContentType).get());
+ if (NS_SUCCEEDED(rv))
+ mConverterContentType = PromiseFlatCString(aContentType).get();
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::GetParentContentListener(nsIURIContentListener** aParent)
+{
+ *aParent = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::SetParentContentListener(nsIURIContentListener* aParent)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::GetLoadCookie(nsISupports ** aLoadCookie)
+{
+ *aLoadCookie = mLoadCookie;
+ NS_IF_ADDREF(*aLoadCookie);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::SetLoadCookie(nsISupports * aLoadCookie)
+{
+ // Remove the DocShell as a listener of the old WebProgress...
+ if (mLoadCookie)
+ {
+ nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));
+
+ if (webProgress)
+ webProgress->RemoveProgressListener(this);
+ }
+
+ mLoadCookie = aLoadCookie;
+
+ // Add the DocShell as a listener to the new WebProgress...
+ if (mLoadCookie)
+ {
+ nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));
+
+ if (webProgress)
+ webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_STATE_ALL);
+ }
+ return NS_OK;
+
+}
+
+nsresult
+nsURLFetcher::StillRunning(bool *running)
+{
+ *running = mStillRunning;
+ return NS_OK;
+}
+
+
+// Methods for nsIStreamListener...
+nsresult
+nsURLFetcher::OnDataAvailable(nsIRequest *request, nsISupports * ctxt, nsIInputStream *aIStream,
+ uint64_t sourceOffset, uint32_t aLength)
+{
+ /* let our converter or consumer process the data */
+ if (!mConverter)
+ return NS_ERROR_FAILURE;
+
+ return mConverter->OnDataAvailable(request, ctxt, aIStream, sourceOffset, aLength);
+}
+
+
+// Methods for nsIStreamObserver
+nsresult
+nsURLFetcher::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
+{
+ /* check if the user has canceld the operation */
+ if (mTagData)
+ {
+ nsCOMPtr<nsIMsgSend> sendPtr;
+ mTagData->GetMimeDeliveryState(getter_AddRefs(sendPtr));
+ if (sendPtr)
+ {
+ nsCOMPtr<nsIMsgProgress> progress;
+ sendPtr->GetProgress(getter_AddRefs(progress));
+ if (progress)
+ {
+ bool cancel = false;
+ progress->GetProcessCanceledByUser(&cancel);
+ if (cancel)
+ return request->Cancel(NS_ERROR_ABORT);
+ }
+ }
+ mTagData->mRequest = request;
+ }
+
+ /* call our converter or consumer */
+ if (mConverter)
+ return mConverter->OnStartRequest(request, ctxt);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::OnStopRequest(nsIRequest *request, nsISupports * ctxt, nsresult aStatus)
+{
+ // it's possible we could get in here from the channel calling us with an OnStopRequest and from our
+ // onStatusChange method (in the case of an error). So we should protect against this to make sure we
+ // don't process the on stop request twice...
+
+ if (mOnStopRequestProcessed)
+ return NS_OK;
+
+ mOnStopRequestProcessed = true;
+
+ /* first, call our converter or consumer */
+ if (mConverter)
+ (void) mConverter->OnStopRequest(request, ctxt, aStatus);
+
+ if (mTagData)
+ mTagData->mRequest = nullptr;
+
+ //
+ // Now complete the stream!
+ //
+ mStillRunning = false;
+
+ // time to close the output stream...
+ if (mOutStream)
+ {
+ mOutStream->Close();
+ mOutStream = nullptr;
+
+ /* In case of multipart/x-mixed-replace, we need to truncate the file to the current part size */
+ if (MsgLowerCaseEqualsLiteral(mConverterContentType, MULTIPART_MIXED_REPLACE))
+ {
+ mLocalFile->SetFileSize(mTotalWritten);
+ }
+ }
+
+ // Now if there is a callback, we need to call it...
+ if (mCallback)
+ mCallback (aStatus, mContentType, mCharset, mTotalWritten, nullptr, mTagData);
+
+ // Time to return...
+ return NS_OK;
+}
+
+nsresult
+nsURLFetcher::Initialize(nsIFile *localFile,
+ nsIOutputStream *outputStream,
+ nsAttachSaveCompletionCallback cb,
+ nsMsgAttachmentHandler *tagData)
+{
+ if (!outputStream || !localFile)
+ return NS_ERROR_INVALID_ARG;
+
+ mOutStream = outputStream;
+ mLocalFile = localFile;
+ mCallback = cb; //JFD: Please, no more callback, use a listener...
+ mTagData = tagData;
+ return NS_OK;
+}
+
+nsresult
+nsURLFetcher::FireURLRequest(nsIURI *aURL, nsIFile *localFile, nsIOutputStream *outputStream,
+ nsAttachSaveCompletionCallback cb, nsMsgAttachmentHandler *tagData)
+{
+ nsresult rv;
+
+ rv = Initialize(localFile, outputStream, cb, tagData);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ //check to see if aURL is a local file or not
+ aURL->SchemeIs("file", &mIsFile);
+
+ // we're about to fire a new url request so make sure the on stop request flag is cleared...
+ mOnStopRequestProcessed = false;
+
+ // let's try uri dispatching...
+ nsCOMPtr<nsIURILoader> pURILoader (do_GetService(NS_URI_LOADER_CONTRACTID));
+ NS_ENSURE_TRUE(pURILoader, NS_ERROR_FAILURE);
+
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewChannel(getter_AddRefs(channel),
+ aURL,
+ nullPrincipal,
+ nsILoadInfo::SEC_NORMAL,
+ nsIContentPolicy::TYPE_OTHER,
+ nullptr, // aLoadGroup
+ this); // aCallbacks
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return pURILoader->OpenURI(channel, false, this);
+}
+
+nsresult
+nsURLFetcher::InsertConverter(const char * aContentType)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIStreamConverterService> convServ(do_GetService("@mozilla.org/streamConverters;1", &rv));
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIStreamListener> toListener(mConverter);
+ nsCOMPtr<nsIStreamListener> fromListener;
+
+ rv = convServ->AsyncConvertData(aContentType,
+ "*/*",
+ toListener,
+ nullptr,
+ getter_AddRefs(fromListener));
+ if (NS_SUCCEEDED(rv))
+ mConverter = fromListener;
+ }
+
+ return rv;
+}
+
+// web progress listener implementation
+
+NS_IMETHODIMP
+nsURLFetcher::OnProgressChange(nsIWebProgress *aProgress, nsIRequest *aRequest,
+ int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress, int32_t aMaxTotalProgress)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::OnStateChange(nsIWebProgress *aProgress, nsIRequest *aRequest,
+ uint32_t aStateFlags, nsresult aStatus)
+{
+ // all we care about is the case where an error occurred (as in we were unable to locate the
+ // the url....
+
+ if (NS_FAILED(aStatus))
+ OnStopRequest(aRequest, nullptr, aStatus);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::OnLocationChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsIURI *aURI,
+ uint32_t aFlags)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::OnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus,
+ const char16_t* aMessage)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsURLFetcher::OnSecurityChange(nsIWebProgress *aWebProgress,
+ nsIRequest *aRequest,
+ uint32_t state)
+{
+ NS_NOTREACHED("notification excluded in AddProgressListener(...)");
+ return NS_OK;
+}
+
+
+/**
+ * Stream consumer used for handling special content type like multipart/x-mixed-replace
+ */
+
+NS_IMPL_ISUPPORTS(nsURLFetcherStreamConsumer, nsIStreamListener, nsIRequestObserver)
+
+nsURLFetcherStreamConsumer::nsURLFetcherStreamConsumer(nsURLFetcher* urlFetcher) :
+ mURLFetcher(urlFetcher)
+{
+}
+
+nsURLFetcherStreamConsumer::~nsURLFetcherStreamConsumer()
+{
+}
+
+/** nsIRequestObserver methods **/
+
+/* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
+NS_IMETHODIMP nsURLFetcherStreamConsumer::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
+{
+ if (!mURLFetcher || !mURLFetcher->mOutStream)
+ return NS_ERROR_FAILURE;
+
+ /* In case of multipart/x-mixed-replace, we need to erase the output file content */
+ if (MsgLowerCaseEqualsLiteral(mURLFetcher->mConverterContentType, MULTIPART_MIXED_REPLACE))
+ {
+ nsCOMPtr<nsISeekableStream> seekStream = do_QueryInterface(mURLFetcher->mOutStream);
+ if (seekStream)
+ seekStream->Seek(nsISeekableStream::NS_SEEK_SET, 0);
+ mURLFetcher->mTotalWritten = 0;
+ }
+
+ return NS_OK;
+}
+
+/* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status); */
+NS_IMETHODIMP nsURLFetcherStreamConsumer::OnStopRequest(nsIRequest *aRequest, nsISupports *ctxt, nsresult status)
+{
+ if (!mURLFetcher)
+ return NS_ERROR_FAILURE;
+
+ // Check the content type!
+ nsAutoCString contentType;
+ nsAutoCString charset;
+
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+ if(!channel) return NS_ERROR_FAILURE;
+
+ if (NS_SUCCEEDED(channel->GetContentType(contentType)) &&
+ !contentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE))
+ {
+ nsAutoCString uriSpec;
+ nsCOMPtr <nsIURI> channelURI;
+ channel->GetURI(getter_AddRefs(channelURI));
+ nsresult rv = channelURI->GetSpec(uriSpec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (uriSpec.Find("&realtype=message/rfc822") >= 0)
+ mURLFetcher->mContentType = MESSAGE_RFC822;
+ else
+ mURLFetcher->mContentType = contentType;
+ }
+
+ if (NS_SUCCEEDED(channel->GetContentCharset(charset)) && !charset.IsEmpty())
+ {
+ mURLFetcher->mCharset = charset;
+ }
+
+ return NS_OK;
+}
+
+/** nsIStreamListener methods **/
+
+/* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long long sourceOffset, in unsigned long count); */
+NS_IMETHODIMP nsURLFetcherStreamConsumer::OnDataAvailable(nsIRequest *aRequest, nsISupports *ctxt, nsIInputStream *inStr, uint64_t sourceOffset, uint32_t count)
+{
+ uint32_t readLen = count;
+ uint32_t wroteIt;
+
+ if (!mURLFetcher)
+ return NS_ERROR_FAILURE;
+
+ if (!mURLFetcher->mOutStream)
+ return NS_ERROR_INVALID_ARG;
+
+ if (mURLFetcher->mBufferSize < count)
+ {
+ PR_FREEIF(mURLFetcher->mBuffer);
+
+ if (count > 0x1000)
+ mURLFetcher->mBufferSize = count;
+ else
+ mURLFetcher->mBufferSize = 0x1000;
+
+ mURLFetcher->mBuffer = (char *)PR_Malloc(mURLFetcher->mBufferSize);
+ if (!mURLFetcher->mBuffer)
+ return NS_ERROR_OUT_OF_MEMORY; /* we couldn't allocate the object */
+ }
+
+ // read the data from the input stram...
+ nsresult rv = inStr->Read(mURLFetcher->mBuffer, count, &readLen);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // write to the output file...
+ mURLFetcher->mOutStream->Write(mURLFetcher->mBuffer, readLen, &wroteIt);
+
+ if (wroteIt != readLen)
+ return NS_ERROR_FAILURE;
+ else
+ {
+ mURLFetcher->mTotalWritten += wroteIt;
+ return NS_OK;
+ }
+}