michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim:set ts=4 sts=4 sw=4 et cin: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsFTPChannel.h" michael@0: #include "nsFtpConnectionThread.h" // defines nsFtpState michael@0: michael@0: #include "nsThreadUtils.h" michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #if defined(PR_LOGGING) michael@0: extern PRLogModuleInfo* gFTPLog; michael@0: #endif /* PR_LOGGING */ michael@0: michael@0: // There are two transport connections established for an michael@0: // ftp connection. One is used for the command channel , and michael@0: // the other for the data channel. The command channel is the first michael@0: // connection made and is used to negotiate the second, data, channel. michael@0: // The data channel is driven by the command channel and is either michael@0: // initiated by the server (PORT command) or by the client (PASV command). michael@0: // Client initiation is the most common case and is attempted first. michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED(nsFtpChannel, michael@0: nsBaseChannel, michael@0: nsIUploadChannel, michael@0: nsIResumableChannel, michael@0: nsIFTPChannel, michael@0: nsIProxiedChannel) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsFtpChannel::SetUploadStream(nsIInputStream *stream, michael@0: const nsACString &contentType, michael@0: int64_t contentLength) michael@0: { michael@0: NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS); michael@0: michael@0: mUploadStream = stream; michael@0: michael@0: // NOTE: contentLength is intentionally ignored here. michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFtpChannel::GetUploadStream(nsIInputStream **stream) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(stream); michael@0: *stream = mUploadStream; michael@0: NS_IF_ADDREF(*stream); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsFtpChannel::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID) michael@0: { michael@0: NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS); michael@0: mEntityID = aEntityID; michael@0: mStartPos = aStartPos; michael@0: mResumeRequested = (mStartPos || !mEntityID.IsEmpty()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFtpChannel::GetEntityID(nsACString& entityID) michael@0: { michael@0: if (mEntityID.IsEmpty()) michael@0: return NS_ERROR_NOT_RESUMABLE; michael@0: michael@0: entityID = mEntityID; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: NS_IMETHODIMP michael@0: nsFtpChannel::GetProxyInfo(nsIProxyInfo** aProxyInfo) michael@0: { michael@0: *aProxyInfo = ProxyInfo(); michael@0: NS_IF_ADDREF(*aProxyInfo); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: nsFtpChannel::OpenContentStream(bool async, nsIInputStream **result, michael@0: nsIChannel** channel) michael@0: { michael@0: if (!async) michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: michael@0: nsFtpState *state = new nsFtpState(); michael@0: if (!state) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(state); michael@0: michael@0: nsresult rv = state->Init(this); michael@0: if (NS_FAILED(rv)) { michael@0: NS_RELEASE(state); michael@0: return rv; michael@0: } michael@0: michael@0: *result = state; michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsFtpChannel::GetStatusArg(nsresult status, nsString &statusArg) michael@0: { michael@0: nsAutoCString host; michael@0: URI()->GetHost(host); michael@0: CopyUTF8toUTF16(host, statusArg); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsFtpChannel::OnCallbacksChanged() michael@0: { michael@0: mFTPEventSink = nullptr; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: namespace { michael@0: michael@0: class FTPEventSinkProxy MOZ_FINAL : public nsIFTPEventSink michael@0: { michael@0: public: michael@0: FTPEventSinkProxy(nsIFTPEventSink* aTarget) michael@0: : mTarget(aTarget) michael@0: , mTargetThread(do_GetCurrentThread()) michael@0: { } michael@0: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSIFTPEVENTSINK michael@0: michael@0: class OnFTPControlLogRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: OnFTPControlLogRunnable(nsIFTPEventSink* aTarget, michael@0: bool aServer, michael@0: const char* aMessage) michael@0: : mTarget(aTarget) michael@0: , mServer(aServer) michael@0: , mMessage(aMessage) michael@0: { } michael@0: michael@0: NS_DECL_NSIRUNNABLE michael@0: michael@0: private: michael@0: nsCOMPtr mTarget; michael@0: bool mServer; michael@0: nsCString mMessage; michael@0: }; michael@0: michael@0: private: michael@0: nsCOMPtr mTarget; michael@0: nsCOMPtr mTargetThread; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(FTPEventSinkProxy, nsIFTPEventSink) michael@0: michael@0: NS_IMETHODIMP michael@0: FTPEventSinkProxy::OnFTPControlLog(bool aServer, const char* aMsg) michael@0: { michael@0: nsRefPtr r = michael@0: new OnFTPControlLogRunnable(mTarget, aServer, aMsg); michael@0: return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: FTPEventSinkProxy::OnFTPControlLogRunnable::Run() michael@0: { michael@0: mTarget->OnFTPControlLog(mServer, mMessage.get()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: void michael@0: nsFtpChannel::GetFTPEventSink(nsCOMPtr &aResult) michael@0: { michael@0: if (!mFTPEventSink) { michael@0: nsCOMPtr ftpSink; michael@0: GetCallback(ftpSink); michael@0: if (ftpSink) { michael@0: mFTPEventSink = new FTPEventSinkProxy(ftpSink); michael@0: } michael@0: } michael@0: aResult = mFTPEventSink; michael@0: } michael@0: michael@0: void michael@0: nsFtpChannel::ForcePending(bool aForcePending) michael@0: { michael@0: // Set true here so IsPending will return true. michael@0: // Required for callback diversion from child back to parent. In such cases michael@0: // OnStopRequest can be called in the parent before callbacks are diverted michael@0: // back from the child to the listener in the parent. michael@0: mForcePending = aForcePending; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFtpChannel::IsPending(bool *result) michael@0: { michael@0: *result = Pending(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: nsFtpChannel::Pending() const michael@0: { michael@0: return nsBaseChannel::Pending() || mForcePending; michael@0: }