summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/ftp
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/protocol/ftp')
-rw-r--r--netwerk/protocol/ftp/FTPChannelChild.cpp932
-rw-r--r--netwerk/protocol/ftp/FTPChannelChild.h164
-rw-r--r--netwerk/protocol/ftp/FTPChannelParent.cpp896
-rw-r--r--netwerk/protocol/ftp/FTPChannelParent.h146
-rw-r--r--netwerk/protocol/ftp/PFTPChannel.ipdl74
-rw-r--r--netwerk/protocol/ftp/doc/rfc959.txt3933
-rw-r--r--netwerk/protocol/ftp/doc/testdoc4
-rw-r--r--netwerk/protocol/ftp/ftpCore.h15
-rw-r--r--netwerk/protocol/ftp/moz.build45
-rw-r--r--netwerk/protocol/ftp/nsFTPChannel.cpp294
-rw-r--r--netwerk/protocol/ftp/nsFTPChannel.h122
-rw-r--r--netwerk/protocol/ftp/nsFtpConnectionThread.cpp2256
-rw-r--r--netwerk/protocol/ftp/nsFtpConnectionThread.h231
-rw-r--r--netwerk/protocol/ftp/nsFtpControlConnection.cpp189
-rw-r--r--netwerk/protocol/ftp/nsFtpControlConnection.h85
-rw-r--r--netwerk/protocol/ftp/nsFtpProtocolHandler.cpp426
-rw-r--r--netwerk/protocol/ftp/nsFtpProtocolHandler.h87
-rw-r--r--netwerk/protocol/ftp/nsIFTPChannel.idl29
-rw-r--r--netwerk/protocol/ftp/nsIFTPChannelParentInternal.idl15
-rw-r--r--netwerk/protocol/ftp/test/frametest/contents.html5
-rw-r--r--netwerk/protocol/ftp/test/frametest/index.html13
-rw-r--r--netwerk/protocol/ftp/test/frametest/menu.html373
22 files changed, 10334 insertions, 0 deletions
diff --git a/netwerk/protocol/ftp/FTPChannelChild.cpp b/netwerk/protocol/ftp/FTPChannelChild.cpp
new file mode 100644
index 000000000..f8284aae3
--- /dev/null
+++ b/netwerk/protocol/ftp/FTPChannelChild.cpp
@@ -0,0 +1,932 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* 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 "mozilla/net/NeckoChild.h"
+#include "mozilla/net/ChannelDiverterChild.h"
+#include "mozilla/net/FTPChannelChild.h"
+#include "mozilla/dom/TabChild.h"
+#include "nsFtpProtocolHandler.h"
+#include "nsITabChild.h"
+#include "nsStringStream.h"
+#include "nsNetUtil.h"
+#include "base/compiler_specific.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "SerializedLoadContext.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "nsIPrompt.h"
+
+using namespace mozilla::ipc;
+
+#undef LOG
+#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
+
+namespace mozilla {
+namespace net {
+
+FTPChannelChild::FTPChannelChild(nsIURI* uri)
+: mIPCOpen(false)
+, mUnknownDecoderInvolved(false)
+, mCanceled(false)
+, mSuspendCount(0)
+, mIsPending(false)
+, mLastModifiedTime(0)
+, mStartPos(0)
+, mDivertingToParent(false)
+, mFlushedForDiversion(false)
+, mSuspendSent(false)
+{
+ LOG(("Creating FTPChannelChild @%x\n", this));
+ // grab a reference to the handler to ensure that it doesn't go away.
+ NS_ADDREF(gFtpHandler);
+ SetURI(uri);
+ mEventQ = new ChannelEventQueue(static_cast<nsIFTPChannel*>(this));
+
+ // We could support thread retargeting, but as long as we're being driven by
+ // IPDL on the main thread it doesn't buy us anything.
+ DisallowThreadRetargeting();
+}
+
+FTPChannelChild::~FTPChannelChild()
+{
+ LOG(("Destroying FTPChannelChild @%x\n", this));
+ gFtpHandler->Release();
+}
+
+void
+FTPChannelChild::AddIPDLReference()
+{
+ MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference");
+ mIPCOpen = true;
+ AddRef();
+}
+
+void
+FTPChannelChild::ReleaseIPDLReference()
+{
+ MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
+ mIPCOpen = false;
+ Release();
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelChild::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS_INHERITED(FTPChannelChild,
+ nsBaseChannel,
+ nsIFTPChannel,
+ nsIUploadChannel,
+ nsIResumableChannel,
+ nsIProxiedChannel,
+ nsIChildChannel,
+ nsIDivertableChannel)
+
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelChild::GetLastModifiedTime(PRTime* lastModifiedTime)
+{
+ *lastModifiedTime = mLastModifiedTime;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::SetLastModifiedTime(PRTime lastModifiedTime)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID)
+{
+ NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+ mStartPos = aStartPos;
+ mEntityID = aEntityID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::GetEntityID(nsACString& entityID)
+{
+ entityID = mEntityID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo)
+{
+ DROP_DEAD();
+}
+
+NS_IMETHODIMP
+FTPChannelChild::SetUploadStream(nsIInputStream* stream,
+ const nsACString& contentType,
+ int64_t contentLength)
+{
+ NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+ mUploadStream = stream;
+ // NOTE: contentLength is intentionally ignored here.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::GetUploadStream(nsIInputStream** stream)
+{
+ NS_ENSURE_ARG_POINTER(stream);
+ *stream = mUploadStream;
+ NS_IF_ADDREF(*stream);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext)
+{
+ LOG(("FTPChannelChild::AsyncOpen [this=%p]\n", this));
+
+ NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
+ NS_ENSURE_ARG_POINTER(listener);
+ NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+ NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
+
+ // Port checked in parent, but duplicate here so we can return with error
+ // immediately, as we've done since before e10s.
+ nsresult rv;
+ rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate,
+ // because in the child ipdl,
+ // a typedef URI is defined...
+ if (NS_FAILED(rv))
+ return rv;
+
+ mozilla::dom::TabChild* tabChild = nullptr;
+ nsCOMPtr<nsITabChild> iTabChild;
+ NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
+ NS_GET_IID(nsITabChild),
+ getter_AddRefs(iTabChild));
+ GetCallback(iTabChild);
+ if (iTabChild) {
+ tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
+ }
+ if (MissingRequiredTabChild(tabChild, "ftp")) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ mListener = listener;
+ mListenerContext = aContext;
+
+ // add ourselves to the load group.
+ if (mLoadGroup)
+ mLoadGroup->AddRequest(this, nullptr);
+
+ OptionalInputStreamParams uploadStream;
+ nsTArray<mozilla::ipc::FileDescriptor> fds;
+ SerializeInputStream(mUploadStream, uploadStream, fds);
+
+ MOZ_ASSERT(fds.IsEmpty());
+
+ FTPChannelOpenArgs openArgs;
+ SerializeURI(nsBaseChannel::URI(), openArgs.uri());
+ openArgs.startPos() = mStartPos;
+ openArgs.entityID() = mEntityID;
+ openArgs.uploadStream() = uploadStream;
+
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ GetLoadInfo(getter_AddRefs(loadInfo));
+ rv = mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &openArgs.loadInfo());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ gNeckoChild->
+ SendPFTPChannelConstructor(this, tabChild, IPC::SerializedLoadContext(this),
+ openArgs);
+
+ // The socket transport layer in the chrome process now has a logical ref to
+ // us until OnStopRequest is called.
+ AddIPDLReference();
+
+ mIsPending = true;
+ mWasOpened = true;
+
+ return rv;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::IsPending(bool* result)
+{
+ *result = mIsPending;
+ return NS_OK;
+}
+
+nsresult
+FTPChannelChild::OpenContentStream(bool async,
+ nsIInputStream** stream,
+ nsIChannel** channel)
+{
+ NS_RUNTIMEABORT("FTPChannel*Child* should never have OpenContentStream called!");
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelChild::PFTPChannelChild
+//-----------------------------------------------------------------------------
+
+class FTPStartRequestEvent : public ChannelEvent
+{
+public:
+ FTPStartRequestEvent(FTPChannelChild* aChild,
+ const nsresult& aChannelStatus,
+ const int64_t& aContentLength,
+ const nsCString& aContentType,
+ const PRTime& aLastModified,
+ const nsCString& aEntityID,
+ const URIParams& aURI)
+ : mChild(aChild)
+ , mChannelStatus(aChannelStatus)
+ , mContentLength(aContentLength)
+ , mContentType(aContentType)
+ , mLastModified(aLastModified)
+ , mEntityID(aEntityID)
+ , mURI(aURI)
+ {
+ }
+ void Run()
+ {
+ mChild->DoOnStartRequest(mChannelStatus, mContentLength, mContentType,
+ mLastModified, mEntityID, mURI);
+ }
+
+private:
+ FTPChannelChild* mChild;
+ nsresult mChannelStatus;
+ int64_t mContentLength;
+ nsCString mContentType;
+ PRTime mLastModified;
+ nsCString mEntityID;
+ URIParams mURI;
+};
+
+bool
+FTPChannelChild::RecvOnStartRequest(const nsresult& aChannelStatus,
+ const int64_t& aContentLength,
+ const nsCString& aContentType,
+ const PRTime& aLastModified,
+ const nsCString& aEntityID,
+ const URIParams& aURI)
+{
+ // mFlushedForDiversion and mDivertingToParent should NEVER be set at this
+ // stage, as they are set in the listener's OnStartRequest.
+ MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
+ "mFlushedForDiversion should be unset before OnStartRequest!");
+ MOZ_RELEASE_ASSERT(!mDivertingToParent,
+ "mDivertingToParent should be unset before OnStartRequest!");
+
+ LOG(("FTPChannelChild::RecvOnStartRequest [this=%p]\n", this));
+
+ mEventQ->RunOrEnqueue(new FTPStartRequestEvent(this, aChannelStatus,
+ aContentLength, aContentType,
+ aLastModified, aEntityID,
+ aURI));
+ return true;
+}
+
+void
+FTPChannelChild::DoOnStartRequest(const nsresult& aChannelStatus,
+ const int64_t& aContentLength,
+ const nsCString& aContentType,
+ const PRTime& aLastModified,
+ const nsCString& aEntityID,
+ const URIParams& aURI)
+{
+ LOG(("FTPChannelChild::DoOnStartRequest [this=%p]\n", this));
+
+ // mFlushedForDiversion and mDivertingToParent should NEVER be set at this
+ // stage, as they are set in the listener's OnStartRequest.
+ MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
+ "mFlushedForDiversion should be unset before OnStartRequest!");
+ MOZ_RELEASE_ASSERT(!mDivertingToParent,
+ "mDivertingToParent should be unset before OnStartRequest!");
+
+ if (!mCanceled && NS_SUCCEEDED(mStatus)) {
+ mStatus = aChannelStatus;
+ }
+
+ mContentLength = aContentLength;
+ SetContentType(aContentType);
+ mLastModifiedTime = aLastModified;
+ mEntityID = aEntityID;
+
+ nsCString spec;
+ nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
+ nsresult rv = uri->GetSpec(spec);
+ if (NS_SUCCEEDED(rv)) {
+ rv = nsBaseChannel::URI()->SetSpec(spec);
+ if (NS_FAILED(rv)) {
+ Cancel(rv);
+ }
+ } else {
+ Cancel(rv);
+ }
+
+ AutoEventEnqueuer ensureSerialDispatch(mEventQ);
+ rv = mListener->OnStartRequest(this, mListenerContext);
+ if (NS_FAILED(rv))
+ Cancel(rv);
+
+ if (mDivertingToParent) {
+ mListener = nullptr;
+ mListenerContext = nullptr;
+ if (mLoadGroup) {
+ mLoadGroup->RemoveRequest(this, nullptr, mStatus);
+ }
+ }
+}
+
+class FTPDataAvailableEvent : public ChannelEvent
+{
+public:
+ FTPDataAvailableEvent(FTPChannelChild* aChild,
+ const nsresult& aChannelStatus,
+ const nsCString& aData,
+ const uint64_t& aOffset,
+ const uint32_t& aCount)
+ : mChild(aChild)
+ , mChannelStatus(aChannelStatus)
+ , mData(aData)
+ , mOffset(aOffset)
+ , mCount(aCount)
+ {
+ }
+ void Run()
+ {
+ mChild->DoOnDataAvailable(mChannelStatus, mData, mOffset, mCount);
+ }
+
+private:
+ FTPChannelChild* mChild;
+ nsresult mChannelStatus;
+ nsCString mData;
+ uint64_t mOffset;
+ uint32_t mCount;
+};
+
+bool
+FTPChannelChild::RecvOnDataAvailable(const nsresult& channelStatus,
+ const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count)
+{
+ MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
+ "Should not be receiving any more callbacks from parent!");
+
+ LOG(("FTPChannelChild::RecvOnDataAvailable [this=%p]\n", this));
+
+ mEventQ->RunOrEnqueue(new FTPDataAvailableEvent(this, channelStatus, data,
+ offset, count),
+ mDivertingToParent);
+
+ return true;
+}
+
+class MaybeDivertOnDataFTPEvent : public ChannelEvent
+{
+ public:
+ MaybeDivertOnDataFTPEvent(FTPChannelChild* child,
+ const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count)
+ : mChild(child)
+ , mData(data)
+ , mOffset(offset)
+ , mCount(count) {}
+
+ void Run()
+ {
+ mChild->MaybeDivertOnData(mData, mOffset, mCount);
+ }
+
+ private:
+ FTPChannelChild* mChild;
+ nsCString mData;
+ uint64_t mOffset;
+ uint32_t mCount;
+};
+
+void
+FTPChannelChild::MaybeDivertOnData(const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count)
+{
+ if (mDivertingToParent) {
+ SendDivertOnDataAvailable(data, offset, count);
+ }
+}
+
+void
+FTPChannelChild::DoOnDataAvailable(const nsresult& channelStatus,
+ const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count)
+{
+ LOG(("FTPChannelChild::DoOnDataAvailable [this=%p]\n", this));
+
+ if (!mCanceled && NS_SUCCEEDED(mStatus)) {
+ mStatus = channelStatus;
+ }
+
+ if (mDivertingToParent) {
+ MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
+ "Should not be processing any more callbacks from parent!");
+
+ SendDivertOnDataAvailable(data, offset, count);
+ return;
+ }
+
+ if (mCanceled)
+ return;
+
+ if (mUnknownDecoderInvolved) {
+ mUnknownDecoderEventQ.AppendElement(
+ MakeUnique<MaybeDivertOnDataFTPEvent>(this, data, offset, count));
+ }
+
+ // NOTE: the OnDataAvailable contract requires the client to read all the data
+ // in the inputstream. This code relies on that ('data' will go away after
+ // this function). Apparently the previous, non-e10s behavior was to actually
+ // support only reading part of the data, allowing later calls to read the
+ // rest.
+ nsCOMPtr<nsIInputStream> stringStream;
+ nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream),
+ data.get(),
+ count,
+ NS_ASSIGNMENT_DEPEND);
+ if (NS_FAILED(rv)) {
+ Cancel(rv);
+ return;
+ }
+
+ AutoEventEnqueuer ensureSerialDispatch(mEventQ);
+ rv = mListener->OnDataAvailable(this, mListenerContext,
+ stringStream, offset, count);
+ if (NS_FAILED(rv))
+ Cancel(rv);
+ stringStream->Close();
+}
+
+class FTPStopRequestEvent : public ChannelEvent
+{
+public:
+ FTPStopRequestEvent(FTPChannelChild* aChild,
+ const nsresult& aChannelStatus,
+ const nsCString &aErrorMsg,
+ bool aUseUTF8)
+ : mChild(aChild)
+ , mChannelStatus(aChannelStatus)
+ , mErrorMsg(aErrorMsg)
+ , mUseUTF8(aUseUTF8)
+ {
+ }
+ void Run()
+ {
+ mChild->DoOnStopRequest(mChannelStatus, mErrorMsg, mUseUTF8);
+ }
+
+private:
+ FTPChannelChild* mChild;
+ nsresult mChannelStatus;
+ nsCString mErrorMsg;
+ bool mUseUTF8;
+};
+
+bool
+FTPChannelChild::RecvOnStopRequest(const nsresult& aChannelStatus,
+ const nsCString &aErrorMsg,
+ const bool &aUseUTF8)
+{
+ MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
+ "Should not be receiving any more callbacks from parent!");
+
+ LOG(("FTPChannelChild::RecvOnStopRequest [this=%p status=%x]\n",
+ this, aChannelStatus));
+
+ mEventQ->RunOrEnqueue(new FTPStopRequestEvent(this, aChannelStatus, aErrorMsg,
+ aUseUTF8));
+ return true;
+}
+
+class nsFtpChildAsyncAlert : public Runnable
+{
+public:
+ nsFtpChildAsyncAlert(nsIPrompt *aPrompter, nsString aResponseMsg)
+ : mPrompter(aPrompter)
+ , mResponseMsg(aResponseMsg)
+ {
+ MOZ_COUNT_CTOR(nsFtpChildAsyncAlert);
+ }
+protected:
+ virtual ~nsFtpChildAsyncAlert()
+ {
+ MOZ_COUNT_DTOR(nsFtpChildAsyncAlert);
+ }
+public:
+ NS_IMETHOD Run() override
+ {
+ if (mPrompter) {
+ mPrompter->Alert(nullptr, mResponseMsg.get());
+ }
+ return NS_OK;
+ }
+private:
+ nsCOMPtr<nsIPrompt> mPrompter;
+ nsString mResponseMsg;
+};
+
+class MaybeDivertOnStopFTPEvent : public ChannelEvent
+{
+ public:
+ MaybeDivertOnStopFTPEvent(FTPChannelChild* child,
+ const nsresult& aChannelStatus)
+ : mChild(child)
+ , mChannelStatus(aChannelStatus) {}
+
+ void Run()
+ {
+ mChild->MaybeDivertOnStop(mChannelStatus);
+ }
+
+ private:
+ FTPChannelChild* mChild;
+ nsresult mChannelStatus;
+};
+
+void
+FTPChannelChild::MaybeDivertOnStop(const nsresult& aChannelStatus)
+{
+ if (mDivertingToParent) {
+ SendDivertOnStopRequest(aChannelStatus);
+ }
+}
+
+void
+FTPChannelChild::DoOnStopRequest(const nsresult& aChannelStatus,
+ const nsCString &aErrorMsg,
+ bool aUseUTF8)
+{
+ LOG(("FTPChannelChild::DoOnStopRequest [this=%p status=%x]\n",
+ this, aChannelStatus));
+
+ if (mDivertingToParent) {
+ MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
+ "Should not be processing any more callbacks from parent!");
+
+ SendDivertOnStopRequest(aChannelStatus);
+ return;
+ }
+
+ if (!mCanceled)
+ mStatus = aChannelStatus;
+
+ if (mUnknownDecoderInvolved) {
+ mUnknownDecoderEventQ.AppendElement(
+ MakeUnique<MaybeDivertOnStopFTPEvent>(this, aChannelStatus));
+ }
+
+ { // Ensure that all queued ipdl events are dispatched before
+ // we initiate protocol deletion below.
+ mIsPending = false;
+ AutoEventEnqueuer ensureSerialDispatch(mEventQ);
+ (void)mListener->OnStopRequest(this, mListenerContext, aChannelStatus);
+
+ if (NS_FAILED(aChannelStatus) && !aErrorMsg.IsEmpty()) {
+ nsCOMPtr<nsIPrompt> prompter;
+ GetCallback(prompter);
+ if (prompter) {
+ nsCOMPtr<nsIRunnable> alertEvent;
+ if (aUseUTF8) {
+ alertEvent = new nsFtpChildAsyncAlert(prompter,
+ NS_ConvertUTF8toUTF16(aErrorMsg));
+ } else {
+ alertEvent = new nsFtpChildAsyncAlert(prompter,
+ NS_ConvertASCIItoUTF16(aErrorMsg));
+ }
+ NS_DispatchToMainThread(alertEvent);
+ }
+ }
+
+ mListener = nullptr;
+ mListenerContext = nullptr;
+
+ if (mLoadGroup)
+ mLoadGroup->RemoveRequest(this, nullptr, aChannelStatus);
+ }
+
+ // This calls NeckoChild::DeallocPFTPChannelChild(), which deletes |this| if IPDL
+ // holds the last reference. Don't rely on |this| existing after here!
+ Send__delete__(this);
+}
+
+class FTPFailedAsyncOpenEvent : public ChannelEvent
+{
+ public:
+ FTPFailedAsyncOpenEvent(FTPChannelChild* aChild, nsresult aStatus)
+ : mChild(aChild), mStatus(aStatus) {}
+ void Run() { mChild->DoFailedAsyncOpen(mStatus); }
+ private:
+ FTPChannelChild* mChild;
+ nsresult mStatus;
+};
+
+bool
+FTPChannelChild::RecvFailedAsyncOpen(const nsresult& statusCode)
+{
+ LOG(("FTPChannelChild::RecvFailedAsyncOpen [this=%p status=%x]\n",
+ this, statusCode));
+ mEventQ->RunOrEnqueue(new FTPFailedAsyncOpenEvent(this, statusCode));
+ return true;
+}
+
+void
+FTPChannelChild::DoFailedAsyncOpen(const nsresult& statusCode)
+{
+ LOG(("FTPChannelChild::DoFailedAsyncOpen [this=%p status=%x]\n",
+ this, statusCode));
+ mStatus = statusCode;
+
+ if (mLoadGroup)
+ mLoadGroup->RemoveRequest(this, nullptr, statusCode);
+
+ if (mListener) {
+ mListener->OnStartRequest(this, mListenerContext);
+ mIsPending = false;
+ mListener->OnStopRequest(this, mListenerContext, statusCode);
+ } else {
+ mIsPending = false;
+ }
+
+ mListener = nullptr;
+ mListenerContext = nullptr;
+
+ if (mIPCOpen)
+ Send__delete__(this);
+}
+
+class FTPFlushedForDiversionEvent : public ChannelEvent
+{
+ public:
+ explicit FTPFlushedForDiversionEvent(FTPChannelChild* aChild)
+ : mChild(aChild)
+ {
+ MOZ_RELEASE_ASSERT(aChild);
+ }
+
+ void Run()
+ {
+ mChild->FlushedForDiversion();
+ }
+ private:
+ FTPChannelChild* mChild;
+};
+
+bool
+FTPChannelChild::RecvFlushedForDiversion()
+{
+ LOG(("FTPChannelChild::RecvFlushedForDiversion [this=%p]\n", this));
+ MOZ_ASSERT(mDivertingToParent);
+
+ mEventQ->RunOrEnqueue(new FTPFlushedForDiversionEvent(this), true);
+ return true;
+}
+
+void
+FTPChannelChild::FlushedForDiversion()
+{
+ LOG(("FTPChannelChild::FlushedForDiversion [this=%p]\n", this));
+ MOZ_RELEASE_ASSERT(mDivertingToParent);
+
+ // Once this is set, it should not be unset before FTPChannelChild is taken
+ // down. After it is set, no OnStart/OnData/OnStop callbacks should be
+ // received from the parent channel, nor dequeued from the ChannelEventQueue.
+ mFlushedForDiversion = true;
+
+ SendDivertComplete();
+}
+
+bool
+FTPChannelChild::RecvDivertMessages()
+{
+ LOG(("FTPChannelChild::RecvDivertMessages [this=%p]\n", this));
+ MOZ_RELEASE_ASSERT(mDivertingToParent);
+ MOZ_RELEASE_ASSERT(mSuspendCount > 0);
+
+ // DivertTo() has been called on parent, so we can now start sending queued
+ // IPDL messages back to parent listener.
+ if (NS_WARN_IF(NS_FAILED(Resume()))) {
+ return false;
+ }
+ return true;
+}
+
+class FTPDeleteSelfEvent : public ChannelEvent
+{
+ public:
+ explicit FTPDeleteSelfEvent(FTPChannelChild* aChild)
+ : mChild(aChild) {}
+ void Run() { mChild->DoDeleteSelf(); }
+ private:
+ FTPChannelChild* mChild;
+};
+
+bool
+FTPChannelChild::RecvDeleteSelf()
+{
+ mEventQ->RunOrEnqueue(new FTPDeleteSelfEvent(this));
+ return true;
+}
+
+void
+FTPChannelChild::DoDeleteSelf()
+{
+ if (mIPCOpen)
+ Send__delete__(this);
+}
+
+NS_IMETHODIMP
+FTPChannelChild::Cancel(nsresult status)
+{
+ LOG(("FTPChannelChild::Cancel [this=%p]\n", this));
+ if (mCanceled)
+ return NS_OK;
+
+ mCanceled = true;
+ mStatus = status;
+ if (mIPCOpen)
+ SendCancel(status);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::Suspend()
+{
+ NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
+
+ LOG(("FTPChannelChild::Suspend [this=%p]\n", this));
+
+ // SendSuspend only once, when suspend goes from 0 to 1.
+ // Don't SendSuspend at all if we're diverting callbacks to the parent;
+ // suspend will be called at the correct time in the parent itself.
+ if (!mSuspendCount++ && !mDivertingToParent) {
+ SendSuspend();
+ mSuspendSent = true;
+ }
+ mEventQ->Suspend();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::Resume()
+{
+ NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
+
+ LOG(("FTPChannelChild::Resume [this=%p]\n", this));
+
+ // SendResume only once, when suspend count drops to 0.
+ // Don't SendResume at all if we're diverting callbacks to the parent (unless
+ // suspend was sent earlier); otherwise, resume will be called at the correct
+ // time in the parent itself.
+ if (!--mSuspendCount && (!mDivertingToParent || mSuspendSent)) {
+ SendResume();
+ }
+ mEventQ->Resume();
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelChild::nsIChildChannel
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelChild::ConnectParent(uint32_t id)
+{
+ LOG(("FTPChannelChild::ConnectParent [this=%p]\n", this));
+
+ mozilla::dom::TabChild* tabChild = nullptr;
+ nsCOMPtr<nsITabChild> iTabChild;
+ NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
+ NS_GET_IID(nsITabChild),
+ getter_AddRefs(iTabChild));
+ GetCallback(iTabChild);
+ if (iTabChild) {
+ tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get());
+ }
+
+ // The socket transport in the chrome process now holds a logical ref to us
+ // until OnStopRequest, or we do a redirect, or we hit an IPDL error.
+ AddIPDLReference();
+
+ FTPChannelConnectArgs connectArgs(id);
+
+ if (!gNeckoChild->SendPFTPChannelConstructor(this, tabChild,
+ IPC::SerializedLoadContext(this),
+ connectArgs)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
+ nsISupports *aContext)
+{
+ LOG(("FTPChannelChild::CompleteRedirectSetup [this=%p]\n", this));
+
+ NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+ NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
+
+ mIsPending = true;
+ mWasOpened = true;
+ mListener = listener;
+ mListenerContext = aContext;
+
+ // add ourselves to the load group.
+ if (mLoadGroup)
+ mLoadGroup->AddRequest(this, nullptr);
+
+ // We already have an open IPDL connection to the parent. If on-modify-request
+ // listeners or load group observers canceled us, let the parent handle it
+ // and send it back to us naturally.
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelChild::nsIDivertableChannel
+//-----------------------------------------------------------------------------
+NS_IMETHODIMP
+FTPChannelChild::DivertToParent(ChannelDiverterChild **aChild)
+{
+ MOZ_RELEASE_ASSERT(aChild);
+ MOZ_RELEASE_ASSERT(gNeckoChild);
+ MOZ_RELEASE_ASSERT(!mDivertingToParent);
+
+ LOG(("FTPChannelChild::DivertToParent [this=%p]\n", this));
+
+ // We must fail DivertToParent() if there's no parent end of the channel (and
+ // won't be!) due to early failure.
+ if (NS_FAILED(mStatus) && !mIPCOpen) {
+ return mStatus;
+ }
+
+ nsresult rv = Suspend();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Once this is set, it should not be unset before the child is taken down.
+ mDivertingToParent = true;
+
+ PChannelDiverterChild* diverter =
+ gNeckoChild->SendPChannelDiverterConstructor(this);
+ MOZ_RELEASE_ASSERT(diverter);
+
+ *aChild = static_cast<ChannelDiverterChild*>(diverter);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::UnknownDecoderInvolvedKeepData()
+{
+ mUnknownDecoderInvolved = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::UnknownDecoderInvolvedOnStartRequestCalled()
+{
+ mUnknownDecoderInvolved = false;
+
+ nsresult rv = NS_OK;
+
+ if (mDivertingToParent) {
+ rv = mEventQ->PrependEvents(mUnknownDecoderEventQ);
+ }
+ mUnknownDecoderEventQ.Clear();
+
+ return rv;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::GetDivertingToParent(bool* aDiverting)
+{
+ NS_ENSURE_ARG_POINTER(aDiverting);
+ *aDiverting = mDivertingToParent;
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
+
diff --git a/netwerk/protocol/ftp/FTPChannelChild.h b/netwerk/protocol/ftp/FTPChannelChild.h
new file mode 100644
index 000000000..f68b85a72
--- /dev/null
+++ b/netwerk/protocol/ftp/FTPChannelChild.h
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* 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 mozilla_net_FTPChannelChild_h
+#define mozilla_net_FTPChannelChild_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/net/PFTPChannelChild.h"
+#include "mozilla/net/ChannelEventQueue.h"
+#include "nsBaseChannel.h"
+#include "nsIFTPChannel.h"
+#include "nsIUploadChannel.h"
+#include "nsIProxiedChannel.h"
+#include "nsIResumableChannel.h"
+#include "nsIChildChannel.h"
+#include "nsIDivertableChannel.h"
+
+#include "nsIStreamListener.h"
+#include "PrivateBrowsingChannel.h"
+
+namespace mozilla {
+namespace net {
+
+// This class inherits logic from nsBaseChannel that is not needed for an
+// e10s child channel, but it works. At some point we could slice up
+// nsBaseChannel and have a new class that has only the common logic for
+// nsFTPChannel/FTPChannelChild.
+
+class FTPChannelChild final : public PFTPChannelChild
+ , public nsBaseChannel
+ , public nsIFTPChannel
+ , public nsIUploadChannel
+ , public nsIResumableChannel
+ , public nsIProxiedChannel
+ , public nsIChildChannel
+ , public nsIDivertableChannel
+{
+public:
+ typedef ::nsIStreamListener nsIStreamListener;
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIFTPCHANNEL
+ NS_DECL_NSIUPLOADCHANNEL
+ NS_DECL_NSIRESUMABLECHANNEL
+ NS_DECL_NSIPROXIEDCHANNEL
+ NS_DECL_NSICHILDCHANNEL
+ NS_DECL_NSIDIVERTABLECHANNEL
+
+ NS_IMETHOD Cancel(nsresult status) override;
+ NS_IMETHOD Suspend() override;
+ NS_IMETHOD Resume() override;
+
+ explicit FTPChannelChild(nsIURI* uri);
+
+ void AddIPDLReference();
+ void ReleaseIPDLReference();
+
+ NS_IMETHOD AsyncOpen(nsIStreamListener* listener, nsISupports* aContext) override;
+
+ // Note that we handle this ourselves, overriding the nsBaseChannel
+ // default behavior, in order to be e10s-friendly.
+ NS_IMETHOD IsPending(bool* result) override;
+
+ nsresult OpenContentStream(bool async,
+ nsIInputStream** stream,
+ nsIChannel** channel) override;
+
+ bool IsSuspended();
+
+ void FlushedForDiversion();
+
+protected:
+ virtual ~FTPChannelChild();
+
+ bool RecvOnStartRequest(const nsresult& aChannelStatus,
+ const int64_t& aContentLength,
+ const nsCString& aContentType,
+ const PRTime& aLastModified,
+ const nsCString& aEntityID,
+ const URIParams& aURI) override;
+ bool RecvOnDataAvailable(const nsresult& channelStatus,
+ const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count) override;
+ bool RecvOnStopRequest(const nsresult& channelStatus,
+ const nsCString &aErrorMsg,
+ const bool &aUseUTF8) override;
+ bool RecvFailedAsyncOpen(const nsresult& statusCode) override;
+ bool RecvFlushedForDiversion() override;
+ bool RecvDivertMessages() override;
+ bool RecvDeleteSelf() override;
+
+ void DoOnStartRequest(const nsresult& aChannelStatus,
+ const int64_t& aContentLength,
+ const nsCString& aContentType,
+ const PRTime& aLastModified,
+ const nsCString& aEntityID,
+ const URIParams& aURI);
+ void DoOnDataAvailable(const nsresult& channelStatus,
+ const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count);
+ void MaybeDivertOnData(const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count);
+ void MaybeDivertOnStop(const nsresult& statusCode);
+ void DoOnStopRequest(const nsresult& statusCode,
+ const nsCString &aErrorMsg,
+ bool aUseUTF8);
+ void DoFailedAsyncOpen(const nsresult& statusCode);
+ void DoDeleteSelf();
+
+ friend class FTPStartRequestEvent;
+ friend class FTPDataAvailableEvent;
+ friend class MaybeDivertOnDataFTPEvent;
+ friend class FTPStopRequestEvent;
+ friend class MaybeDivertOnStopFTPEvent;
+ friend class FTPFailedAsyncOpenEvent;
+ friend class FTPDeleteSelfEvent;
+
+private:
+ nsCOMPtr<nsIInputStream> mUploadStream;
+
+ bool mIPCOpen;
+ RefPtr<ChannelEventQueue> mEventQ;
+
+ // If nsUnknownDecoder is involved we queue onDataAvailable (and possibly
+ // OnStopRequest) so that we can divert them if needed when the listener's
+ // OnStartRequest is finally called
+ nsTArray<UniquePtr<ChannelEvent>> mUnknownDecoderEventQ;
+ bool mUnknownDecoderInvolved;
+
+ bool mCanceled;
+ uint32_t mSuspendCount;
+ bool mIsPending;
+
+ PRTime mLastModifiedTime;
+ uint64_t mStartPos;
+ nsCString mEntityID;
+
+ // Once set, OnData and possibly OnStop will be diverted to the parent.
+ bool mDivertingToParent;
+ // Once set, no OnStart/OnData/OnStop callbacks should be received from the
+ // parent channel, nor dequeued from the ChannelEventQueue.
+ bool mFlushedForDiversion;
+ // Set if SendSuspend is called. Determines if SendResume is needed when
+ // diverting callbacks to parent.
+ bool mSuspendSent;
+};
+
+inline bool
+FTPChannelChild::IsSuspended()
+{
+ return mSuspendCount != 0;
+}
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_FTPChannelChild_h
diff --git a/netwerk/protocol/ftp/FTPChannelParent.cpp b/netwerk/protocol/ftp/FTPChannelParent.cpp
new file mode 100644
index 000000000..cdd334971
--- /dev/null
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -0,0 +1,896 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* 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 "mozilla/net/FTPChannelParent.h"
+#include "nsStringStream.h"
+#include "mozilla/net/ChannelEventQueue.h"
+#include "mozilla/dom/TabParent.h"
+#include "nsFTPChannel.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsQueryObject.h"
+#include "nsFtpProtocolHandler.h"
+#include "nsIAuthPrompt.h"
+#include "nsIAuthPromptProvider.h"
+#include "nsIEncodedChannel.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIForcePendingChannel.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/Unused.h"
+#include "SerializedLoadContext.h"
+#include "nsIContentPolicy.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/LoadInfo.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+#undef LOG
+#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
+
+namespace mozilla {
+namespace net {
+
+FTPChannelParent::FTPChannelParent(const PBrowserOrId& aIframeEmbedding,
+ nsILoadContext* aLoadContext,
+ PBOverrideStatus aOverrideStatus)
+ : mIPCClosed(false)
+ , mLoadContext(aLoadContext)
+ , mPBOverride(aOverrideStatus)
+ , mStatus(NS_OK)
+ , mDivertingFromChild(false)
+ , mDivertedOnStartRequest(false)
+ , mSuspendedForDiversion(false)
+ , mUseUTF8(false)
+{
+ nsIProtocolHandler* handler;
+ CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &handler);
+ MOZ_ASSERT(handler, "no ftp handler");
+
+ if (aIframeEmbedding.type() == PBrowserOrId::TPBrowserParent) {
+ mTabParent = static_cast<dom::TabParent*>(aIframeEmbedding.get_PBrowserParent());
+ }
+
+ mEventQ = new ChannelEventQueue(static_cast<nsIParentChannel*>(this));
+}
+
+FTPChannelParent::~FTPChannelParent()
+{
+ gFtpHandler->Release();
+}
+
+void
+FTPChannelParent::ActorDestroy(ActorDestroyReason why)
+{
+ // We may still have refcount>0 if the channel hasn't called OnStopRequest
+ // yet, but we must not send any more msgs to child.
+ mIPCClosed = true;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(FTPChannelParent,
+ nsIStreamListener,
+ nsIParentChannel,
+ nsIInterfaceRequestor,
+ nsIRequestObserver,
+ nsIChannelEventSink,
+ nsIFTPChannelParentInternal)
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::PFTPChannelParent
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent methods
+//-----------------------------------------------------------------------------
+
+bool
+FTPChannelParent::Init(const FTPChannelCreationArgs& aArgs)
+{
+ switch (aArgs.type()) {
+ case FTPChannelCreationArgs::TFTPChannelOpenArgs:
+ {
+ const FTPChannelOpenArgs& a = aArgs.get_FTPChannelOpenArgs();
+ return DoAsyncOpen(a.uri(), a.startPos(), a.entityID(), a.uploadStream(),
+ a.loadInfo());
+ }
+ case FTPChannelCreationArgs::TFTPChannelConnectArgs:
+ {
+ const FTPChannelConnectArgs& cArgs = aArgs.get_FTPChannelConnectArgs();
+ return ConnectChannel(cArgs.channelId());
+ }
+ default:
+ NS_NOTREACHED("unknown open type");
+ return false;
+ }
+}
+
+bool
+FTPChannelParent::DoAsyncOpen(const URIParams& aURI,
+ const uint64_t& aStartPos,
+ const nsCString& aEntityID,
+ const OptionalInputStreamParams& aUploadStream,
+ const OptionalLoadInfoArgs& aLoadInfoArgs)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
+ if (!uri)
+ return false;
+
+#ifdef DEBUG
+ LOG(("FTPChannelParent DoAsyncOpen [this=%p uri=%s]\n",
+ this, uri->GetSpecOrDefault().get()));
+#endif
+
+ nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
+ if (NS_FAILED(rv)) {
+ return SendFailedAsyncOpen(rv);
+ }
+
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfoArgs,
+ getter_AddRefs(loadInfo));
+ if (NS_FAILED(rv)) {
+ return SendFailedAsyncOpen(rv);
+ }
+
+ NeckoOriginAttributes attrs;
+ rv = loadInfo->GetOriginAttributes(&attrs);
+ if (NS_FAILED(rv)) {
+ return SendFailedAsyncOpen(rv);
+ }
+
+ nsCOMPtr<nsIChannel> chan;
+ rv = NS_NewChannelInternal(getter_AddRefs(chan), uri, loadInfo,
+ nullptr, nullptr,
+ nsIRequest::LOAD_NORMAL, ios);
+
+ if (NS_FAILED(rv))
+ return SendFailedAsyncOpen(rv);
+
+ mChannel = chan;
+
+ // later on mChannel may become an HTTP channel (we'll be redirected to one
+ // if we're using a proxy), but for now this is safe
+ nsFtpChannel* ftpChan = static_cast<nsFtpChannel*>(mChannel.get());
+
+ if (mPBOverride != kPBOverride_Unset) {
+ ftpChan->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
+ }
+ rv = ftpChan->SetNotificationCallbacks(this);
+ if (NS_FAILED(rv))
+ return SendFailedAsyncOpen(rv);
+
+ nsTArray<mozilla::ipc::FileDescriptor> fds;
+ nsCOMPtr<nsIInputStream> upload = DeserializeInputStream(aUploadStream, fds);
+ if (upload) {
+ // contentType and contentLength are ignored
+ rv = ftpChan->SetUploadStream(upload, EmptyCString(), 0);
+ if (NS_FAILED(rv))
+ return SendFailedAsyncOpen(rv);
+ }
+
+ rv = ftpChan->ResumeAt(aStartPos, aEntityID);
+ if (NS_FAILED(rv))
+ return SendFailedAsyncOpen(rv);
+
+ if (loadInfo && loadInfo->GetEnforceSecurity()) {
+ rv = ftpChan->AsyncOpen2(this);
+ }
+ else {
+ rv = ftpChan->AsyncOpen(this, nullptr);
+ }
+
+ if (NS_FAILED(rv))
+ return SendFailedAsyncOpen(rv);
+
+ return true;
+}
+
+bool
+FTPChannelParent::ConnectChannel(const uint32_t& channelId)
+{
+ nsresult rv;
+
+ LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId));
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
+ if (NS_SUCCEEDED(rv))
+ mChannel = channel;
+
+ LOG((" found channel %p, rv=%08x", mChannel.get(), rv));
+
+ return true;
+}
+
+bool
+FTPChannelParent::RecvCancel(const nsresult& status)
+{
+ if (mChannel)
+ mChannel->Cancel(status);
+
+ return true;
+}
+
+bool
+FTPChannelParent::RecvSuspend()
+{
+ if (mChannel) {
+ SuspendChannel();
+ }
+ return true;
+}
+
+bool
+FTPChannelParent::RecvResume()
+{
+ if (mChannel) {
+ ResumeChannel();
+ }
+ return true;
+}
+
+class FTPDivertDataAvailableEvent : public ChannelEvent
+{
+public:
+ FTPDivertDataAvailableEvent(FTPChannelParent* aParent,
+ const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count)
+ : mParent(aParent)
+ , mData(data)
+ , mOffset(offset)
+ , mCount(count)
+ {
+ }
+
+ void Run()
+ {
+ mParent->DivertOnDataAvailable(mData, mOffset, mCount);
+ }
+
+private:
+ FTPChannelParent* mParent;
+ nsCString mData;
+ uint64_t mOffset;
+ uint32_t mCount;
+};
+
+bool
+FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count)
+{
+ if (NS_WARN_IF(!mDivertingFromChild)) {
+ MOZ_ASSERT(mDivertingFromChild,
+ "Cannot RecvDivertOnDataAvailable if diverting is not set!");
+ FailDiversion(NS_ERROR_UNEXPECTED);
+ return false;
+ }
+
+ // Drop OnDataAvailables if the parent was canceled already.
+ if (NS_FAILED(mStatus)) {
+ return true;
+ }
+
+ mEventQ->RunOrEnqueue(new FTPDivertDataAvailableEvent(this, data, offset,
+ count));
+ return true;
+}
+
+void
+FTPChannelParent::DivertOnDataAvailable(const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count)
+{
+ LOG(("FTPChannelParent::DivertOnDataAvailable [this=%p]\n", this));
+
+ if (NS_WARN_IF(!mDivertingFromChild)) {
+ MOZ_ASSERT(mDivertingFromChild,
+ "Cannot DivertOnDataAvailable if diverting is not set!");
+ FailDiversion(NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+ // Drop OnDataAvailables if the parent was canceled already.
+ if (NS_FAILED(mStatus)) {
+ return;
+ }
+
+ nsCOMPtr<nsIInputStream> stringStream;
+ nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(),
+ count, NS_ASSIGNMENT_DEPEND);
+ if (NS_FAILED(rv)) {
+ if (mChannel) {
+ mChannel->Cancel(rv);
+ }
+ mStatus = rv;
+ return;
+ }
+
+ AutoEventEnqueuer ensureSerialDispatch(mEventQ);
+
+ rv = OnDataAvailable(mChannel, nullptr, stringStream, offset, count);
+
+ stringStream->Close();
+ if (NS_FAILED(rv)) {
+ if (mChannel) {
+ mChannel->Cancel(rv);
+ }
+ mStatus = rv;
+ }
+}
+
+class FTPDivertStopRequestEvent : public ChannelEvent
+{
+public:
+ FTPDivertStopRequestEvent(FTPChannelParent* aParent,
+ const nsresult& statusCode)
+ : mParent(aParent)
+ , mStatusCode(statusCode)
+ {
+ }
+
+ void Run() {
+ mParent->DivertOnStopRequest(mStatusCode);
+ }
+
+private:
+ FTPChannelParent* mParent;
+ nsresult mStatusCode;
+};
+
+bool
+FTPChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode)
+{
+ if (NS_WARN_IF(!mDivertingFromChild)) {
+ MOZ_ASSERT(mDivertingFromChild,
+ "Cannot RecvDivertOnStopRequest if diverting is not set!");
+ FailDiversion(NS_ERROR_UNEXPECTED);
+ return false;
+ }
+
+ mEventQ->RunOrEnqueue(new FTPDivertStopRequestEvent(this, statusCode));
+ return true;
+}
+
+void
+FTPChannelParent::DivertOnStopRequest(const nsresult& statusCode)
+{
+ LOG(("FTPChannelParent::DivertOnStopRequest [this=%p]\n", this));
+
+ if (NS_WARN_IF(!mDivertingFromChild)) {
+ MOZ_ASSERT(mDivertingFromChild,
+ "Cannot DivertOnStopRequest if diverting is not set!");
+ FailDiversion(NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+ // Honor the channel's status even if the underlying transaction completed.
+ nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode;
+
+ // Reset fake pending status in case OnStopRequest has already been called.
+ if (mChannel) {
+ nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
+ if (forcePendingIChan) {
+ forcePendingIChan->ForcePending(false);
+ }
+ }
+
+ AutoEventEnqueuer ensureSerialDispatch(mEventQ);
+ OnStopRequest(mChannel, nullptr, status);
+}
+
+class FTPDivertCompleteEvent : public ChannelEvent
+{
+public:
+ explicit FTPDivertCompleteEvent(FTPChannelParent* aParent)
+ : mParent(aParent)
+ {
+ }
+
+ void Run() {
+ mParent->DivertComplete();
+ }
+
+private:
+ FTPChannelParent* mParent;
+};
+
+bool
+FTPChannelParent::RecvDivertComplete()
+{
+ if (NS_WARN_IF(!mDivertingFromChild)) {
+ MOZ_ASSERT(mDivertingFromChild,
+ "Cannot RecvDivertComplete if diverting is not set!");
+ FailDiversion(NS_ERROR_UNEXPECTED);
+ return false;
+ }
+
+ mEventQ->RunOrEnqueue(new FTPDivertCompleteEvent(this));
+ return true;
+}
+
+void
+FTPChannelParent::DivertComplete()
+{
+ LOG(("FTPChannelParent::DivertComplete [this=%p]\n", this));
+
+ if (NS_WARN_IF(!mDivertingFromChild)) {
+ MOZ_ASSERT(mDivertingFromChild,
+ "Cannot DivertComplete if diverting is not set!");
+ FailDiversion(NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+ nsresult rv = ResumeForDiversion();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ FailDiversion(NS_ERROR_UNEXPECTED);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::nsIRequestObserver
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelParent::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
+{
+ LOG(("FTPChannelParent::OnStartRequest [this=%p]\n", this));
+
+ if (mDivertingFromChild) {
+ MOZ_RELEASE_ASSERT(mDivertToListener,
+ "Cannot divert if listener is unset!");
+ return mDivertToListener->OnStartRequest(aRequest, aContext);
+ }
+
+ nsCOMPtr<nsIChannel> chan = do_QueryInterface(aRequest);
+ MOZ_ASSERT(chan);
+ NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
+
+ int64_t contentLength;
+ chan->GetContentLength(&contentLength);
+ nsCString contentType;
+ chan->GetContentType(contentType);
+
+ nsCString entityID;
+ nsCOMPtr<nsIResumableChannel> resChan = do_QueryInterface(aRequest);
+ MOZ_ASSERT(resChan); // both FTP and HTTP should implement nsIResumableChannel
+ if (resChan) {
+ resChan->GetEntityID(entityID);
+ }
+
+ PRTime lastModified = 0;
+ nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(aRequest);
+ if (ftpChan) {
+ ftpChan->GetLastModifiedTime(&lastModified);
+ }
+ nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(aRequest);
+ if (httpChan) {
+ httpChan->GetLastModifiedTime(&lastModified);
+ }
+
+ URIParams uriparam;
+ nsCOMPtr<nsIURI> uri;
+ chan->GetURI(getter_AddRefs(uri));
+ SerializeURI(uri, uriparam);
+
+ if (mIPCClosed || !SendOnStartRequest(mStatus, contentLength, contentType,
+ lastModified, entityID, uriparam)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelParent::OnStopRequest(nsIRequest* aRequest,
+ nsISupports* aContext,
+ nsresult aStatusCode)
+{
+ LOG(("FTPChannelParent::OnStopRequest: [this=%p status=%ul]\n",
+ this, aStatusCode));
+
+ if (mDivertingFromChild) {
+ MOZ_RELEASE_ASSERT(mDivertToListener,
+ "Cannot divert if listener is unset!");
+ return mDivertToListener->OnStopRequest(aRequest, aContext, aStatusCode);
+ }
+
+ if (mIPCClosed || !SendOnStopRequest(aStatusCode, mErrorMsg, mUseUTF8)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::nsIStreamListener
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelParent::OnDataAvailable(nsIRequest* aRequest,
+ nsISupports* aContext,
+ nsIInputStream* aInputStream,
+ uint64_t aOffset,
+ uint32_t aCount)
+{
+ LOG(("FTPChannelParent::OnDataAvailable [this=%p]\n", this));
+
+ if (mDivertingFromChild) {
+ MOZ_RELEASE_ASSERT(mDivertToListener,
+ "Cannot divert if listener is unset!");
+ return mDivertToListener->OnDataAvailable(aRequest, aContext, aInputStream,
+ aOffset, aCount);
+ }
+
+ nsCString data;
+ nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (mIPCClosed || !SendOnDataAvailable(mStatus, data, aOffset, aCount))
+ return NS_ERROR_UNEXPECTED;
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::nsIParentChannel
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelParent::SetParentListener(HttpChannelParentListener* aListener)
+{
+ // Do not need ptr to HttpChannelParentListener.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelParent::NotifyTrackingProtectionDisabled()
+{
+ // One day, this should probably be filled in.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelParent::Delete()
+{
+ if (mIPCClosed || !SendDeleteSelf())
+ return NS_ERROR_UNEXPECTED;
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::nsIInterfaceRequestor
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelParent::GetInterface(const nsIID& uuid, void** result)
+{
+ if (uuid.Equals(NS_GET_IID(nsIAuthPromptProvider)) ||
+ uuid.Equals(NS_GET_IID(nsISecureBrowserUI))) {
+ if (mTabParent) {
+ return mTabParent->QueryInterface(uuid, result);
+ }
+ } else if (uuid.Equals(NS_GET_IID(nsIAuthPrompt)) ||
+ uuid.Equals(NS_GET_IID(nsIAuthPrompt2))) {
+ nsCOMPtr<nsIAuthPromptProvider> provider(do_QueryObject(mTabParent));
+ if (provider) {
+ return provider->GetAuthPrompt(nsIAuthPromptProvider::PROMPT_NORMAL,
+ uuid,
+ result);
+ }
+ }
+
+ // Only support nsILoadContext if child channel's callbacks did too
+ if (uuid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
+ nsCOMPtr<nsILoadContext> copy = mLoadContext;
+ copy.forget(result);
+ return NS_OK;
+ }
+
+ return QueryInterface(uuid, result);
+}
+
+nsresult
+FTPChannelParent::SuspendChannel()
+{
+ nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
+ do_QueryInterface(mChannel);
+ if (chan) {
+ return chan->SuspendInternal();
+ } else {
+ return mChannel->Suspend();
+ }
+}
+
+nsresult
+FTPChannelParent::ResumeChannel()
+{
+ nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
+ do_QueryInterface(mChannel);
+ if (chan) {
+ return chan->ResumeInternal();
+ } else {
+ return mChannel->Resume();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::ADivertableParentChannel
+//-----------------------------------------------------------------------------
+nsresult
+FTPChannelParent::SuspendForDiversion()
+{
+ MOZ_ASSERT(mChannel);
+ if (NS_WARN_IF(mDivertingFromChild)) {
+ MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Try suspending the channel. Allow it to fail, since OnStopRequest may have
+ // been called and thus the channel may not be pending.
+ nsresult rv = SuspendChannel();
+ MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE);
+ mSuspendedForDiversion = NS_SUCCEEDED(rv);
+
+ // Once this is set, no more OnStart/OnData/OnStop callbacks should be sent
+ // to the child.
+ mDivertingFromChild = true;
+
+ nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
+ do_QueryInterface(mChannel);
+ if (chan) {
+ chan->MessageDiversionStarted(this);
+ }
+
+ return NS_OK;
+}
+
+/* private, supporting function for ADivertableParentChannel */
+nsresult
+FTPChannelParent::ResumeForDiversion()
+{
+ MOZ_ASSERT(mChannel);
+ MOZ_ASSERT(mDivertToListener);
+ if (NS_WARN_IF(!mDivertingFromChild)) {
+ MOZ_ASSERT(mDivertingFromChild,
+ "Cannot ResumeForDiversion if not diverting!");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsCOMPtr<nsIChannelWithDivertableParentListener> chan =
+ do_QueryInterface(mChannel);
+ if (chan) {
+ chan->MessageDiversionStop();
+ }
+
+ if (mSuspendedForDiversion) {
+ nsresult rv = ResumeChannel();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ FailDiversion(NS_ERROR_UNEXPECTED, true);
+ return rv;
+ }
+ mSuspendedForDiversion = false;
+ }
+
+ // Delete() will tear down IPDL, but ref from underlying nsFTPChannel will
+ // keep us alive if there's more data to be delivered to listener.
+ if (NS_WARN_IF(NS_FAILED(Delete()))) {
+ FailDiversion(NS_ERROR_UNEXPECTED);
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+nsresult
+FTPChannelParent::SuspendMessageDiversion()
+{
+ // This only need to suspend message queue.
+ mEventQ->Suspend();
+ return NS_OK;
+}
+
+nsresult
+FTPChannelParent::ResumeMessageDiversion()
+{
+ // This only need to resumes message queue.
+ mEventQ->Resume();
+ return NS_OK;
+}
+
+void
+FTPChannelParent::DivertTo(nsIStreamListener *aListener)
+{
+ MOZ_ASSERT(aListener);
+ if (NS_WARN_IF(!mDivertingFromChild)) {
+ MOZ_ASSERT(mDivertingFromChild,
+ "Cannot DivertTo new listener if diverting is not set!");
+ return;
+ }
+
+ if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) {
+ FailDiversion(NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+ mDivertToListener = aListener;
+
+ // Call OnStartRequest and SendDivertMessages asynchronously to avoid
+ // reentering client context.
+ NS_DispatchToCurrentThread(
+ NewRunnableMethod(this, &FTPChannelParent::StartDiversion));
+ return;
+}
+
+void
+FTPChannelParent::StartDiversion()
+{
+ if (NS_WARN_IF(!mDivertingFromChild)) {
+ MOZ_ASSERT(mDivertingFromChild,
+ "Cannot StartDiversion if diverting is not set!");
+ return;
+ }
+
+ // Fake pending status in case OnStopRequest has already been called.
+ if (mChannel) {
+ nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
+ if (forcePendingIChan) {
+ forcePendingIChan->ForcePending(true);
+ }
+ }
+
+ {
+ AutoEventEnqueuer ensureSerialDispatch(mEventQ);
+ // Call OnStartRequest for the "DivertTo" listener.
+ nsresult rv = OnStartRequest(mChannel, nullptr);
+ if (NS_FAILED(rv)) {
+ if (mChannel) {
+ mChannel->Cancel(rv);
+ }
+ mStatus = rv;
+ return;
+ }
+ }
+
+ // After OnStartRequest has been called, tell FTPChannelChild to divert the
+ // OnDataAvailables and OnStopRequest to this FTPChannelParent.
+ if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
+ FailDiversion(NS_ERROR_UNEXPECTED);
+ return;
+ }
+}
+
+class FTPFailDiversionEvent : public Runnable
+{
+public:
+ FTPFailDiversionEvent(FTPChannelParent *aChannelParent,
+ nsresult aErrorCode,
+ bool aSkipResume)
+ : mChannelParent(aChannelParent)
+ , mErrorCode(aErrorCode)
+ , mSkipResume(aSkipResume)
+ {
+ MOZ_RELEASE_ASSERT(aChannelParent);
+ MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
+ }
+ NS_IMETHOD Run() override
+ {
+ mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume);
+ return NS_OK;
+ }
+private:
+ RefPtr<FTPChannelParent> mChannelParent;
+ nsresult mErrorCode;
+ bool mSkipResume;
+};
+
+void
+FTPChannelParent::FailDiversion(nsresult aErrorCode,
+ bool aSkipResume)
+{
+ MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
+ MOZ_RELEASE_ASSERT(mDivertingFromChild);
+ MOZ_RELEASE_ASSERT(mDivertToListener);
+ MOZ_RELEASE_ASSERT(mChannel);
+
+ NS_DispatchToCurrentThread(
+ new FTPFailDiversionEvent(this, aErrorCode, aSkipResume));
+}
+
+void
+FTPChannelParent::NotifyDiversionFailed(nsresult aErrorCode,
+ bool aSkipResume)
+{
+ MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
+ MOZ_RELEASE_ASSERT(mDivertingFromChild);
+ MOZ_RELEASE_ASSERT(mDivertToListener);
+ MOZ_RELEASE_ASSERT(mChannel);
+
+ mChannel->Cancel(aErrorCode);
+ nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
+ if (forcePendingIChan) {
+ forcePendingIChan->ForcePending(false);
+ }
+
+ bool isPending = false;
+ nsresult rv = mChannel->IsPending(&isPending);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+
+ // Resume only we suspended earlier.
+ if (mSuspendedForDiversion) {
+ ResumeChannel();
+ }
+ // Channel has already sent OnStartRequest to the child, so ensure that we
+ // call it here if it hasn't already been called.
+ if (!mDivertedOnStartRequest) {
+ nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel);
+ if (forcePendingIChan) {
+ forcePendingIChan->ForcePending(true);
+ }
+ mDivertToListener->OnStartRequest(mChannel, nullptr);
+
+ if (forcePendingIChan) {
+ forcePendingIChan->ForcePending(false);
+ }
+ }
+ // If the channel is pending, it will call OnStopRequest itself; otherwise, do
+ // it here.
+ if (!isPending) {
+ mDivertToListener->OnStopRequest(mChannel, nullptr, aErrorCode);
+ }
+ mDivertToListener = nullptr;
+ mChannel = nullptr;
+
+ if (!mIPCClosed) {
+ Unused << SendDeleteSelf();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::nsIChannelEventSink
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelParent::AsyncOnChannelRedirect(
+ nsIChannel *oldChannel,
+ nsIChannel *newChannel,
+ uint32_t redirectFlags,
+ nsIAsyncVerifyRedirectCallback* callback)
+{
+ nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(newChannel);
+ if (!ftpChan) {
+ // when FTP is set to use HTTP proxying, we wind up getting redirected to an HTTP channel.
+ nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(newChannel);
+ if (!httpChan)
+ return NS_ERROR_UNEXPECTED;
+ }
+ mChannel = newChannel;
+ callback->OnRedirectVerifyCallback(NS_OK);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelParent::SetErrorMsg(const char *aMsg, bool aUseUTF8)
+{
+ mErrorMsg = aMsg;
+ mUseUTF8 = aUseUTF8;
+ return NS_OK;
+}
+
+//---------------------
+} // namespace net
+} // namespace mozilla
+
diff --git a/netwerk/protocol/ftp/FTPChannelParent.h b/netwerk/protocol/ftp/FTPChannelParent.h
new file mode 100644
index 000000000..210c99618
--- /dev/null
+++ b/netwerk/protocol/ftp/FTPChannelParent.h
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* 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 mozilla_net_FTPChannelParent_h
+#define mozilla_net_FTPChannelParent_h
+
+#include "ADivertableParentChannel.h"
+#include "mozilla/net/PFTPChannelParent.h"
+#include "mozilla/net/NeckoParent.h"
+#include "nsIParentChannel.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIChannelEventSink.h"
+#include "nsIFTPChannelParentInternal.h"
+
+class nsILoadContext;
+
+namespace mozilla {
+
+namespace dom {
+class TabParent;
+class PBrowserOrId;
+} // namespace dom
+
+namespace net {
+class ChannelEventQueue;
+
+class FTPChannelParent final : public PFTPChannelParent
+ , public nsIParentChannel
+ , public nsIInterfaceRequestor
+ , public ADivertableParentChannel
+ , public nsIChannelEventSink
+ , public nsIFTPChannelParentInternal
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_NSIPARENTCHANNEL
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSICHANNELEVENTSINK
+
+ FTPChannelParent(const dom::PBrowserOrId& aIframeEmbedding,
+ nsILoadContext* aLoadContext,
+ PBOverrideStatus aOverrideStatus);
+
+ bool Init(const FTPChannelCreationArgs& aOpenArgs);
+
+ // ADivertableParentChannel functions.
+ void DivertTo(nsIStreamListener *aListener) override;
+ nsresult SuspendForDiversion() override;
+ nsresult SuspendMessageDiversion() override;
+ nsresult ResumeMessageDiversion() override;
+
+ // Calls OnStartRequest for "DivertTo" listener, then notifies child channel
+ // that it should divert OnDataAvailable and OnStopRequest calls to this
+ // parent channel.
+ void StartDiversion();
+
+ // Handles calling OnStart/Stop if there are errors during diversion.
+ // Called asynchronously from FailDiversion.
+ void NotifyDiversionFailed(nsresult aErrorCode, bool aSkipResume = true);
+
+ NS_IMETHOD SetErrorMsg(const char *aMsg, bool aUseUTF8) override;
+
+protected:
+ virtual ~FTPChannelParent();
+
+ // private, supporting function for ADivertableParentChannel.
+ nsresult ResumeForDiversion();
+
+ // Asynchronously calls NotifyDiversionFailed.
+ void FailDiversion(nsresult aErrorCode, bool aSkipResume = true);
+
+ bool DoAsyncOpen(const URIParams& aURI, const uint64_t& aStartPos,
+ const nsCString& aEntityID,
+ const OptionalInputStreamParams& aUploadStream,
+ const OptionalLoadInfoArgs& aLoadInfoArgs);
+
+ // used to connect redirected-to channel in parent with just created
+ // ChildChannel. Used during HTTP->FTP redirects.
+ bool ConnectChannel(const uint32_t& channelId);
+
+ void DivertOnDataAvailable(const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count);
+ void DivertOnStopRequest(const nsresult& statusCode);
+ void DivertComplete();
+
+ friend class FTPDivertDataAvailableEvent;
+ friend class FTPDivertStopRequestEvent;
+ friend class FTPDivertCompleteEvent;
+
+ virtual bool RecvCancel(const nsresult& status) override;
+ virtual bool RecvSuspend() override;
+ virtual bool RecvResume() override;
+ virtual bool RecvDivertOnDataAvailable(const nsCString& data,
+ const uint64_t& offset,
+ const uint32_t& count) override;
+ virtual bool RecvDivertOnStopRequest(const nsresult& statusCode) override;
+ virtual bool RecvDivertComplete() override;
+
+ nsresult SuspendChannel();
+ nsresult ResumeChannel();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ // if configured to use HTTP proxy for FTP, this can an an HTTP channel.
+ nsCOMPtr<nsIChannel> mChannel;
+
+ bool mIPCClosed;
+
+ nsCOMPtr<nsILoadContext> mLoadContext;
+
+ PBOverrideStatus mPBOverride;
+
+ // If OnStart/OnData/OnStop have been diverted from the child, forward them to
+ // this listener.
+ nsCOMPtr<nsIStreamListener> mDivertToListener;
+ // Set to the canceled status value if the main channel was canceled.
+ nsresult mStatus;
+ // Once set, no OnStart/OnData/OnStop calls should be accepted; conversely, it
+ // must be set when RecvDivertOnData/~DivertOnStop/~DivertComplete are
+ // received from the child channel.
+ bool mDivertingFromChild;
+ // Set if OnStart|StopRequest was called during a diversion from the child.
+ bool mDivertedOnStartRequest;
+
+ // Set if we successfully suspended the nsHttpChannel for diversion. Unset
+ // when we call ResumeForDiversion.
+ bool mSuspendedForDiversion;
+ RefPtr<mozilla::dom::TabParent> mTabParent;
+
+ RefPtr<ChannelEventQueue> mEventQ;
+
+ nsCString mErrorMsg;
+ bool mUseUTF8;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_FTPChannelParent_h
diff --git a/netwerk/protocol/ftp/PFTPChannel.ipdl b/netwerk/protocol/ftp/PFTPChannel.ipdl
new file mode 100644
index 000000000..55721bdbe
--- /dev/null
+++ b/netwerk/protocol/ftp/PFTPChannel.ipdl
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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 protocol PNecko;
+include InputStreamParams;
+include URIParams;
+
+//FIXME: bug #792908 (NeckoChannelParams already included by PNecko)
+include NeckoChannelParams;
+include protocol PBlob; //FIXME: bug #792908
+
+using PRTime from "prtime.h";
+
+namespace mozilla {
+namespace net {
+
+async protocol PFTPChannel
+{
+ manager PNecko;
+
+parent:
+ // Note: channels are opened during construction, so no open method here:
+ // see PNecko.ipdl
+
+ async __delete__();
+
+ async Cancel(nsresult status);
+ async Suspend();
+ async Resume();
+
+ // Divert OnDataAvailable to the parent.
+ async DivertOnDataAvailable(nsCString data,
+ uint64_t offset,
+ uint32_t count);
+
+ // Divert OnStopRequest to the parent.
+ async DivertOnStopRequest(nsresult statusCode);
+
+ // Child has no more events/messages to divert to the parent.
+ async DivertComplete();
+
+child:
+ async OnStartRequest(nsresult aChannelStatus,
+ int64_t aContentLength,
+ nsCString aContentType,
+ PRTime aLastModified,
+ nsCString aEntityID,
+ URIParams aURI);
+ async OnDataAvailable(nsresult channelStatus,
+ nsCString data,
+ uint64_t offset,
+ uint32_t count);
+ async OnStopRequest(nsresult channelStatus,
+ nsCString aErrorMsg,
+ bool aUseUTF8);
+ async FailedAsyncOpen(nsresult statusCode);
+
+ // Parent has been suspended for diversion; no more events to be enqueued.
+ async FlushedForDiversion();
+
+ // Child should resume processing the ChannelEventQueue, i.e. diverting any
+ // OnDataAvailable and OnStopRequest messages in the queue back to the parent.
+ async DivertMessages();
+
+ async DeleteSelf();
+};
+
+} // namespace net
+} // namespace mozilla
+
diff --git a/netwerk/protocol/ftp/doc/rfc959.txt b/netwerk/protocol/ftp/doc/rfc959.txt
new file mode 100644
index 000000000..5c9f11af5
--- /dev/null
+++ b/netwerk/protocol/ftp/doc/rfc959.txt
@@ -0,0 +1,3933 @@
+
+
+Network Working Group J. Postel
+Request for Comments: 959 J. Reynolds
+ ISI
+Obsoletes RFC: 765 (IEN 149) October 1985
+
+ FILE TRANSFER PROTOCOL (FTP)
+
+
+Status of this Memo
+
+ This memo is the official specification of the File Transfer
+ Protocol (FTP). Distribution of this memo is unlimited.
+
+ The following new optional commands are included in this edition of
+ the specification:
+
+ CDUP (Change to Parent Directory), SMNT (Structure Mount), STOU
+ (Store Unique), RMD (Remove Directory), MKD (Make Directory), PWD
+ (Print Directory), and SYST (System).
+
+ Note that this specification is compatible with the previous edition.
+
+1. INTRODUCTION
+
+ The objectives of FTP are 1) to promote sharing of files (computer
+ programs and/or data), 2) to encourage indirect or implicit (via
+ programs) use of remote computers, 3) to shield a user from
+ variations in file storage systems among hosts, and 4) to transfer
+ data reliably and efficiently. FTP, though usable directly by a user
+ at a terminal, is designed mainly for use by programs.
+
+ The attempt in this specification is to satisfy the diverse needs of
+ users of maxi-hosts, mini-hosts, personal workstations, and TACs,
+ with a simple, and easily implemented protocol design.
+
+ This paper assumes knowledge of the Transmission Control Protocol
+ (TCP) [2] and the Telnet Protocol [3]. These documents are contained
+ in the ARPA-Internet protocol handbook [1].
+
+2. OVERVIEW
+
+ In this section, the history, the terminology, and the FTP model are
+ discussed. The terms defined in this section are only those that
+ have special significance in FTP. Some of the terminology is very
+ specific to the FTP model; some readers may wish to turn to the
+ section on the FTP model while reviewing the terminology.
+
+
+
+
+
+
+
+Postel & Reynolds [Page 1]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 2.1. HISTORY
+
+ FTP has had a long evolution over the years. Appendix III is a
+ chronological compilation of Request for Comments documents
+ relating to FTP. These include the first proposed file transfer
+ mechanisms in 1971 that were developed for implementation on hosts
+ at M.I.T. (RFC 114), plus comments and discussion in RFC 141.
+
+ RFC 172 provided a user-level oriented protocol for file transfer
+ between host computers (including terminal IMPs). A revision of
+ this as RFC 265, restated FTP for additional review, while RFC 281
+ suggested further changes. The use of a "Set Data Type"
+ transaction was proposed in RFC 294 in January 1982.
+
+ RFC 354 obsoleted RFCs 264 and 265. The File Transfer Protocol
+ was now defined as a protocol for file transfer between HOSTs on
+ the ARPANET, with the primary function of FTP defined as
+ transfering files efficiently and reliably among hosts and
+ allowing the convenient use of remote file storage capabilities.
+ RFC 385 further commented on errors, emphasis points, and
+ additions to the protocol, while RFC 414 provided a status report
+ on the working server and user FTPs. RFC 430, issued in 1973,
+ (among other RFCs too numerous to mention) presented further
+ comments on FTP. Finally, an "official" FTP document was
+ published as RFC 454.
+
+ By July 1973, considerable changes from the last versions of FTP
+ were made, but the general structure remained the same. RFC 542
+ was published as a new "official" specification to reflect these
+ changes. However, many implementations based on the older
+ specification were not updated.
+
+ In 1974, RFCs 607 and 614 continued comments on FTP. RFC 624
+ proposed further design changes and minor modifications. In 1975,
+ RFC 686 entitled, "Leaving Well Enough Alone", discussed the
+ differences between all of the early and later versions of FTP.
+ RFC 691 presented a minor revision of RFC 686, regarding the
+ subject of print files.
+
+ Motivated by the transition from the NCP to the TCP as the
+ underlying protocol, a phoenix was born out of all of the above
+ efforts in RFC 765 as the specification of FTP for use on TCP.
+
+ This current edition of the FTP specification is intended to
+ correct some minor documentation errors, to improve the
+ explanation of some protocol features, and to add some new
+ optional commands.
+
+
+Postel & Reynolds [Page 2]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ In particular, the following new optional commands are included in
+ this edition of the specification:
+
+ CDUP - Change to Parent Directory
+
+ SMNT - Structure Mount
+
+ STOU - Store Unique
+
+ RMD - Remove Directory
+
+ MKD - Make Directory
+
+ PWD - Print Directory
+
+ SYST - System
+
+ This specification is compatible with the previous edition. A
+ program implemented in conformance to the previous specification
+ should automatically be in conformance to this specification.
+
+ 2.2. TERMINOLOGY
+
+ ASCII
+
+ The ASCII character set is as defined in the ARPA-Internet
+ Protocol Handbook. In FTP, ASCII characters are defined to be
+ the lower half of an eight-bit code set (i.e., the most
+ significant bit is zero).
+
+ access controls
+
+ Access controls define users' access privileges to the use of a
+ system, and to the files in that system. Access controls are
+ necessary to prevent unauthorized or accidental use of files.
+ It is the prerogative of a server-FTP process to invoke access
+ controls.
+
+ byte size
+
+ There are two byte sizes of interest in FTP: the logical byte
+ size of the file, and the transfer byte size used for the
+ transmission of the data. The transfer byte size is always 8
+ bits. The transfer byte size is not necessarily the byte size
+ in which data is to be stored in a system, nor the logical byte
+ size for interpretation of the structure of the data.
+
+
+
+Postel & Reynolds [Page 3]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ control connection
+
+ The communication path between the USER-PI and SERVER-PI for
+ the exchange of commands and replies. This connection follows
+ the Telnet Protocol.
+
+ data connection
+
+ A full duplex connection over which data is transferred, in a
+ specified mode and type. The data transferred may be a part of
+ a file, an entire file or a number of files. The path may be
+ between a server-DTP and a user-DTP, or between two
+ server-DTPs.
+
+ data port
+
+ The passive data transfer process "listens" on the data port
+ for a connection from the active transfer process in order to
+ open the data connection.
+
+ DTP
+
+ The data transfer process establishes and manages the data
+ connection. The DTP can be passive or active.
+
+ End-of-Line
+
+ The end-of-line sequence defines the separation of printing
+ lines. The sequence is Carriage Return, followed by Line Feed.
+
+ EOF
+
+ The end-of-file condition that defines the end of a file being
+ transferred.
+
+ EOR
+
+ The end-of-record condition that defines the end of a record
+ being transferred.
+
+ error recovery
+
+ A procedure that allows a user to recover from certain errors
+ such as failure of either host system or transfer process. In
+ FTP, error recovery may involve restarting a file transfer at a
+ given checkpoint.
+
+
+
+Postel & Reynolds [Page 4]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ FTP commands
+
+ A set of commands that comprise the control information flowing
+ from the user-FTP to the server-FTP process.
+
+ file
+
+ An ordered set of computer data (including programs), of
+ arbitrary length, uniquely identified by a pathname.
+
+ mode
+
+ The mode in which data is to be transferred via the data
+ connection. The mode defines the data format during transfer
+ including EOR and EOF. The transfer modes defined in FTP are
+ described in the Section on Transmission Modes.
+
+ NVT
+
+ The Network Virtual Terminal as defined in the Telnet Protocol.
+
+ NVFS
+
+ The Network Virtual File System. A concept which defines a
+ standard network file system with standard commands and
+ pathname conventions.
+
+ page
+
+ A file may be structured as a set of independent parts called
+ pages. FTP supports the transmission of discontinuous files as
+ independent indexed pages.
+
+ pathname
+
+ Pathname is defined to be the character string which must be
+ input to a file system by a user in order to identify a file.
+ Pathname normally contains device and/or directory names, and
+ file name specification. FTP does not yet specify a standard
+ pathname convention. Each user must follow the file naming
+ conventions of the file systems involved in the transfer.
+
+ PI
+
+ The protocol interpreter. The user and server sides of the
+ protocol have distinct roles implemented in a user-PI and a
+ server-PI.
+
+
+Postel & Reynolds [Page 5]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ record
+
+ A sequential file may be structured as a number of contiguous
+ parts called records. Record structures are supported by FTP
+ but a file need not have record structure.
+
+ reply
+
+ A reply is an acknowledgment (positive or negative) sent from
+ server to user via the control connection in response to FTP
+ commands. The general form of a reply is a completion code
+ (including error codes) followed by a text string. The codes
+ are for use by programs and the text is usually intended for
+ human users.
+
+ server-DTP
+
+ The data transfer process, in its normal "active" state,
+ establishes the data connection with the "listening" data port.
+ It sets up parameters for transfer and storage, and transfers
+ data on command from its PI. The DTP can be placed in a
+ "passive" state to listen for, rather than initiate a
+ connection on the data port.
+
+ server-FTP process
+
+ A process or set of processes which perform the function of
+ file transfer in cooperation with a user-FTP process and,
+ possibly, another server. The functions consist of a protocol
+ interpreter (PI) and a data transfer process (DTP).
+
+ server-PI
+
+ The server protocol interpreter "listens" on Port L for a
+ connection from a user-PI and establishes a control
+ communication connection. It receives standard FTP commands
+ from the user-PI, sends replies, and governs the server-DTP.
+
+ type
+
+ The data representation type used for data transfer and
+ storage. Type implies certain transformations between the time
+ of data storage and data transfer. The representation types
+ defined in FTP are described in the Section on Establishing
+ Data Connections.
+
+
+
+
+Postel & Reynolds [Page 6]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ user
+
+ A person or a process on behalf of a person wishing to obtain
+ file transfer service. The human user may interact directly
+ with a server-FTP process, but use of a user-FTP process is
+ preferred since the protocol design is weighted towards
+ automata.
+
+ user-DTP
+
+ The data transfer process "listens" on the data port for a
+ connection from a server-FTP process. If two servers are
+ transferring data between them, the user-DTP is inactive.
+
+ user-FTP process
+
+ A set of functions including a protocol interpreter, a data
+ transfer process and a user interface which together perform
+ the function of file transfer in cooperation with one or more
+ server-FTP processes. The user interface allows a local
+ language to be used in the command-reply dialogue with the
+ user.
+
+ user-PI
+
+ The user protocol interpreter initiates the control connection
+ from its port U to the server-FTP process, initiates FTP
+ commands, and governs the user-DTP if that process is part of
+ the file transfer.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 7]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 2.3. THE FTP MODEL
+
+ With the above definitions in mind, the following model (shown in
+ Figure 1) may be diagrammed for an FTP service.
+
+ -------------
+ |/---------\|
+ || User || --------
+ ||Interface|<--->| User |
+ |\----^----/| --------
+ ---------- | | |
+ |/------\| FTP Commands |/----V----\|
+ ||Server|<---------------->| User ||
+ || PI || FTP Replies || PI ||
+ |\--^---/| |\----^----/|
+ | | | | | |
+ -------- |/--V---\| Data |/----V----\| --------
+ | File |<--->|Server|<---------------->| User |<--->| File |
+ |System| || DTP || Connection || DTP || |System|
+ -------- |\------/| |\---------/| --------
+ ---------- -------------
+
+ Server-FTP USER-FTP
+
+ NOTES: 1. The data connection may be used in either direction.
+ 2. The data connection need not exist all of the time.
+
+ Figure 1 Model for FTP Use
+
+ In the model described in Figure 1, the user-protocol interpreter
+ initiates the control connection. The control connection follows
+ the Telnet protocol. At the initiation of the user, standard FTP
+ commands are generated by the user-PI and transmitted to the
+ server process via the control connection. (The user may
+ establish a direct control connection to the server-FTP, from a
+ TAC terminal for example, and generate standard FTP commands
+ independently, bypassing the user-FTP process.) Standard replies
+ are sent from the server-PI to the user-PI over the control
+ connection in response to the commands.
+
+ The FTP commands specify the parameters for the data connection
+ (data port, transfer mode, representation type, and structure) and
+ the nature of file system operation (store, retrieve, append,
+ delete, etc.). The user-DTP or its designate should "listen" on
+ the specified data port, and the server initiate the data
+ connection and data transfer in accordance with the specified
+ parameters. It should be noted that the data port need not be in
+
+
+Postel & Reynolds [Page 8]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ the same host that initiates the FTP commands via the control
+ connection, but the user or the user-FTP process must ensure a
+ "listen" on the specified data port. It ought to also be noted
+ that the data connection may be used for simultaneous sending and
+ receiving.
+
+ In another situation a user might wish to transfer files between
+ two hosts, neither of which is a local host. The user sets up
+ control connections to the two servers and then arranges for a
+ data connection between them. In this manner, control information
+ is passed to the user-PI but data is transferred between the
+ server data transfer processes. Following is a model of this
+ server-server interaction.
+
+
+ Control ------------ Control
+ ---------->| User-FTP |<-----------
+ | | User-PI | |
+ | | "C" | |
+ V ------------ V
+ -------------- --------------
+ | Server-FTP | Data Connection | Server-FTP |
+ | "A" |<---------------------->| "B" |
+ -------------- Port (A) Port (B) --------------
+
+
+ Figure 2
+
+ The protocol requires that the control connections be open while
+ data transfer is in progress. It is the responsibility of the
+ user to request the closing of the control connections when
+ finished using the FTP service, while it is the server who takes
+ the action. The server may abort data transfer if the control
+ connections are closed without command.
+
+ The Relationship between FTP and Telnet:
+
+ The FTP uses the Telnet protocol on the control connection.
+ This can be achieved in two ways: first, the user-PI or the
+ server-PI may implement the rules of the Telnet Protocol
+ directly in their own procedures; or, second, the user-PI or
+ the server-PI may make use of the existing Telnet module in the
+ system.
+
+ Ease of implementaion, sharing code, and modular programming
+ argue for the second approach. Efficiency and independence
+
+
+
+Postel & Reynolds [Page 9]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ argue for the first approach. In practice, FTP relies on very
+ little of the Telnet Protocol, so the first approach does not
+ necessarily involve a large amount of code.
+
+3. DATA TRANSFER FUNCTIONS
+
+ Files are transferred only via the data connection. The control
+ connection is used for the transfer of commands, which describe the
+ functions to be performed, and the replies to these commands (see the
+ Section on FTP Replies). Several commands are concerned with the
+ transfer of data between hosts. These data transfer commands include
+ the MODE command which specify how the bits of the data are to be
+ transmitted, and the STRUcture and TYPE commands, which are used to
+ define the way in which the data are to be represented. The
+ transmission and representation are basically independent but the
+ "Stream" transmission mode is dependent on the file structure
+ attribute and if "Compressed" transmission mode is used, the nature
+ of the filler byte depends on the representation type.
+
+ 3.1. DATA REPRESENTATION AND STORAGE
+
+ Data is transferred from a storage device in the sending host to a
+ storage device in the receiving host. Often it is necessary to
+ perform certain transformations on the data because data storage
+ representations in the two systems are different. For example,
+ NVT-ASCII has different data storage representations in different
+ systems. DEC TOPS-20s's generally store NVT-ASCII as five 7-bit
+ ASCII characters, left-justified in a 36-bit word. IBM Mainframe's
+ store NVT-ASCII as 8-bit EBCDIC codes. Multics stores NVT-ASCII
+ as four 9-bit characters in a 36-bit word. It is desirable to
+ convert characters into the standard NVT-ASCII representation when
+ transmitting text between dissimilar systems. The sending and
+ receiving sites would have to perform the necessary
+ transformations between the standard representation and their
+ internal representations.
+
+ A different problem in representation arises when transmitting
+ binary data (not character codes) between host systems with
+ different word lengths. It is not always clear how the sender
+ should send data, and the receiver store it. For example, when
+ transmitting 32-bit bytes from a 32-bit word-length system to a
+ 36-bit word-length system, it may be desirable (for reasons of
+ efficiency and usefulness) to store the 32-bit bytes
+ right-justified in a 36-bit word in the latter system. In any
+ case, the user should have the option of specifying data
+ representation and transformation functions. It should be noted
+
+
+
+Postel & Reynolds [Page 10]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ that FTP provides for very limited data type representations.
+ Transformations desired beyond this limited capability should be
+ performed by the user directly.
+
+ 3.1.1. DATA TYPES
+
+ Data representations are handled in FTP by a user specifying a
+ representation type. This type may implicitly (as in ASCII or
+ EBCDIC) or explicitly (as in Local byte) define a byte size for
+ interpretation which is referred to as the "logical byte size."
+ Note that this has nothing to do with the byte size used for
+ transmission over the data connection, called the "transfer
+ byte size", and the two should not be confused. For example,
+ NVT-ASCII has a logical byte size of 8 bits. If the type is
+ Local byte, then the TYPE command has an obligatory second
+ parameter specifying the logical byte size. The transfer byte
+ size is always 8 bits.
+
+ 3.1.1.1. ASCII TYPE
+
+ This is the default type and must be accepted by all FTP
+ implementations. It is intended primarily for the transfer
+ of text files, except when both hosts would find the EBCDIC
+ type more convenient.
+
+ The sender converts the data from an internal character
+ representation to the standard 8-bit NVT-ASCII
+ representation (see the Telnet specification). The receiver
+ will convert the data from the standard form to his own
+ internal form.
+
+ In accordance with the NVT standard, the <CRLF> sequence
+ should be used where necessary to denote the end of a line
+ of text. (See the discussion of file structure at the end
+ of the Section on Data Representation and Storage.)
+
+ Using the standard NVT-ASCII representation means that data
+ must be interpreted as 8-bit bytes.
+
+ The Format parameter for ASCII and EBCDIC types is discussed
+ below.
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 11]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 3.1.1.2. EBCDIC TYPE
+
+ This type is intended for efficient transfer between hosts
+ which use EBCDIC for their internal character
+ representation.
+
+ For transmission, the data are represented as 8-bit EBCDIC
+ characters. The character code is the only difference
+ between the functional specifications of EBCDIC and ASCII
+ types.
+
+ End-of-line (as opposed to end-of-record--see the discussion
+ of structure) will probably be rarely used with EBCDIC type
+ for purposes of denoting structure, but where it is
+ necessary the <NL> character should be used.
+
+ 3.1.1.3. IMAGE TYPE
+
+ The data are sent as contiguous bits which, for transfer,
+ are packed into the 8-bit transfer bytes. The receiving
+ site must store the data as contiguous bits. The structure
+ of the storage system might necessitate the padding of the
+ file (or of each record, for a record-structured file) to
+ some convenient boundary (byte, word or block). This
+ padding, which must be all zeros, may occur only at the end
+ of the file (or at the end of each record) and there must be
+ a way of identifying the padding bits so that they may be
+ stripped off if the file is retrieved. The padding
+ transformation should be well publicized to enable a user to
+ process a file at the storage site.
+
+ Image type is intended for the efficient storage and
+ retrieval of files and for the transfer of binary data. It
+ is recommended that this type be accepted by all FTP
+ implementations.
+
+ 3.1.1.4. LOCAL TYPE
+
+ The data is transferred in logical bytes of the size
+ specified by the obligatory second parameter, Byte size.
+ The value of Byte size must be a decimal integer; there is
+ no default value. The logical byte size is not necessarily
+ the same as the transfer byte size. If there is a
+ difference in byte sizes, then the logical bytes should be
+ packed contiguously, disregarding transfer byte boundaries
+ and with any necessary padding at the end.
+
+
+
+Postel & Reynolds [Page 12]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ When the data reaches the receiving host, it will be
+ transformed in a manner dependent on the logical byte size
+ and the particular host. This transformation must be
+ invertible (i.e., an identical file can be retrieved if the
+ same parameters are used) and should be well publicized by
+ the FTP implementors.
+
+ For example, a user sending 36-bit floating-point numbers to
+ a host with a 32-bit word could send that data as Local byte
+ with a logical byte size of 36. The receiving host would
+ then be expected to store the logical bytes so that they
+ could be easily manipulated; in this example putting the
+ 36-bit logical bytes into 64-bit double words should
+ suffice.
+
+ In another example, a pair of hosts with a 36-bit word size
+ may send data to one another in words by using TYPE L 36.
+ The data would be sent in the 8-bit transmission bytes
+ packed so that 9 transmission bytes carried two host words.
+
+ 3.1.1.5. FORMAT CONTROL
+
+ The types ASCII and EBCDIC also take a second (optional)
+ parameter; this is to indicate what kind of vertical format
+ control, if any, is associated with a file. The following
+ data representation types are defined in FTP:
+
+ A character file may be transferred to a host for one of
+ three purposes: for printing, for storage and later
+ retrieval, or for processing. If a file is sent for
+ printing, the receiving host must know how the vertical
+ format control is represented. In the second case, it must
+ be possible to store a file at a host and then retrieve it
+ later in exactly the same form. Finally, it should be
+ possible to move a file from one host to another and process
+ the file at the second host without undue trouble. A single
+ ASCII or EBCDIC format does not satisfy all these
+ conditions. Therefore, these types have a second parameter
+ specifying one of the following three formats:
+
+ 3.1.1.5.1. NON PRINT
+
+ This is the default format to be used if the second
+ (format) parameter is omitted. Non-print format must be
+ accepted by all FTP implementations.
+
+
+
+
+Postel & Reynolds [Page 13]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ The file need contain no vertical format information. If
+ it is passed to a printer process, this process may
+ assume standard values for spacing and margins.
+
+ Normally, this format will be used with files destined
+ for processing or just storage.
+
+ 3.1.1.5.2. TELNET FORMAT CONTROLS
+
+ The file contains ASCII/EBCDIC vertical format controls
+ (i.e., <CR>, <LF>, <NL>, <VT>, <FF>) which the printer
+ process will interpret appropriately. <CRLF>, in exactly
+ this sequence, also denotes end-of-line.
+
+ 3.1.1.5.2. CARRIAGE CONTROL (ASA)
+
+ The file contains ASA (FORTRAN) vertical format control
+ characters. (See RFC 740 Appendix C; and Communications
+ of the ACM, Vol. 7, No. 10, p. 606, October 1964.) In a
+ line or a record formatted according to the ASA Standard,
+ the first character is not to be printed. Instead, it
+ should be used to determine the vertical movement of the
+ paper which should take place before the rest of the
+ record is printed.
+
+ The ASA Standard specifies the following control
+ characters:
+
+ Character Vertical Spacing
+
+ blank Move paper up one line
+ 0 Move paper up two lines
+ 1 Move paper to top of next page
+ + No movement, i.e., overprint
+
+ Clearly there must be some way for a printer process to
+ distinguish the end of the structural entity. If a file
+ has record structure (see below) this is no problem;
+ records will be explicitly marked during transfer and
+ storage. If the file has no record structure, the <CRLF>
+ end-of-line sequence is used to separate printing lines,
+ but these format effectors are overridden by the ASA
+ controls.
+
+
+
+
+
+
+Postel & Reynolds [Page 14]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 3.1.2. DATA STRUCTURES
+
+ In addition to different representation types, FTP allows the
+ structure of a file to be specified. Three file structures are
+ defined in FTP:
+
+ file-structure, where there is no internal structure and
+ the file is considered to be a
+ continuous sequence of data bytes,
+
+ record-structure, where the file is made up of sequential
+ records,
+
+ and page-structure, where the file is made up of independent
+ indexed pages.
+
+ File-structure is the default to be assumed if the STRUcture
+ command has not been used but both file and record structures
+ must be accepted for "text" files (i.e., files with TYPE ASCII
+ or EBCDIC) by all FTP implementations. The structure of a file
+ will affect both the transfer mode of a file (see the Section
+ on Transmission Modes) and the interpretation and storage of
+ the file.
+
+ The "natural" structure of a file will depend on which host
+ stores the file. A source-code file will usually be stored on
+ an IBM Mainframe in fixed length records but on a DEC TOPS-20
+ as a stream of characters partitioned into lines, for example
+ by <CRLF>. If the transfer of files between such disparate
+ sites is to be useful, there must be some way for one site to
+ recognize the other's assumptions about the file.
+
+ With some sites being naturally file-oriented and others
+ naturally record-oriented there may be problems if a file with
+ one structure is sent to a host oriented to the other. If a
+ text file is sent with record-structure to a host which is file
+ oriented, then that host should apply an internal
+ transformation to the file based on the record structure.
+ Obviously, this transformation should be useful, but it must
+ also be invertible so that an identical file may be retrieved
+ using record structure.
+
+ In the case of a file being sent with file-structure to a
+ record-oriented host, there exists the question of what
+ criteria the host should use to divide the file into records
+ which can be processed locally. If this division is necessary,
+ the FTP implementation should use the end-of-line sequence,
+
+
+Postel & Reynolds [Page 15]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ <CRLF> for ASCII, or <NL> for EBCDIC text files, as the
+ delimiter. If an FTP implementation adopts this technique, it
+ must be prepared to reverse the transformation if the file is
+ retrieved with file-structure.
+
+ 3.1.2.1. FILE STRUCTURE
+
+ File structure is the default to be assumed if the STRUcture
+ command has not been used.
+
+ In file-structure there is no internal structure and the
+ file is considered to be a continuous sequence of data
+ bytes.
+
+ 3.1.2.2. RECORD STRUCTURE
+
+ Record structures must be accepted for "text" files (i.e.,
+ files with TYPE ASCII or EBCDIC) by all FTP implementations.
+
+ In record-structure the file is made up of sequential
+ records.
+
+ 3.1.2.3. PAGE STRUCTURE
+
+ To transmit files that are discontinuous, FTP defines a page
+ structure. Files of this type are sometimes known as
+ "random access files" or even as "holey files". In these
+ files there is sometimes other information associated with
+ the file as a whole (e.g., a file descriptor), or with a
+ section of the file (e.g., page access controls), or both.
+ In FTP, the sections of the file are called pages.
+
+ To provide for various page sizes and associated
+ information, each page is sent with a page header. The page
+ header has the following defined fields:
+
+ Header Length
+
+ The number of logical bytes in the page header
+ including this byte. The minimum header length is 4.
+
+ Page Index
+
+ The logical page number of this section of the file.
+ This is not the transmission sequence number of this
+ page, but the index used to identify this page of the
+ file.
+
+
+Postel & Reynolds [Page 16]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ Data Length
+
+ The number of logical bytes in the page data. The
+ minimum data length is 0.
+
+ Page Type
+
+ The type of page this is. The following page types
+ are defined:
+
+ 0 = Last Page
+
+ This is used to indicate the end of a paged
+ structured transmission. The header length must
+ be 4, and the data length must be 0.
+
+ 1 = Simple Page
+
+ This is the normal type for simple paged files
+ with no page level associated control
+ information. The header length must be 4.
+
+ 2 = Descriptor Page
+
+ This type is used to transmit the descriptive
+ information for the file as a whole.
+
+ 3 = Access Controlled Page
+
+ This type includes an additional header field
+ for paged files with page level access control
+ information. The header length must be 5.
+
+ Optional Fields
+
+ Further header fields may be used to supply per page
+ control information, for example, per page access
+ control.
+
+ All fields are one logical byte in length. The logical byte
+ size is specified by the TYPE command. See Appendix I for
+ further details and a specific case at the page structure.
+
+ A note of caution about parameters: a file must be stored and
+ retrieved with the same parameters if the retrieved version is to
+
+
+
+
+Postel & Reynolds [Page 17]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ be identical to the version originally transmitted. Conversely,
+ FTP implementations must return a file identical to the original
+ if the parameters used to store and retrieve a file are the same.
+
+ 3.2. ESTABLISHING DATA CONNECTIONS
+
+ The mechanics of transferring data consists of setting up the data
+ connection to the appropriate ports and choosing the parameters
+ for transfer. Both the user and the server-DTPs have a default
+ data port. The user-process default data port is the same as the
+ control connection port (i.e., U). The server-process default
+ data port is the port adjacent to the control connection port
+ (i.e., L-1).
+
+ The transfer byte size is 8-bit bytes. This byte size is relevant
+ only for the actual transfer of the data; it has no bearing on
+ representation of the data within a host's file system.
+
+ The passive data transfer process (this may be a user-DTP or a
+ second server-DTP) shall "listen" on the data port prior to
+ sending a transfer request command. The FTP request command
+ determines the direction of the data transfer. The server, upon
+ receiving the transfer request, will initiate the data connection
+ to the port. When the connection is established, the data
+ transfer begins between DTP's, and the server-PI sends a
+ confirming reply to the user-PI.
+
+ Every FTP implementation must support the use of the default data
+ ports, and only the USER-PI can initiate a change to non-default
+ ports.
+
+ It is possible for the user to specify an alternate data port by
+ use of the PORT command. The user may want a file dumped on a TAC
+ line printer or retrieved from a third party host. In the latter
+ case, the user-PI sets up control connections with both
+ server-PI's. One server is then told (by an FTP command) to
+ "listen" for a connection which the other will initiate. The
+ user-PI sends one server-PI a PORT command indicating the data
+ port of the other. Finally, both are sent the appropriate
+ transfer commands. The exact sequence of commands and replies
+ sent between the user-controller and the servers is defined in the
+ Section on FTP Replies.
+
+ In general, it is the server's responsibility to maintain the data
+ connection--to initiate it and to close it. The exception to this
+
+
+
+
+Postel & Reynolds [Page 18]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ is when the user-DTP is sending the data in a transfer mode that
+ requires the connection to be closed to indicate EOF. The server
+ MUST close the data connection under the following conditions:
+
+ 1. The server has completed sending data in a transfer mode
+ that requires a close to indicate EOF.
+
+ 2. The server receives an ABORT command from the user.
+
+ 3. The port specification is changed by a command from the
+ user.
+
+ 4. The control connection is closed legally or otherwise.
+
+ 5. An irrecoverable error condition occurs.
+
+ Otherwise the close is a server option, the exercise of which the
+ server must indicate to the user-process by either a 250 or 226
+ reply only.
+
+ 3.3. DATA CONNECTION MANAGEMENT
+
+ Default Data Connection Ports: All FTP implementations must
+ support use of the default data connection ports, and only the
+ User-PI may initiate the use of non-default ports.
+
+ Negotiating Non-Default Data Ports: The User-PI may specify a
+ non-default user side data port with the PORT command. The
+ User-PI may request the server side to identify a non-default
+ server side data port with the PASV command. Since a connection
+ is defined by the pair of addresses, either of these actions is
+ enough to get a different data connection, still it is permitted
+ to do both commands to use new ports on both ends of the data
+ connection.
+
+ Reuse of the Data Connection: When using the stream mode of data
+ transfer the end of the file must be indicated by closing the
+ connection. This causes a problem if multiple files are to be
+ transfered in the session, due to need for TCP to hold the
+ connection record for a time out period to guarantee the reliable
+ communication. Thus the connection can not be reopened at once.
+
+ There are two solutions to this problem. The first is to
+ negotiate a non-default port. The second is to use another
+ transfer mode.
+
+ A comment on transfer modes. The stream transfer mode is
+
+
+Postel & Reynolds [Page 19]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ inherently unreliable, since one can not determine if the
+ connection closed prematurely or not. The other transfer modes
+ (Block, Compressed) do not close the connection to indicate the
+ end of file. They have enough FTP encoding that the data
+ connection can be parsed to determine the end of the file.
+ Thus using these modes one can leave the data connection open
+ for multiple file transfers.
+
+ 3.4. TRANSMISSION MODES
+
+ The next consideration in transferring data is choosing the
+ appropriate transmission mode. There are three modes: one which
+ formats the data and allows for restart procedures; one which also
+ compresses the data for efficient transfer; and one which passes
+ the data with little or no processing. In this last case the mode
+ interacts with the structure attribute to determine the type of
+ processing. In the compressed mode, the representation type
+ determines the filler byte.
+
+ All data transfers must be completed with an end-of-file (EOF)
+ which may be explicitly stated or implied by the closing of the
+ data connection. For files with record structure, all the
+ end-of-record markers (EOR) are explicit, including the final one.
+ For files transmitted in page structure a "last-page" page type is
+ used.
+
+ NOTE: In the rest of this section, byte means "transfer byte"
+ except where explicitly stated otherwise.
+
+ For the purpose of standardized transfer, the sending host will
+ translate its internal end of line or end of record denotation
+ into the representation prescribed by the transfer mode and file
+ structure, and the receiving host will perform the inverse
+ translation to its internal denotation. An IBM Mainframe record
+ count field may not be recognized at another host, so the
+ end-of-record information may be transferred as a two byte control
+ code in Stream mode or as a flagged bit in a Block or Compressed
+ mode descriptor. End-of-line in an ASCII or EBCDIC file with no
+ record structure should be indicated by <CRLF> or <NL>,
+ respectively. Since these transformations imply extra work for
+ some systems, identical systems transferring non-record structured
+ text files might wish to use a binary representation and stream
+ mode for the transfer.
+
+
+
+
+
+
+Postel & Reynolds [Page 20]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ The following transmission modes are defined in FTP:
+
+ 3.4.1. STREAM MODE
+
+ The data is transmitted as a stream of bytes. There is no
+ restriction on the representation type used; record structures
+ are allowed.
+
+ In a record structured file EOR and EOF will each be indicated
+ by a two-byte control code. The first byte of the control code
+ will be all ones, the escape character. The second byte will
+ have the low order bit on and zeros elsewhere for EOR and the
+ second low order bit on for EOF; that is, the byte will have
+ value 1 for EOR and value 2 for EOF. EOR and EOF may be
+ indicated together on the last byte transmitted by turning both
+ low order bits on (i.e., the value 3). If a byte of all ones
+ was intended to be sent as data, it should be repeated in the
+ second byte of the control code.
+
+ If the structure is a file structure, the EOF is indicated by
+ the sending host closing the data connection and all bytes are
+ data bytes.
+
+ 3.4.2. BLOCK MODE
+
+ The file is transmitted as a series of data blocks preceded by
+ one or more header bytes. The header bytes contain a count
+ field, and descriptor code. The count field indicates the
+ total length of the data block in bytes, thus marking the
+ beginning of the next data block (there are no filler bits).
+ The descriptor code defines: last block in the file (EOF) last
+ block in the record (EOR), restart marker (see the Section on
+ Error Recovery and Restart) or suspect data (i.e., the data
+ being transferred is suspected of errors and is not reliable).
+ This last code is NOT intended for error control within FTP.
+ It is motivated by the desire of sites exchanging certain types
+ of data (e.g., seismic or weather data) to send and receive all
+ the data despite local errors (such as "magnetic tape read
+ errors"), but to indicate in the transmission that certain
+ portions are suspect). Record structures are allowed in this
+ mode, and any representation type may be used.
+
+ The header consists of the three bytes. Of the 24 bits of
+ header information, the 16 low order bits shall represent byte
+ count, and the 8 high order bits shall represent descriptor
+ codes as shown below.
+
+
+
+Postel & Reynolds [Page 21]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ Block Header
+
+ +----------------+----------------+----------------+
+ | Descriptor | Byte Count |
+ | 8 bits | 16 bits |
+ +----------------+----------------+----------------+
+
+
+ The descriptor codes are indicated by bit flags in the
+ descriptor byte. Four codes have been assigned, where each
+ code number is the decimal value of the corresponding bit in
+ the byte.
+
+ Code Meaning
+
+ 128 End of data block is EOR
+ 64 End of data block is EOF
+ 32 Suspected errors in data block
+ 16 Data block is a restart marker
+
+ With this encoding, more than one descriptor coded condition
+ may exist for a particular block. As many bits as necessary
+ may be flagged.
+
+ The restart marker is embedded in the data stream as an
+ integral number of 8-bit bytes representing printable
+ characters in the language being used over the control
+ connection (e.g., default--NVT-ASCII). <SP> (Space, in the
+ appropriate language) must not be used WITHIN a restart marker.
+
+ For example, to transmit a six-character marker, the following
+ would be sent:
+
+ +--------+--------+--------+
+ |Descrptr| Byte count |
+ |code= 16| = 6 |
+ +--------+--------+--------+
+
+ +--------+--------+--------+
+ | Marker | Marker | Marker |
+ | 8 bits | 8 bits | 8 bits |
+ +--------+--------+--------+
+
+ +--------+--------+--------+
+ | Marker | Marker | Marker |
+ | 8 bits | 8 bits | 8 bits |
+ +--------+--------+--------+
+
+
+Postel & Reynolds [Page 22]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 3.4.3. COMPRESSED MODE
+
+ There are three kinds of information to be sent: regular data,
+ sent in a byte string; compressed data, consisting of
+ replications or filler; and control information, sent in a
+ two-byte escape sequence. If n>0 bytes (up to 127) of regular
+ data are sent, these n bytes are preceded by a byte with the
+ left-most bit set to 0 and the right-most 7 bits containing the
+ number n.
+
+ Byte string:
+
+ 1 7 8 8
+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
+ |0| n | | d(1) | ... | d(n) |
+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
+ ^ ^
+ |---n bytes---|
+ of data
+
+ String of n data bytes d(1),..., d(n)
+ Count n must be positive.
+
+ To compress a string of n replications of the data byte d, the
+ following 2 bytes are sent:
+
+ Replicated Byte:
+
+ 2 6 8
+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
+ |1 0| n | | d |
+ +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+
+
+ A string of n filler bytes can be compressed into a single
+ byte, where the filler byte varies with the representation
+ type. If the type is ASCII or EBCDIC the filler byte is <SP>
+ (Space, ASCII code 32, EBCDIC code 64). If the type is Image
+ or Local byte the filler is a zero byte.
+
+ Filler String:
+
+ 2 6
+ +-+-+-+-+-+-+-+-+
+ |1 1| n |
+ +-+-+-+-+-+-+-+-+
+
+ The escape sequence is a double byte, the first of which is the
+
+
+Postel & Reynolds [Page 23]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ escape byte (all zeros) and the second of which contains
+ descriptor codes as defined in Block mode. The descriptor
+ codes have the same meaning as in Block mode and apply to the
+ succeeding string of bytes.
+
+ Compressed mode is useful for obtaining increased bandwidth on
+ very large network transmissions at a little extra CPU cost.
+ It can be most effectively used to reduce the size of printer
+ files such as those generated by RJE hosts.
+
+ 3.5. ERROR RECOVERY AND RESTART
+
+ There is no provision for detecting bits lost or scrambled in data
+ transfer; this level of error control is handled by the TCP.
+ However, a restart procedure is provided to protect users from
+ gross system failures (including failures of a host, an
+ FTP-process, or the underlying network).
+
+ The restart procedure is defined only for the block and compressed
+ modes of data transfer. It requires the sender of data to insert
+ a special marker code in the data stream with some marker
+ information. The marker information has meaning only to the
+ sender, but must consist of printable characters in the default or
+ negotiated language of the control connection (ASCII or EBCDIC).
+ The marker could represent a bit-count, a record-count, or any
+ other information by which a system may identify a data
+ checkpoint. The receiver of data, if it implements the restart
+ procedure, would then mark the corresponding position of this
+ marker in the receiving system, and return this information to the
+ user.
+
+ In the event of a system failure, the user can restart the data
+ transfer by identifying the marker point with the FTP restart
+ procedure. The following example illustrates the use of the
+ restart procedure.
+
+ The sender of the data inserts an appropriate marker block in the
+ data stream at a convenient point. The receiving host marks the
+ corresponding data point in its file system and conveys the last
+ known sender and receiver marker information to the user, either
+ directly or over the control connection in a 110 reply (depending
+ on who is the sender). In the event of a system failure, the user
+ or controller process restarts the server at the last server
+ marker by sending a restart command with server's marker code as
+ its argument. The restart command is transmitted over the control
+
+
+
+
+Postel & Reynolds [Page 24]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ connection and is immediately followed by the command (such as
+ RETR, STOR or LIST) which was being executed when the system
+ failure occurred.
+
+4. FILE TRANSFER FUNCTIONS
+
+ The communication channel from the user-PI to the server-PI is
+ established as a TCP connection from the user to the standard server
+ port. The user protocol interpreter is responsible for sending FTP
+ commands and interpreting the replies received; the server-PI
+ interprets commands, sends replies and directs its DTP to set up the
+ data connection and transfer the data. If the second party to the
+ data transfer (the passive transfer process) is the user-DTP, then it
+ is governed through the internal protocol of the user-FTP host; if it
+ is a second server-DTP, then it is governed by its PI on command from
+ the user-PI. The FTP replies are discussed in the next section. In
+ the description of a few of the commands in this section, it is
+ helpful to be explicit about the possible replies.
+
+ 4.1. FTP COMMANDS
+
+ 4.1.1. ACCESS CONTROL COMMANDS
+
+ The following commands specify access control identifiers
+ (command codes are shown in parentheses).
+
+ USER NAME (USER)
+
+ The argument field is a Telnet string identifying the user.
+ The user identification is that which is required by the
+ server for access to its file system. This command will
+ normally be the first command transmitted by the user after
+ the control connections are made (some servers may require
+ this). Additional identification information in the form of
+ a password and/or an account command may also be required by
+ some servers. Servers may allow a new USER command to be
+ entered at any point in order to change the access control
+ and/or accounting information. This has the effect of
+ flushing any user, password, and account information already
+ supplied and beginning the login sequence again. All
+ transfer parameters are unchanged and any file transfer in
+ progress is completed under the old access control
+ parameters.
+
+
+
+
+
+
+Postel & Reynolds [Page 25]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ PASSWORD (PASS)
+
+ The argument field is a Telnet string specifying the user's
+ password. This command must be immediately preceded by the
+ user name command, and, for some sites, completes the user's
+ identification for access control. Since password
+ information is quite sensitive, it is desirable in general
+ to "mask" it or suppress typeout. It appears that the
+ server has no foolproof way to achieve this. It is
+ therefore the responsibility of the user-FTP process to hide
+ the sensitive password information.
+
+ ACCOUNT (ACCT)
+
+ The argument field is a Telnet string identifying the user's
+ account. The command is not necessarily related to the USER
+ command, as some sites may require an account for login and
+ others only for specific access, such as storing files. In
+ the latter case the command may arrive at any time.
+
+ There are reply codes to differentiate these cases for the
+ automation: when account information is required for login,
+ the response to a successful PASSword command is reply code
+ 332. On the other hand, if account information is NOT
+ required for login, the reply to a successful PASSword
+ command is 230; and if the account information is needed for
+ a command issued later in the dialogue, the server should
+ return a 332 or 532 reply depending on whether it stores
+ (pending receipt of the ACCounT command) or discards the
+ command, respectively.
+
+ CHANGE WORKING DIRECTORY (CWD)
+
+ This command allows the user to work with a different
+ directory or dataset for file storage or retrieval without
+ altering his login or accounting information. Transfer
+ parameters are similarly unchanged. The argument is a
+ pathname specifying a directory or other system dependent
+ file group designator.
+
+ CHANGE TO PARENT DIRECTORY (CDUP)
+
+ This command is a special case of CWD, and is included to
+ simplify the implementation of programs for transferring
+ directory trees between operating systems having different
+
+
+
+
+Postel & Reynolds [Page 26]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ syntaxes for naming the parent directory. The reply codes
+ shall be identical to the reply codes of CWD. See
+ Appendix II for further details.
+
+ STRUCTURE MOUNT (SMNT)
+
+ This command allows the user to mount a different file
+ system data structure without altering his login or
+ accounting information. Transfer parameters are similarly
+ unchanged. The argument is a pathname specifying a
+ directory or other system dependent file group designator.
+
+ REINITIALIZE (REIN)
+
+ This command terminates a USER, flushing all I/O and account
+ information, except to allow any transfer in progress to be
+ completed. All parameters are reset to the default settings
+ and the control connection is left open. This is identical
+ to the state in which a user finds himself immediately after
+ the control connection is opened. A USER command may be
+ expected to follow.
+
+ LOGOUT (QUIT)
+
+ This command terminates a USER and if file transfer is not
+ in progress, the server closes the control connection. If
+ file transfer is in progress, the connection will remain
+ open for result response and the server will then close it.
+ If the user-process is transferring files for several USERs
+ but does not wish to close and then reopen connections for
+ each, then the REIN command should be used instead of QUIT.
+
+ An unexpected close on the control connection will cause the
+ server to take the effective action of an abort (ABOR) and a
+ logout (QUIT).
+
+ 4.1.2. TRANSFER PARAMETER COMMANDS
+
+ All data transfer parameters have default values, and the
+ commands specifying data transfer parameters are required only
+ if the default parameter values are to be changed. The default
+ value is the last specified value, or if no value has been
+ specified, the standard default value is as stated here. This
+ implies that the server must "remember" the applicable default
+ values. The commands may be in any order except that they must
+ precede the FTP service request. The following commands
+ specify data transfer parameters:
+
+
+Postel & Reynolds [Page 27]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ DATA PORT (PORT)
+
+ The argument is a HOST-PORT specification for the data port
+ to be used in data connection. There are defaults for both
+ the user and server data ports, and under normal
+ circumstances this command and its reply are not needed. If
+ this command is used, the argument is the concatenation of a
+ 32-bit internet host address and a 16-bit TCP port address.
+ This address information is broken into 8-bit fields and the
+ value of each field is transmitted as a decimal number (in
+ character string representation). The fields are separated
+ by commas. A port command would be:
+
+ PORT h1,h2,h3,h4,p1,p2
+
+ where h1 is the high order 8 bits of the internet host
+ address.
+
+ PASSIVE (PASV)
+
+ This command requests the server-DTP to "listen" on a data
+ port (which is not its default data port) and to wait for a
+ connection rather than initiate one upon receipt of a
+ transfer command. The response to this command includes the
+ host and port address this server is listening on.
+
+ REPRESENTATION TYPE (TYPE)
+
+ The argument specifies the representation type as described
+ in the Section on Data Representation and Storage. Several
+ types take a second parameter. The first parameter is
+ denoted by a single Telnet character, as is the second
+ Format parameter for ASCII and EBCDIC; the second parameter
+ for local byte is a decimal integer to indicate Bytesize.
+ The parameters are separated by a <SP> (Space, ASCII code
+ 32).
+
+ The following codes are assigned for type:
+
+ \ /
+ A - ASCII | | N - Non-print
+ |-><-| T - Telnet format effectors
+ E - EBCDIC| | C - Carriage Control (ASA)
+ / \
+ I - Image
+
+ L <byte size> - Local byte Byte size
+
+
+Postel & Reynolds [Page 28]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ The default representation type is ASCII Non-print. If the
+ Format parameter is changed, and later just the first
+ argument is changed, Format then returns to the Non-print
+ default.
+
+ FILE STRUCTURE (STRU)
+
+ The argument is a single Telnet character code specifying
+ file structure described in the Section on Data
+ Representation and Storage.
+
+ The following codes are assigned for structure:
+
+ F - File (no record structure)
+ R - Record structure
+ P - Page structure
+
+ The default structure is File.
+
+ TRANSFER MODE (MODE)
+
+ The argument is a single Telnet character code specifying
+ the data transfer modes described in the Section on
+ Transmission Modes.
+
+ The following codes are assigned for transfer modes:
+
+ S - Stream
+ B - Block
+ C - Compressed
+
+ The default transfer mode is Stream.
+
+ 4.1.3. FTP SERVICE COMMANDS
+
+ The FTP service commands define the file transfer or the file
+ system function requested by the user. The argument of an FTP
+ service command will normally be a pathname. The syntax of
+ pathnames must conform to server site conventions (with
+ standard defaults applicable), and the language conventions of
+ the control connection. The suggested default handling is to
+ use the last specified device, directory or file name, or the
+ standard default defined for local users. The commands may be
+ in any order except that a "rename from" command must be
+ followed by a "rename to" command and the restart command must
+ be followed by the interrupted service command (e.g., STOR or
+ RETR). The data, when transferred in response to FTP service
+
+
+Postel & Reynolds [Page 29]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ commands, shall always be sent over the data connection, except
+ for certain informative replies. The following commands
+ specify FTP service requests:
+
+ RETRIEVE (RETR)
+
+ This command causes the server-DTP to transfer a copy of the
+ file, specified in the pathname, to the server- or user-DTP
+ at the other end of the data connection. The status and
+ contents of the file at the server site shall be unaffected.
+
+ STORE (STOR)
+
+ This command causes the server-DTP to accept the data
+ transferred via the data connection and to store the data as
+ a file at the server site. If the file specified in the
+ pathname exists at the server site, then its contents shall
+ be replaced by the data being transferred. A new file is
+ created at the server site if the file specified in the
+ pathname does not already exist.
+
+ STORE UNIQUE (STOU)
+
+ This command behaves like STOR except that the resultant
+ file is to be created in the current directory under a name
+ unique to that directory. The 250 Transfer Started response
+ must include the name generated.
+
+ APPEND (with create) (APPE)
+
+ This command causes the server-DTP to accept the data
+ transferred via the data connection and to store the data in
+ a file at the server site. If the file specified in the
+ pathname exists at the server site, then the data shall be
+ appended to that file; otherwise the file specified in the
+ pathname shall be created at the server site.
+
+ ALLOCATE (ALLO)
+
+ This command may be required by some servers to reserve
+ sufficient storage to accommodate the new file to be
+ transferred. The argument shall be a decimal integer
+ representing the number of bytes (using the logical byte
+ size) of storage to be reserved for the file. For files
+ sent with record or page structure a maximum record or page
+ size (in logical bytes) might also be necessary; this is
+ indicated by a decimal integer in a second argument field of
+
+
+Postel & Reynolds [Page 30]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ the command. This second argument is optional, but when
+ present should be separated from the first by the three
+ Telnet characters <SP> R <SP>. This command shall be
+ followed by a STORe or APPEnd command. The ALLO command
+ should be treated as a NOOP (no operation) by those servers
+ which do not require that the maximum size of the file be
+ declared beforehand, and those servers interested in only
+ the maximum record or page size should accept a dummy value
+ in the first argument and ignore it.
+
+ RESTART (REST)
+
+ The argument field represents the server marker at which
+ file transfer is to be restarted. This command does not
+ cause file transfer but skips over the file to the specified
+ data checkpoint. This command shall be immediately followed
+ by the appropriate FTP service command which shall cause
+ file transfer to resume.
+
+ RENAME FROM (RNFR)
+
+ This command specifies the old pathname of the file which is
+ to be renamed. This command must be immediately followed by
+ a "rename to" command specifying the new file pathname.
+
+ RENAME TO (RNTO)
+
+ This command specifies the new pathname of the file
+ specified in the immediately preceding "rename from"
+ command. Together the two commands cause a file to be
+ renamed.
+
+ ABORT (ABOR)
+
+ This command tells the server to abort the previous FTP
+ service command and any associated transfer of data. The
+ abort command may require "special action", as discussed in
+ the Section on FTP Commands, to force recognition by the
+ server. No action is to be taken if the previous command
+ has been completed (including data transfer). The control
+ connection is not to be closed by the server, but the data
+ connection must be closed.
+
+ There are two cases for the server upon receipt of this
+ command: (1) the FTP service command was already completed,
+ or (2) the FTP service command is still in progress.
+
+
+
+Postel & Reynolds [Page 31]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ In the first case, the server closes the data connection
+ (if it is open) and responds with a 226 reply, indicating
+ that the abort command was successfully processed.
+
+ In the second case, the server aborts the FTP service in
+ progress and closes the data connection, returning a 426
+ reply to indicate that the service request terminated
+ abnormally. The server then sends a 226 reply,
+ indicating that the abort command was successfully
+ processed.
+
+ DELETE (DELE)
+
+ This command causes the file specified in the pathname to be
+ deleted at the server site. If an extra level of protection
+ is desired (such as the query, "Do you really wish to
+ delete?"), it should be provided by the user-FTP process.
+
+ REMOVE DIRECTORY (RMD)
+
+ This command causes the directory specified in the pathname
+ to be removed as a directory (if the pathname is absolute)
+ or as a subdirectory of the current working directory (if
+ the pathname is relative). See Appendix II.
+
+ MAKE DIRECTORY (MKD)
+
+ This command causes the directory specified in the pathname
+ to be created as a directory (if the pathname is absolute)
+ or as a subdirectory of the current working directory (if
+ the pathname is relative). See Appendix II.
+
+ PRINT WORKING DIRECTORY (PWD)
+
+ This command causes the name of the current working
+ directory to be returned in the reply. See Appendix II.
+
+ LIST (LIST)
+
+ This command causes a list to be sent from the server to the
+ passive DTP. If the pathname specifies a directory or other
+ group of files, the server should transfer a list of files
+ in the specified directory. If the pathname specifies a
+ file then the server should send current information on the
+ file. A null argument implies the user's current working or
+ default directory. The data transfer is over the data
+ connection in type ASCII or type EBCDIC. (The user must
+
+
+Postel & Reynolds [Page 32]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ ensure that the TYPE is appropriately ASCII or EBCDIC).
+ Since the information on a file may vary widely from system
+ to system, this information may be hard to use automatically
+ in a program, but may be quite useful to a human user.
+
+ NAME LIST (NLST)
+
+ This command causes a directory listing to be sent from
+ server to user site. The pathname should specify a
+ directory or other system-specific file group descriptor; a
+ null argument implies the current directory. The server
+ will return a stream of names of files and no other
+ information. The data will be transferred in ASCII or
+ EBCDIC type over the data connection as valid pathname
+ strings separated by <CRLF> or <NL>. (Again the user must
+ ensure that the TYPE is correct.) This command is intended
+ to return information that can be used by a program to
+ further process the files automatically. For example, in
+ the implementation of a "multiple get" function.
+
+ SITE PARAMETERS (SITE)
+
+ This command is used by the server to provide services
+ specific to his system that are essential to file transfer
+ but not sufficiently universal to be included as commands in
+ the protocol. The nature of these services and the
+ specification of their syntax can be stated in a reply to
+ the HELP SITE command.
+
+ SYSTEM (SYST)
+
+ This command is used to find out the type of operating
+ system at the server. The reply shall have as its first
+ word one of the system names listed in the current version
+ of the Assigned Numbers document [4].
+
+ STATUS (STAT)
+
+ This command shall cause a status response to be sent over
+ the control connection in the form of a reply. The command
+ may be sent during a file transfer (along with the Telnet IP
+ and Synch signals--see the Section on FTP Commands) in which
+ case the server will respond with the status of the
+ operation in progress, or it may be sent between file
+ transfers. In the latter case, the command may have an
+ argument field. If the argument is a pathname, the command
+ is analogous to the "list" command except that data shall be
+
+
+Postel & Reynolds [Page 33]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ transferred over the control connection. If a partial
+ pathname is given, the server may respond with a list of
+ file names or attributes associated with that specification.
+ If no argument is given, the server should return general
+ status information about the server FTP process. This
+ should include current values of all transfer parameters and
+ the status of connections.
+
+ HELP (HELP)
+
+ This command shall cause the server to send helpful
+ information regarding its implementation status over the
+ control connection to the user. The command may take an
+ argument (e.g., any command name) and return more specific
+ information as a response. The reply is type 211 or 214.
+ It is suggested that HELP be allowed before entering a USER
+ command. The server may use this reply to specify
+ site-dependent parameters, e.g., in response to HELP SITE.
+
+ NOOP (NOOP)
+
+ This command does not affect any parameters or previously
+ entered commands. It specifies no action other than that the
+ server send an OK reply.
+
+ The File Transfer Protocol follows the specifications of the Telnet
+ protocol for all communications over the control connection. Since
+ the language used for Telnet communication may be a negotiated
+ option, all references in the next two sections will be to the
+ "Telnet language" and the corresponding "Telnet end-of-line code".
+ Currently, one may take these to mean NVT-ASCII and <CRLF>. No other
+ specifications of the Telnet protocol will be cited.
+
+ FTP commands are "Telnet strings" terminated by the "Telnet end of
+ line code". The command codes themselves are alphabetic characters
+ terminated by the character <SP> (Space) if parameters follow and
+ Telnet-EOL otherwise. The command codes and the semantics of
+ commands are described in this section; the detailed syntax of
+ commands is specified in the Section on Commands, the reply sequences
+ are discussed in the Section on Sequencing of Commands and Replies,
+ and scenarios illustrating the use of commands are provided in the
+ Section on Typical FTP Scenarios.
+
+ FTP commands may be partitioned as those specifying access-control
+ identifiers, data transfer parameters, or FTP service requests.
+ Certain commands (such as ABOR, STAT, QUIT) may be sent over the
+ control connection while a data transfer is in progress. Some
+
+
+Postel & Reynolds [Page 34]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ servers may not be able to monitor the control and data connections
+ simultaneously, in which case some special action will be necessary
+ to get the server's attention. The following ordered format is
+ tentatively recommended:
+
+ 1. User system inserts the Telnet "Interrupt Process" (IP) signal
+ in the Telnet stream.
+
+ 2. User system sends the Telnet "Synch" signal.
+
+ 3. User system inserts the command (e.g., ABOR) in the Telnet
+ stream.
+
+ 4. Server PI, after receiving "IP", scans the Telnet stream for
+ EXACTLY ONE FTP command.
+
+ (For other servers this may not be necessary but the actions listed
+ above should have no unusual effect.)
+
+ 4.2. FTP REPLIES
+
+ Replies to File Transfer Protocol commands are devised to ensure
+ the synchronization of requests and actions in the process of file
+ transfer, and to guarantee that the user process always knows the
+ state of the Server. Every command must generate at least one
+ reply, although there may be more than one; in the latter case,
+ the multiple replies must be easily distinguished. In addition,
+ some commands occur in sequential groups, such as USER, PASS and
+ ACCT, or RNFR and RNTO. The replies show the existence of an
+ intermediate state if all preceding commands have been successful.
+ A failure at any point in the sequence necessitates the repetition
+ of the entire sequence from the beginning.
+
+ The details of the command-reply sequence are made explicit in
+ a set of state diagrams below.
+
+ An FTP reply consists of a three digit number (transmitted as
+ three alphanumeric characters) followed by some text. The number
+ is intended for use by automata to determine what state to enter
+ next; the text is intended for the human user. It is intended
+ that the three digits contain enough encoded information that the
+ user-process (the User-PI) will not need to examine the text and
+ may either discard it or pass it on to the user, as appropriate.
+ In particular, the text may be server-dependent, so there are
+ likely to be varying texts for each reply code.
+
+ A reply is defined to contain the 3-digit code, followed by Space
+
+
+Postel & Reynolds [Page 35]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ <SP>, followed by one line of text (where some maximum line length
+ has been specified), and terminated by the Telnet end-of-line
+ code. There will be cases however, where the text is longer than
+ a single line. In these cases the complete text must be bracketed
+ so the User-process knows when it may stop reading the reply (i.e.
+ stop processing input on the control connection) and go do other
+ things. This requires a special format on the first line to
+ indicate that more than one line is coming, and another on the
+ last line to designate it as the last. At least one of these must
+ contain the appropriate reply code to indicate the state of the
+ transaction. To satisfy all factions, it was decided that both
+ the first and last line codes should be the same.
+
+ Thus the format for multi-line replies is that the first line
+ will begin with the exact required reply code, followed
+ immediately by a Hyphen, "-" (also known as Minus), followed by
+ text. The last line will begin with the same code, followed
+ immediately by Space <SP>, optionally some text, and the Telnet
+ end-of-line code.
+
+ For example:
+ 123-First line
+ Second line
+ 234 A line beginning with numbers
+ 123 The last line
+
+ The user-process then simply needs to search for the second
+ occurrence of the same reply code, followed by <SP> (Space), at
+ the beginning of a line, and ignore all intermediary lines. If
+ an intermediary line begins with a 3-digit number, the Server
+ must pad the front to avoid confusion.
+
+ This scheme allows standard system routines to be used for
+ reply information (such as for the STAT reply), with
+ "artificial" first and last lines tacked on. In rare cases
+ where these routines are able to generate three digits and a
+ Space at the beginning of any line, the beginning of each
+ text line should be offset by some neutral text, like Space.
+
+ This scheme assumes that multi-line replies may not be nested.
+
+ The three digits of the reply each have a special significance.
+ This is intended to allow a range of very simple to very
+ sophisticated responses by the user-process. The first digit
+ denotes whether the response is good, bad or incomplete.
+ (Referring to the state diagram), an unsophisticated user-process
+ will be able to determine its next action (proceed as planned,
+
+
+Postel & Reynolds [Page 36]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ redo, retrench, etc.) by simply examining this first digit. A
+ user-process that wants to know approximately what kind of error
+ occurred (e.g. file system error, command syntax error) may
+ examine the second digit, reserving the third digit for the finest
+ gradation of information (e.g., RNTO command without a preceding
+ RNFR).
+
+ There are five values for the first digit of the reply code:
+
+ 1yz Positive Preliminary reply
+
+ The requested action is being initiated; expect another
+ reply before proceeding with a new command. (The
+ user-process sending another command before the
+ completion reply would be in violation of protocol; but
+ server-FTP processes should queue any commands that
+ arrive while a preceding command is in progress.) This
+ type of reply can be used to indicate that the command
+ was accepted and the user-process may now pay attention
+ to the data connections, for implementations where
+ simultaneous monitoring is difficult. The server-FTP
+ process may send at most, one 1yz reply per command.
+
+ 2yz Positive Completion reply
+
+ The requested action has been successfully completed. A
+ new request may be initiated.
+
+ 3yz Positive Intermediate reply
+
+ The command has been accepted, but the requested action
+ is being held in abeyance, pending receipt of further
+ information. The user should send another command
+ specifying this information. This reply is used in
+ command sequence groups.
+
+ 4yz Transient Negative Completion reply
+
+ The command was not accepted and the requested action did
+ not take place, but the error condition is temporary and
+ the action may be requested again. The user should
+ return to the beginning of the command sequence, if any.
+ It is difficult to assign a meaning to "transient",
+ particularly when two distinct sites (Server- and
+ User-processes) have to agree on the interpretation.
+ Each reply in the 4yz category might have a slightly
+ different time value, but the intent is that the
+
+
+Postel & Reynolds [Page 37]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ user-process is encouraged to try again. A rule of thumb
+ in determining if a reply fits into the 4yz or the 5yz
+ (Permanent Negative) category is that replies are 4yz if
+ the commands can be repeated without any change in
+ command form or in properties of the User or Server
+ (e.g., the command is spelled the same with the same
+ arguments used; the user does not change his file access
+ or user name; the server does not put up a new
+ implementation.)
+
+ 5yz Permanent Negative Completion reply
+
+ The command was not accepted and the requested action did
+ not take place. The User-process is discouraged from
+ repeating the exact request (in the same sequence). Even
+ some "permanent" error conditions can be corrected, so
+ the human user may want to direct his User-process to
+ reinitiate the command sequence by direct action at some
+ point in the future (e.g., after the spelling has been
+ changed, or the user has altered his directory status.)
+
+ The following function groupings are encoded in the second
+ digit:
+
+ x0z Syntax - These replies refer to syntax errors,
+ syntactically correct commands that don't fit any
+ functional category, unimplemented or superfluous
+ commands.
+
+ x1z Information - These are replies to requests for
+ information, such as status or help.
+
+ x2z Connections - Replies referring to the control and
+ data connections.
+
+ x3z Authentication and accounting - Replies for the login
+ process and accounting procedures.
+
+ x4z Unspecified as yet.
+
+ x5z File system - These replies indicate the status of the
+ Server file system vis-a-vis the requested transfer or
+ other file system action.
+
+ The third digit gives a finer gradation of meaning in each of
+ the function categories, specified by the second digit. The
+ list of replies below will illustrate this. Note that the text
+
+
+Postel & Reynolds [Page 38]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ associated with each reply is recommended, rather than
+ mandatory, and may even change according to the command with
+ which it is associated. The reply codes, on the other hand,
+ must strictly follow the specifications in the last section;
+ that is, Server implementations should not invent new codes for
+ situations that are only slightly different from the ones
+ described here, but rather should adapt codes already defined.
+
+ A command such as TYPE or ALLO whose successful execution
+ does not offer the user-process any new information will
+ cause a 200 reply to be returned. If the command is not
+ implemented by a particular Server-FTP process because it
+ has no relevance to that computer system, for example ALLO
+ at a TOPS20 site, a Positive Completion reply is still
+ desired so that the simple User-process knows it can proceed
+ with its course of action. A 202 reply is used in this case
+ with, for example, the reply text: "No storage allocation
+ necessary." If, on the other hand, the command requests a
+ non-site-specific action and is unimplemented, the response
+ is 502. A refinement of that is the 504 reply for a command
+ that is implemented, but that requests an unimplemented
+ parameter.
+
+ 4.2.1 Reply Codes by Function Groups
+
+ 200 Command okay.
+ 500 Syntax error, command unrecognized.
+ This may include errors such as command line too long.
+ 501 Syntax error in parameters or arguments.
+ 202 Command not implemented, superfluous at this site.
+ 502 Command not implemented.
+ 503 Bad sequence of commands.
+ 504 Command not implemented for that parameter.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 39]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 110 Restart marker reply.
+ In this case, the text is exact and not left to the
+ particular implementation; it must read:
+ MARK yyyy = mmmm
+ Where yyyy is User-process data stream marker, and mmmm
+ server's equivalent marker (note the spaces between markers
+ and "=").
+ 211 System status, or system help reply.
+ 212 Directory status.
+ 213 File status.
+ 214 Help message.
+ On how to use the server or the meaning of a particular
+ non-standard command. This reply is useful only to the
+ human user.
+ 215 NAME system type.
+ Where NAME is an official system name from the list in the
+ Assigned Numbers document.
+
+ 120 Service ready in nnn minutes.
+ 220 Service ready for new user.
+ 221 Service closing control connection.
+ Logged out if appropriate.
+ 421 Service not available, closing control connection.
+ This may be a reply to any command if the service knows it
+ must shut down.
+ 125 Data connection already open; transfer starting.
+ 225 Data connection open; no transfer in progress.
+ 425 Can't open data connection.
+ 226 Closing data connection.
+ Requested file action successful (for example, file
+ transfer or file abort).
+ 426 Connection closed; transfer aborted.
+ 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
+
+ 230 User logged in, proceed.
+ 530 Not logged in.
+ 331 User name okay, need password.
+ 332 Need account for login.
+ 532 Need account for storing files.
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 40]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 150 File status okay; about to open data connection.
+ 250 Requested file action okay, completed.
+ 257 "PATHNAME" created.
+ 350 Requested file action pending further information.
+ 450 Requested file action not taken.
+ File unavailable (e.g., file busy).
+ 550 Requested action not taken.
+ File unavailable (e.g., file not found, no access).
+ 451 Requested action aborted. Local error in processing.
+ 551 Requested action aborted. Page type unknown.
+ 452 Requested action not taken.
+ Insufficient storage space in system.
+ 552 Requested file action aborted.
+ Exceeded storage allocation (for current directory or
+ dataset).
+ 553 Requested action not taken.
+ File name not allowed.
+
+
+ 4.2.2 Numeric Order List of Reply Codes
+
+ 110 Restart marker reply.
+ In this case, the text is exact and not left to the
+ particular implementation; it must read:
+ MARK yyyy = mmmm
+ Where yyyy is User-process data stream marker, and mmmm
+ server's equivalent marker (note the spaces between markers
+ and "=").
+ 120 Service ready in nnn minutes.
+ 125 Data connection already open; transfer starting.
+ 150 File status okay; about to open data connection.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 41]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 200 Command okay.
+ 202 Command not implemented, superfluous at this site.
+ 211 System status, or system help reply.
+ 212 Directory status.
+ 213 File status.
+ 214 Help message.
+ On how to use the server or the meaning of a particular
+ non-standard command. This reply is useful only to the
+ human user.
+ 215 NAME system type.
+ Where NAME is an official system name from the list in the
+ Assigned Numbers document.
+ 220 Service ready for new user.
+ 221 Service closing control connection.
+ Logged out if appropriate.
+ 225 Data connection open; no transfer in progress.
+ 226 Closing data connection.
+ Requested file action successful (for example, file
+ transfer or file abort).
+ 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2).
+ 230 User logged in, proceed.
+ 250 Requested file action okay, completed.
+ 257 "PATHNAME" created.
+
+ 331 User name okay, need password.
+ 332 Need account for login.
+ 350 Requested file action pending further information.
+
+ 421 Service not available, closing control connection.
+ This may be a reply to any command if the service knows it
+ must shut down.
+ 425 Can't open data connection.
+ 426 Connection closed; transfer aborted.
+ 450 Requested file action not taken.
+ File unavailable (e.g., file busy).
+ 451 Requested action aborted: local error in processing.
+ 452 Requested action not taken.
+ Insufficient storage space in system.
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 42]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 500 Syntax error, command unrecognized.
+ This may include errors such as command line too long.
+ 501 Syntax error in parameters or arguments.
+ 502 Command not implemented.
+ 503 Bad sequence of commands.
+ 504 Command not implemented for that parameter.
+ 530 Not logged in.
+ 532 Need account for storing files.
+ 550 Requested action not taken.
+ File unavailable (e.g., file not found, no access).
+ 551 Requested action aborted: page type unknown.
+ 552 Requested file action aborted.
+ Exceeded storage allocation (for current directory or
+ dataset).
+ 553 Requested action not taken.
+ File name not allowed.
+
+
+5. DECLARATIVE SPECIFICATIONS
+
+ 5.1. MINIMUM IMPLEMENTATION
+
+ In order to make FTP workable without needless error messages, the
+ following minimum implementation is required for all servers:
+
+ TYPE - ASCII Non-print
+ MODE - Stream
+ STRUCTURE - File, Record
+ COMMANDS - USER, QUIT, PORT,
+ TYPE, MODE, STRU,
+ for the default values
+ RETR, STOR,
+ NOOP.
+
+ The default values for transfer parameters are:
+
+ TYPE - ASCII Non-print
+ MODE - Stream
+ STRU - File
+
+ All hosts must accept the above as the standard defaults.
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 43]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 5.2. CONNECTIONS
+
+ The server protocol interpreter shall "listen" on Port L. The
+ user or user protocol interpreter shall initiate the full-duplex
+ control connection. Server- and user- processes should follow the
+ conventions of the Telnet protocol as specified in the
+ ARPA-Internet Protocol Handbook [1]. Servers are under no
+ obligation to provide for editing of command lines and may require
+ that it be done in the user host. The control connection shall be
+ closed by the server at the user's request after all transfers and
+ replies are completed.
+
+ The user-DTP must "listen" on the specified data port; this may be
+ the default user port (U) or a port specified in the PORT command.
+ The server shall initiate the data connection from his own default
+ data port (L-1) using the specified user data port. The direction
+ of the transfer and the port used will be determined by the FTP
+ service command.
+
+ Note that all FTP implementation must support data transfer using
+ the default port, and that only the USER-PI may initiate the use
+ of non-default ports.
+
+ When data is to be transferred between two servers, A and B (refer
+ to Figure 2), the user-PI, C, sets up control connections with
+ both server-PI's. One of the servers, say A, is then sent a PASV
+ command telling him to "listen" on his data port rather than
+ initiate a connection when he receives a transfer service command.
+ When the user-PI receives an acknowledgment to the PASV command,
+ which includes the identity of the host and port being listened
+ on, the user-PI then sends A's port, a, to B in a PORT command; a
+ reply is returned. The user-PI may then send the corresponding
+ service commands to A and B. Server B initiates the connection
+ and the transfer proceeds. The command-reply sequence is listed
+ below where the messages are vertically synchronous but
+ horizontally asynchronous:
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 44]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ User-PI - Server A User-PI - Server B
+ ------------------ ------------------
+
+ C->A : Connect C->B : Connect
+ C->A : PASV
+ A->C : 227 Entering Passive Mode. A1,A2,A3,A4,a1,a2
+ C->B : PORT A1,A2,A3,A4,a1,a2
+ B->C : 200 Okay
+ C->A : STOR C->B : RETR
+ B->A : Connect to HOST-A, PORT-a
+
+ Figure 3
+
+ The data connection shall be closed by the server under the
+ conditions described in the Section on Establishing Data
+ Connections. If the data connection is to be closed following a
+ data transfer where closing the connection is not required to
+ indicate the end-of-file, the server must do so immediately.
+ Waiting until after a new transfer command is not permitted
+ because the user-process will have already tested the data
+ connection to see if it needs to do a "listen"; (remember that the
+ user must "listen" on a closed data port BEFORE sending the
+ transfer request). To prevent a race condition here, the server
+ sends a reply (226) after closing the data connection (or if the
+ connection is left open, a "file transfer completed" reply (250)
+ and the user-PI should wait for one of these replies before
+ issuing a new transfer command).
+
+ Any time either the user or server see that the connection is
+ being closed by the other side, it should promptly read any
+ remaining data queued on the connection and issue the close on its
+ own side.
+
+ 5.3. COMMANDS
+
+ The commands are Telnet character strings transmitted over the
+ control connections as described in the Section on FTP Commands.
+ The command functions and semantics are described in the Section
+ on Access Control Commands, Transfer Parameter Commands, FTP
+ Service Commands, and Miscellaneous Commands. The command syntax
+ is specified here.
+
+ The commands begin with a command code followed by an argument
+ field. The command codes are four or fewer alphabetic characters.
+ Upper and lower case alphabetic characters are to be treated
+ identically. Thus, any of the following may represent the
+ retrieve command:
+
+
+Postel & Reynolds [Page 45]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ RETR Retr retr ReTr rETr
+
+ This also applies to any symbols representing parameter values,
+ such as A or a for ASCII TYPE. The command codes and the argument
+ fields are separated by one or more spaces.
+
+ The argument field consists of a variable length character string
+ ending with the character sequence <CRLF> (Carriage Return, Line
+ Feed) for NVT-ASCII representation; for other negotiated languages
+ a different end of line character might be used. It should be
+ noted that the server is to take no action until the end of line
+ code is received.
+
+ The syntax is specified below in NVT-ASCII. All characters in the
+ argument field are ASCII characters including any ASCII
+ represented decimal integers. Square brackets denote an optional
+ argument field. If the option is not taken, the appropriate
+ default is implied.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 46]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 5.3.1. FTP COMMANDS
+
+ The following are the FTP commands:
+
+ USER <SP> <username> <CRLF>
+ PASS <SP> <password> <CRLF>
+ ACCT <SP> <account-information> <CRLF>
+ CWD <SP> <pathname> <CRLF>
+ CDUP <CRLF>
+ SMNT <SP> <pathname> <CRLF>
+ QUIT <CRLF>
+ REIN <CRLF>
+ PORT <SP> <host-port> <CRLF>
+ PASV <CRLF>
+ TYPE <SP> <type-code> <CRLF>
+ STRU <SP> <structure-code> <CRLF>
+ MODE <SP> <mode-code> <CRLF>
+ RETR <SP> <pathname> <CRLF>
+ STOR <SP> <pathname> <CRLF>
+ STOU <CRLF>
+ APPE <SP> <pathname> <CRLF>
+ ALLO <SP> <decimal-integer>
+ [<SP> R <SP> <decimal-integer>] <CRLF>
+ REST <SP> <marker> <CRLF>
+ RNFR <SP> <pathname> <CRLF>
+ RNTO <SP> <pathname> <CRLF>
+ ABOR <CRLF>
+ DELE <SP> <pathname> <CRLF>
+ RMD <SP> <pathname> <CRLF>
+ MKD <SP> <pathname> <CRLF>
+ PWD <CRLF>
+ LIST [<SP> <pathname>] <CRLF>
+ NLST [<SP> <pathname>] <CRLF>
+ SITE <SP> <string> <CRLF>
+ SYST <CRLF>
+ STAT [<SP> <pathname>] <CRLF>
+ HELP [<SP> <string>] <CRLF>
+ NOOP <CRLF>
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 47]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 5.3.2. FTP COMMAND ARGUMENTS
+
+ The syntax of the above argument fields (using BNF notation
+ where applicable) is:
+
+ <username> ::= <string>
+ <password> ::= <string>
+ <account-information> ::= <string>
+ <string> ::= <char> | <char><string>
+ <char> ::= any of the 128 ASCII characters except <CR> and
+ <LF>
+ <marker> ::= <pr-string>
+ <pr-string> ::= <pr-char> | <pr-char><pr-string>
+ <pr-char> ::= printable characters, any
+ ASCII code 33 through 126
+ <byte-size> ::= <number>
+ <host-port> ::= <host-number>,<port-number>
+ <host-number> ::= <number>,<number>,<number>,<number>
+ <port-number> ::= <number>,<number>
+ <number> ::= any decimal integer 1 through 255
+ <form-code> ::= N | T | C
+ <type-code> ::= A [<sp> <form-code>]
+ | E [<sp> <form-code>]
+ | I
+ | L <sp> <byte-size>
+ <structure-code> ::= F | R | P
+ <mode-code> ::= S | B | C
+ <pathname> ::= <string>
+ <decimal-integer> ::= any decimal integer
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 48]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ 5.4. SEQUENCING OF COMMANDS AND REPLIES
+
+ The communication between the user and server is intended to be an
+ alternating dialogue. As such, the user issues an FTP command and
+ the server responds with a prompt primary reply. The user should
+ wait for this initial primary success or failure response before
+ sending further commands.
+
+ Certain commands require a second reply for which the user should
+ also wait. These replies may, for example, report on the progress
+ or completion of file transfer or the closing of the data
+ connection. They are secondary replies to file transfer commands.
+
+ One important group of informational replies is the connection
+ greetings. Under normal circumstances, a server will send a 220
+ reply, "awaiting input", when the connection is completed. The
+ user should wait for this greeting message before sending any
+ commands. If the server is unable to accept input right away, a
+ 120 "expected delay" reply should be sent immediately and a 220
+ reply when ready. The user will then know not to hang up if there
+ is a delay.
+
+ Spontaneous Replies
+
+ Sometimes "the system" spontaneously has a message to be sent
+ to a user (usually all users). For example, "System going down
+ in 15 minutes". There is no provision in FTP for such
+ spontaneous information to be sent from the server to the user.
+ It is recommended that such information be queued in the
+ server-PI and delivered to the user-PI in the next reply
+ (possibly making it a multi-line reply).
+
+ The table below lists alternative success and failure replies for
+ each command. These must be strictly adhered to; a server may
+ substitute text in the replies, but the meaning and action implied
+ by the code numbers and by the specific command reply sequence
+ cannot be altered.
+
+ Command-Reply Sequences
+
+ In this section, the command-reply sequence is presented. Each
+ command is listed with its possible replies; command groups are
+ listed together. Preliminary replies are listed first (with
+ their succeeding replies indented and under them), then
+ positive and negative completion, and finally intermediary
+
+
+
+
+Postel & Reynolds [Page 49]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ replies with the remaining commands from the sequence
+ following. This listing forms the basis for the state
+ diagrams, which will be presented separately.
+
+ Connection Establishment
+ 120
+ 220
+ 220
+ 421
+ Login
+ USER
+ 230
+ 530
+ 500, 501, 421
+ 331, 332
+ PASS
+ 230
+ 202
+ 530
+ 500, 501, 503, 421
+ 332
+ ACCT
+ 230
+ 202
+ 530
+ 500, 501, 503, 421
+ CWD
+ 250
+ 500, 501, 502, 421, 530, 550
+ CDUP
+ 200
+ 500, 501, 502, 421, 530, 550
+ SMNT
+ 202, 250
+ 500, 501, 502, 421, 530, 550
+ Logout
+ REIN
+ 120
+ 220
+ 220
+ 421
+ 500, 502
+ QUIT
+ 221
+ 500
+
+
+
+
+Postel & Reynolds [Page 50]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ Transfer parameters
+ PORT
+ 200
+ 500, 501, 421, 530
+ PASV
+ 227
+ 500, 501, 502, 421, 530
+ MODE
+ 200
+ 500, 501, 504, 421, 530
+ TYPE
+ 200
+ 500, 501, 504, 421, 530
+ STRU
+ 200
+ 500, 501, 504, 421, 530
+ File action commands
+ ALLO
+ 200
+ 202
+ 500, 501, 504, 421, 530
+ REST
+ 500, 501, 502, 421, 530
+ 350
+ STOR
+ 125, 150
+ (110)
+ 226, 250
+ 425, 426, 451, 551, 552
+ 532, 450, 452, 553
+ 500, 501, 421, 530
+ STOU
+ 125, 150
+ (110)
+ 226, 250
+ 425, 426, 451, 551, 552
+ 532, 450, 452, 553
+ 500, 501, 421, 530
+ RETR
+ 125, 150
+ (110)
+ 226, 250
+ 425, 426, 451
+ 450, 550
+ 500, 501, 421, 530
+
+
+
+
+Postel & Reynolds [Page 51]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ LIST
+ 125, 150
+ 226, 250
+ 425, 426, 451
+ 450
+ 500, 501, 502, 421, 530
+ NLST
+ 125, 150
+ 226, 250
+ 425, 426, 451
+ 450
+ 500, 501, 502, 421, 530
+ APPE
+ 125, 150
+ (110)
+ 226, 250
+ 425, 426, 451, 551, 552
+ 532, 450, 550, 452, 553
+ 500, 501, 502, 421, 530
+ RNFR
+ 450, 550
+ 500, 501, 502, 421, 530
+ 350
+ RNTO
+ 250
+ 532, 553
+ 500, 501, 502, 503, 421, 530
+ DELE
+ 250
+ 450, 550
+ 500, 501, 502, 421, 530
+ RMD
+ 250
+ 500, 501, 502, 421, 530, 550
+ MKD
+ 257
+ 500, 501, 502, 421, 530, 550
+ PWD
+ 257
+ 500, 501, 502, 421, 550
+ ABOR
+ 225, 226
+ 500, 501, 502, 421
+
+
+
+
+
+
+Postel & Reynolds [Page 52]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ Informational commands
+ SYST
+ 215
+ 500, 501, 502, 421
+ STAT
+ 211, 212, 213
+ 450
+ 500, 501, 502, 421, 530
+ HELP
+ 211, 214
+ 500, 501, 502, 421
+ Miscellaneous commands
+ SITE
+ 200
+ 202
+ 500, 501, 530
+ NOOP
+ 200
+ 500 421
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 53]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+6. STATE DIAGRAMS
+
+ Here we present state diagrams for a very simple minded FTP
+ implementation. Only the first digit of the reply codes is used.
+ There is one state diagram for each group of FTP commands or command
+ sequences.
+
+ The command groupings were determined by constructing a model for
+ each command then collecting together the commands with structurally
+ identical models.
+
+ For each command or command sequence there are three possible
+ outcomes: success (S), failure (F), and error (E). In the state
+ diagrams below we use the symbol B for "begin", and the symbol W for
+ "wait for reply".
+
+ We first present the diagram that represents the largest group of FTP
+ commands:
+
+
+ 1,3 +---+
+ ----------->| E |
+ | +---+
+ |
+ +---+ cmd +---+ 2 +---+
+ | B |---------->| W |---------->| S |
+ +---+ +---+ +---+
+ |
+ | 4,5 +---+
+ ----------->| F |
+ +---+
+
+
+ This diagram models the commands:
+
+ ABOR, ALLO, DELE, CWD, CDUP, SMNT, HELP, MODE, NOOP, PASV,
+ QUIT, SITE, PORT, SYST, STAT, RMD, MKD, PWD, STRU, and TYPE.
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 54]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ The other large group of commands is represented by a very similar
+ diagram:
+
+
+ 3 +---+
+ ----------->| E |
+ | +---+
+ |
+ +---+ cmd +---+ 2 +---+
+ | B |---------->| W |---------->| S |
+ +---+ --->+---+ +---+
+ | | |
+ | | | 4,5 +---+
+ | 1 | ----------->| F |
+ ----- +---+
+
+
+ This diagram models the commands:
+
+ APPE, LIST, NLST, REIN, RETR, STOR, and STOU.
+
+ Note that this second model could also be used to represent the first
+ group of commands, the only difference being that in the first group
+ the 100 series replies are unexpected and therefore treated as error,
+ while the second group expects (some may require) 100 series replies.
+ Remember that at most, one 100 series reply is allowed per command.
+
+ The remaining diagrams model command sequences, perhaps the simplest
+ of these is the rename sequence:
+
+
+ +---+ RNFR +---+ 1,2 +---+
+ | B |---------->| W |---------->| E |
+ +---+ +---+ -->+---+
+ | | |
+ 3 | | 4,5 |
+ -------------- ------ |
+ | | | +---+
+ | ------------->| S |
+ | | 1,3 | | +---+
+ | 2| --------
+ | | | |
+ V | | |
+ +---+ RNTO +---+ 4,5 ----->+---+
+ | |---------->| W |---------->| F |
+ +---+ +---+ +---+
+
+
+
+Postel & Reynolds [Page 55]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ The next diagram is a simple model of the Restart command:
+
+
+ +---+ REST +---+ 1,2 +---+
+ | B |---------->| W |---------->| E |
+ +---+ +---+ -->+---+
+ | | |
+ 3 | | 4,5 |
+ -------------- ------ |
+ | | | +---+
+ | ------------->| S |
+ | | 3 | | +---+
+ | 2| --------
+ | | | |
+ V | | |
+ +---+ cmd +---+ 4,5 ----->+---+
+ | |---------->| W |---------->| F |
+ +---+ -->+---+ +---+
+ | |
+ | 1 |
+ ------
+
+
+ Where "cmd" is APPE, STOR, or RETR.
+
+ We note that the above three models are similar. The Restart differs
+ from the Rename two only in the treatment of 100 series replies at
+ the second stage, while the second group expects (some may require)
+ 100 series replies. Remember that at most, one 100 series reply is
+ allowed per command.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 56]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ The most complicated diagram is for the Login sequence:
+
+
+ 1
+ +---+ USER +---+------------->+---+
+ | B |---------->| W | 2 ---->| E |
+ +---+ +---+------ | -->+---+
+ | | | | |
+ 3 | | 4,5 | | |
+ -------------- ----- | | |
+ | | | | |
+ | | | | |
+ | --------- |
+ | 1| | | |
+ V | | | |
+ +---+ PASS +---+ 2 | ------>+---+
+ | |---------->| W |------------->| S |
+ +---+ +---+ ---------->+---+
+ | | | | |
+ 3 | |4,5| | |
+ -------------- -------- |
+ | | | | |
+ | | | | |
+ | -----------
+ | 1,3| | | |
+ V | 2| | |
+ +---+ ACCT +---+-- | ----->+---+
+ | |---------->| W | 4,5 -------->| F |
+ +---+ +---+------------->+---+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 57]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ Finally, we present a generalized diagram that could be used to model
+ the command and reply interchange:
+
+
+ ------------------------------------
+ | |
+ Begin | |
+ | V |
+ | +---+ cmd +---+ 2 +---+ |
+ -->| |------->| |---------->| | |
+ | | | W | | S |-----|
+ -->| | -->| |----- | | |
+ | +---+ | +---+ 4,5 | +---+ |
+ | | | | | | |
+ | | | 1| |3 | +---+ |
+ | | | | | | | | |
+ | | ---- | ---->| F |-----
+ | | | | |
+ | | | +---+
+ -------------------
+ |
+ |
+ V
+ End
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 58]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+7. TYPICAL FTP SCENARIO
+
+ User at host U wanting to transfer files to/from host S:
+
+ In general, the user will communicate to the server via a mediating
+ user-FTP process. The following may be a typical scenario. The
+ user-FTP prompts are shown in parentheses, '---->' represents
+ commands from host U to host S, and '<----' represents replies from
+ host S to host U.
+
+ LOCAL COMMANDS BY USER ACTION INVOLVED
+
+ ftp (host) multics<CR> Connect to host S, port L,
+ establishing control connections.
+ <---- 220 Service ready <CRLF>.
+ username Doe <CR> USER Doe<CRLF>---->
+ <---- 331 User name ok,
+ need password<CRLF>.
+ password mumble <CR> PASS mumble<CRLF>---->
+ <---- 230 User logged in<CRLF>.
+ retrieve (local type) ASCII<CR>
+ (local pathname) test 1 <CR> User-FTP opens local file in ASCII.
+ (for. pathname) test.pl1<CR> RETR test.pl1<CRLF> ---->
+ <---- 150 File status okay;
+ about to open data
+ connection<CRLF>.
+ Server makes data connection
+ to port U.
+
+ <---- 226 Closing data connection,
+ file transfer successful<CRLF>.
+ type Image<CR> TYPE I<CRLF> ---->
+ <---- 200 Command OK<CRLF>
+ store (local type) image<CR>
+ (local pathname) file dump<CR> User-FTP opens local file in Image.
+ (for.pathname) >udd>cn>fd<CR> STOR >udd>cn>fd<CRLF> ---->
+ <---- 550 Access denied<CRLF>
+ terminate QUIT <CRLF> ---->
+ Server closes all
+ connections.
+
+8. CONNECTION ESTABLISHMENT
+
+ The FTP control connection is established via TCP between the user
+ process port U and the server process port L. This protocol is
+ assigned the service port 21 (25 octal), that is L=21.
+
+
+
+Postel & Reynolds [Page 59]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+APPENDIX I - PAGE STRUCTURE
+
+ The need for FTP to support page structure derives principally from
+ the need to support efficient transmission of files between TOPS-20
+ systems, particularly the files used by NLS.
+
+ The file system of TOPS-20 is based on the concept of pages. The
+ operating system is most efficient at manipulating files as pages.
+ The operating system provides an interface to the file system so that
+ many applications view files as sequential streams of characters.
+ However, a few applications use the underlying page structures
+ directly, and some of these create holey files.
+
+ A TOPS-20 disk file consists of four things: a pathname, a page
+ table, a (possibly empty) set of pages, and a set of attributes.
+
+ The pathname is specified in the RETR or STOR command. It includes
+ the directory name, file name, file name extension, and generation
+ number.
+
+ The page table contains up to 2**18 entries. Each entry may be
+ EMPTY, or may point to a page. If it is not empty, there are also
+ some page-specific access bits; not all pages of a file need have the
+ same access protection.
+
+ A page is a contiguous set of 512 words of 36 bits each.
+
+ The attributes of the file, in the File Descriptor Block (FDB),
+ contain such things as creation time, write time, read time, writer's
+ byte-size, end-of-file pointer, count of reads and writes, backup
+ system tape numbers, etc.
+
+ Note that there is NO requirement that entries in the page table be
+ contiguous. There may be empty page table slots between occupied
+ ones. Also, the end of file pointer is simply a number. There is no
+ requirement that it in fact point at the "last" datum in the file.
+ Ordinary sequential I/O calls in TOPS-20 will cause the end of file
+ pointer to be left after the last datum written, but other operations
+ may cause it not to be so, if a particular programming system so
+ requires.
+
+ In fact, in both of these special cases, "holey" files and
+ end-of-file pointers NOT at the end of the file, occur with NLS data
+ files.
+
+
+
+
+
+Postel & Reynolds [Page 60]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ The TOPS-20 paged files can be sent with the FTP transfer parameters:
+ TYPE L 36, STRU P, and MODE S (in fact, any mode could be used).
+
+ Each page of information has a header. Each header field, which is a
+ logical byte, is a TOPS-20 word, since the TYPE is L 36.
+
+ The header fields are:
+
+ Word 0: Header Length.
+
+ The header length is 5.
+
+ Word 1: Page Index.
+
+ If the data is a disk file page, this is the number of that
+ page in the file's page map. Empty pages (holes) in the file
+ are simply not sent. Note that a hole is NOT the same as a
+ page of zeros.
+
+ Word 2: Data Length.
+
+ The number of data words in this page, following the header.
+ Thus, the total length of the transmission unit is the Header
+ Length plus the Data Length.
+
+ Word 3: Page Type.
+
+ A code for what type of chunk this is. A data page is type 3,
+ the FDB page is type 2.
+
+ Word 4: Page Access Control.
+
+ The access bits associated with the page in the file's page
+ map. (This full word quantity is put into AC2 of an SPACS by
+ the program reading from net to disk.)
+
+ After the header are Data Length data words. Data Length is
+ currently either 512 for a data page or 31 for an FDB. Trailing
+ zeros in a disk file page may be discarded, making Data Length less
+ than 512 in that case.
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 61]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+APPENDIX II - DIRECTORY COMMANDS
+
+ Since UNIX has a tree-like directory structure in which directories
+ are as easy to manipulate as ordinary files, it is useful to expand
+ the FTP servers on these machines to include commands which deal with
+ the creation of directories. Since there are other hosts on the
+ ARPA-Internet which have tree-like directories (including TOPS-20 and
+ Multics), these commands are as general as possible.
+
+ Four directory commands have been added to FTP:
+
+ MKD pathname
+
+ Make a directory with the name "pathname".
+
+ RMD pathname
+
+ Remove the directory with the name "pathname".
+
+ PWD
+
+ Print the current working directory name.
+
+ CDUP
+
+ Change to the parent of the current working directory.
+
+ The "pathname" argument should be created (removed) as a
+ subdirectory of the current working directory, unless the "pathname"
+ string contains sufficient information to specify otherwise to the
+ server, e.g., "pathname" is an absolute pathname (in UNIX and
+ Multics), or pathname is something like "<abso.lute.path>" to
+ TOPS-20.
+
+ REPLY CODES
+
+ The CDUP command is a special case of CWD, and is included to
+ simplify the implementation of programs for transferring directory
+ trees between operating systems having different syntaxes for
+ naming the parent directory. The reply codes for CDUP be
+ identical to the reply codes of CWD.
+
+ The reply codes for RMD be identical to the reply codes for its
+ file analogue, DELE.
+
+ The reply codes for MKD, however, are a bit more complicated. A
+ freshly created directory will probably be the object of a future
+
+
+Postel & Reynolds [Page 62]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ CWD command. Unfortunately, the argument to MKD may not always be
+ a suitable argument for CWD. This is the case, for example, when
+ a TOPS-20 subdirectory is created by giving just the subdirectory
+ name. That is, with a TOPS-20 server FTP, the command sequence
+
+ MKD MYDIR
+ CWD MYDIR
+
+ will fail. The new directory may only be referred to by its
+ "absolute" name; e.g., if the MKD command above were issued while
+ connected to the directory <DFRANKLIN>, the new subdirectory
+ could only be referred to by the name <DFRANKLIN.MYDIR>.
+
+ Even on UNIX and Multics, however, the argument given to MKD may
+ not be suitable. If it is a "relative" pathname (i.e., a pathname
+ which is interpreted relative to the current directory), the user
+ would need to be in the same current directory in order to reach
+ the subdirectory. Depending on the application, this may be
+ inconvenient. It is not very robust in any case.
+
+ To solve these problems, upon successful completion of an MKD
+ command, the server should return a line of the form:
+
+ 257<space>"<directory-name>"<space><commentary>
+
+ That is, the server will tell the user what string to use when
+ referring to the created directory. The directory name can
+ contain any character; embedded double-quotes should be escaped by
+ double-quotes (the "quote-doubling" convention).
+
+ For example, a user connects to the directory /usr/dm, and creates
+ a subdirectory, named pathname:
+
+ CWD /usr/dm
+ 200 directory changed to /usr/dm
+ MKD pathname
+ 257 "/usr/dm/pathname" directory created
+
+ An example with an embedded double quote:
+
+ MKD foo"bar
+ 257 "/usr/dm/foo""bar" directory created
+ CWD /usr/dm/foo"bar
+ 200 directory changed to /usr/dm/foo"bar
+
+
+
+
+
+Postel & Reynolds [Page 63]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ The prior existence of a subdirectory with the same name is an
+ error, and the server must return an "access denied" error reply
+ in that case.
+
+ CWD /usr/dm
+ 200 directory changed to /usr/dm
+ MKD pathname
+ 521-"/usr/dm/pathname" directory already exists;
+ 521 taking no action.
+
+ The failure replies for MKD are analogous to its file creating
+ cousin, STOR. Also, an "access denied" return is given if a file
+ name with the same name as the subdirectory will conflict with the
+ creation of the subdirectory (this is a problem on UNIX, but
+ shouldn't be one on TOPS-20).
+
+ Essentially because the PWD command returns the same type of
+ information as the successful MKD command, the successful PWD
+ command uses the 257 reply code as well.
+
+ SUBTLETIES
+
+ Because these commands will be most useful in transferring
+ subtrees from one machine to another, carefully observe that the
+ argument to MKD is to be interpreted as a sub-directory of the
+ current working directory, unless it contains enough information
+ for the destination host to tell otherwise. A hypothetical
+ example of its use in the TOPS-20 world:
+
+ CWD <some.where>
+ 200 Working directory changed
+ MKD overrainbow
+ 257 "<some.where.overrainbow>" directory created
+ CWD overrainbow
+ 431 No such directory
+ CWD <some.where.overrainbow>
+ 200 Working directory changed
+
+ CWD <some.where>
+ 200 Working directory changed to <some.where>
+ MKD <unambiguous>
+ 257 "<unambiguous>" directory created
+ CWD <unambiguous>
+
+ Note that the first example results in a subdirectory of the
+ connected directory. In contrast, the argument in the second
+ example contains enough information for TOPS-20 to tell that the
+
+
+Postel & Reynolds [Page 64]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ <unambiguous> directory is a top-level directory. Note also that
+ in the first example the user "violated" the protocol by
+ attempting to access the freshly created directory with a name
+ other than the one returned by TOPS-20. Problems could have
+ resulted in this case had there been an <overrainbow> directory;
+ this is an ambiguity inherent in some TOPS-20 implementations.
+ Similar considerations apply to the RMD command. The point is
+ this: except where to do so would violate a host's conventions for
+ denoting relative versus absolute pathnames, the host should treat
+ the operands of the MKD and RMD commands as subdirectories. The
+ 257 reply to the MKD command must always contain the absolute
+ pathname of the created directory.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 65]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+APPENDIX III - RFCs on FTP
+
+ Bhushan, Abhay, "A File Transfer Protocol", RFC 114 (NIC 5823),
+ MIT-Project MAC, 16 April 1971.
+
+ Harslem, Eric, and John Heafner, "Comments on RFC 114 (A File
+ Transfer Protocol)", RFC 141 (NIC 6726), RAND, 29 April 1971.
+
+ Bhushan, Abhay, et al, "The File Transfer Protocol", RFC 172
+ (NIC 6794), MIT-Project MAC, 23 June 1971.
+
+ Braden, Bob, "Comments on DTP and FTP Proposals", RFC 238 (NIC 7663),
+ UCLA/CCN, 29 September 1971.
+
+ Bhushan, Abhay, et al, "The File Transfer Protocol", RFC 265
+ (NIC 7813), MIT-Project MAC, 17 November 1971.
+
+ McKenzie, Alex, "A Suggested Addition to File Transfer Protocol",
+ RFC 281 (NIC 8163), BBN, 8 December 1971.
+
+ Bhushan, Abhay, "The Use of "Set Data Type" Transaction in File
+ Transfer Protocol", RFC 294 (NIC 8304), MIT-Project MAC,
+ 25 January 1972.
+
+ Bhushan, Abhay, "The File Transfer Protocol", RFC 354 (NIC 10596),
+ MIT-Project MAC, 8 July 1972.
+
+ Bhushan, Abhay, "Comments on the File Transfer Protocol (RFC 354)",
+ RFC 385 (NIC 11357), MIT-Project MAC, 18 August 1972.
+
+ Hicks, Greg, "User FTP Documentation", RFC 412 (NIC 12404), Utah,
+ 27 November 1972.
+
+ Bhushan, Abhay, "File Transfer Protocol (FTP) Status and Further
+ Comments", RFC 414 (NIC 12406), MIT-Project MAC, 20 November 1972.
+
+ Braden, Bob, "Comments on File Transfer Protocol", RFC 430
+ (NIC 13299), UCLA/CCN, 7 February 1973.
+
+ Thomas, Bob, and Bob Clements, "FTP Server-Server Interaction",
+ RFC 438 (NIC 13770), BBN, 15 January 1973.
+
+ Braden, Bob, "Print Files in FTP", RFC 448 (NIC 13299), UCLA/CCN,
+ 27 February 1973.
+
+ McKenzie, Alex, "File Transfer Protocol", RFC 454 (NIC 14333), BBN,
+ 16 February 1973.
+
+
+Postel & Reynolds [Page 66]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ Bressler, Bob, and Bob Thomas, "Mail Retrieval via FTP", RFC 458
+ (NIC 14378), BBN-NET and BBN-TENEX, 20 February 1973.
+
+ Neigus, Nancy, "File Transfer Protocol", RFC 542 (NIC 17759), BBN,
+ 12 July 1973.
+
+ Krilanovich, Mark, and George Gregg, "Comments on the File Transfer
+ Protocol", RFC 607 (NIC 21255), UCSB, 7 January 1974.
+
+ Pogran, Ken, and Nancy Neigus, "Response to RFC 607 - Comments on the
+ File Transfer Protocol", RFC 614 (NIC 21530), BBN, 28 January 1974.
+
+ Krilanovich, Mark, George Gregg, Wayne Hathaway, and Jim White,
+ "Comments on the File Transfer Protocol", RFC 624 (NIC 22054), UCSB,
+ Ames Research Center, SRI-ARC, 28 February 1974.
+
+ Bhushan, Abhay, "FTP Comments and Response to RFC 430", RFC 463
+ (NIC 14573), MIT-DMCG, 21 February 1973.
+
+ Braden, Bob, "FTP Data Compression", RFC 468 (NIC 14742), UCLA/CCN,
+ 8 March 1973.
+
+ Bhushan, Abhay, "FTP and Network Mail System", RFC 475 (NIC 14919),
+ MIT-DMCG, 6 March 1973.
+
+ Bressler, Bob, and Bob Thomas "FTP Server-Server Interaction - II",
+ RFC 478 (NIC 14947), BBN-NET and BBN-TENEX, 26 March 1973.
+
+ White, Jim, "Use of FTP by the NIC Journal", RFC 479 (NIC 14948),
+ SRI-ARC, 8 March 1973.
+
+ White, Jim, "Host-Dependent FTP Parameters", RFC 480 (NIC 14949),
+ SRI-ARC, 8 March 1973.
+
+ Padlipsky, Mike, "An FTP Command-Naming Problem", RFC 506
+ (NIC 16157), MIT-Multics, 26 June 1973.
+
+ Day, John, "Memo to FTP Group (Proposal for File Access Protocol)",
+ RFC 520 (NIC 16819), Illinois, 25 June 1973.
+
+ Merryman, Robert, "The UCSD-CC Server-FTP Facility", RFC 532
+ (NIC 17451), UCSD-CC, 22 June 1973.
+
+ Braden, Bob, "TENEX FTP Problem", RFC 571 (NIC 18974), UCLA/CCN,
+ 15 November 1973.
+
+
+
+
+Postel & Reynolds [Page 67]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+ McKenzie, Alex, and Jon Postel, "Telnet and FTP Implementation -
+ Schedule Change", RFC 593 (NIC 20615), BBN and MITRE,
+ 29 November 1973.
+
+ Sussman, Julie, "FTP Error Code Usage for More Reliable Mail
+ Service", RFC 630 (NIC 30237), BBN, 10 April 1974.
+
+ Postel, Jon, "Revised FTP Reply Codes", RFC 640 (NIC 30843),
+ UCLA/NMC, 5 June 1974.
+
+ Harvey, Brian, "Leaving Well Enough Alone", RFC 686 (NIC 32481),
+ SU-AI, 10 May 1975.
+
+ Harvey, Brian, "One More Try on the FTP", RFC 691 (NIC 32700), SU-AI,
+ 28 May 1975.
+
+ Lieb, J., "CWD Command of FTP", RFC 697 (NIC 32963), 14 July 1975.
+
+ Harrenstien, Ken, "FTP Extension: XSEN", RFC 737 (NIC 42217), SRI-KL,
+ 31 October 1977.
+
+ Harrenstien, Ken, "FTP Extension: XRSQ/XRCP", RFC 743 (NIC 42758),
+ SRI-KL, 30 December 1977.
+
+ Lebling, P. David, "Survey of FTP Mail and MLFL", RFC 751, MIT,
+ 10 December 1978.
+
+ Postel, Jon, "File Transfer Protocol Specification", RFC 765, ISI,
+ June 1980.
+
+ Mankins, David, Dan Franklin, and Buzz Owen, "Directory Oriented FTP
+ Commands", RFC 776, BBN, December 1980.
+
+ Padlipsky, Michael, "FTP Unique-Named Store Command", RFC 949, MITRE,
+ July 1985.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 68]
+
+
+
+RFC 959 October 1985
+File Transfer Protocol
+
+
+REFERENCES
+
+ [1] Feinler, Elizabeth, "Internet Protocol Transition Workbook",
+ Network Information Center, SRI International, March 1982.
+
+ [2] Postel, Jon, "Transmission Control Protocol - DARPA Internet
+ Program Protocol Specification", RFC 793, DARPA, September 1981.
+
+ [3] Postel, Jon, and Joyce Reynolds, "Telnet Protocol
+ Specification", RFC 854, ISI, May 1983.
+
+ [4] Reynolds, Joyce, and Jon Postel, "Assigned Numbers", RFC 943,
+ ISI, April 1985.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Postel & Reynolds [Page 69]
+
diff --git a/netwerk/protocol/ftp/doc/testdoc b/netwerk/protocol/ftp/doc/testdoc
new file mode 100644
index 000000000..61fda16fc
--- /dev/null
+++ b/netwerk/protocol/ftp/doc/testdoc
@@ -0,0 +1,4 @@
+Test
+here
+there
+everywhere
diff --git a/netwerk/protocol/ftp/ftpCore.h b/netwerk/protocol/ftp/ftpCore.h
new file mode 100644
index 000000000..3f708952a
--- /dev/null
+++ b/netwerk/protocol/ftp/ftpCore.h
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 __ftpCore_h___
+#define __ftpCore_h___
+
+#include "nsError.h"
+
+/**
+ * Status nsresult codes
+ */
+
+#endif // __ftpCore_h___
diff --git a/netwerk/protocol/ftp/moz.build b/netwerk/protocol/ftp/moz.build
new file mode 100644
index 000000000..060fb7575
--- /dev/null
+++ b/netwerk/protocol/ftp/moz.build
@@ -0,0 +1,45 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+ 'nsIFTPChannel.idl',
+ 'nsIFTPChannelParentInternal.idl',
+]
+
+XPIDL_MODULE = 'necko_ftp'
+
+EXPORTS += [
+ 'ftpCore.h',
+]
+
+EXPORTS.mozilla.net += [
+ 'FTPChannelChild.h',
+ 'FTPChannelParent.h',
+]
+
+UNIFIED_SOURCES += [
+ 'FTPChannelChild.cpp',
+ 'FTPChannelParent.cpp',
+ 'nsFTPChannel.cpp',
+ 'nsFtpConnectionThread.cpp',
+ 'nsFtpControlConnection.cpp',
+ 'nsFtpProtocolHandler.cpp',
+]
+
+IPDL_SOURCES += [
+ 'PFTPChannel.ipdl',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+ '/netwerk/base',
+]
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/netwerk/protocol/ftp/nsFTPChannel.cpp b/netwerk/protocol/ftp/nsFTPChannel.cpp
new file mode 100644
index 000000000..2a0f04915
--- /dev/null
+++ b/netwerk/protocol/ftp/nsFTPChannel.cpp
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sts=4 sw=4 et cin: */
+/* 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 "nsFTPChannel.h"
+#include "nsFtpConnectionThread.h" // defines nsFtpState
+
+#include "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+
+using namespace mozilla;
+using namespace mozilla::net;
+extern LazyLogModule gFTPLog;
+
+// There are two transport connections established for an
+// ftp connection. One is used for the command channel , and
+// the other for the data channel. The command channel is the first
+// connection made and is used to negotiate the second, data, channel.
+// The data channel is driven by the command channel and is either
+// initiated by the server (PORT command) or by the client (PASV command).
+// Client initiation is the most common case and is attempted first.
+
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS_INHERITED(nsFtpChannel,
+ nsBaseChannel,
+ nsIUploadChannel,
+ nsIResumableChannel,
+ nsIFTPChannel,
+ nsIProxiedChannel,
+ nsIForcePendingChannel,
+ nsIChannelWithDivertableParentListener)
+
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsFtpChannel::SetUploadStream(nsIInputStream *stream,
+ const nsACString &contentType,
+ int64_t contentLength)
+{
+ NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS);
+
+ mUploadStream = stream;
+
+ // NOTE: contentLength is intentionally ignored here.
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFtpChannel::GetUploadStream(nsIInputStream **stream)
+{
+ NS_ENSURE_ARG_POINTER(stream);
+ *stream = mUploadStream;
+ NS_IF_ADDREF(*stream);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsFtpChannel::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID)
+{
+ NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS);
+ mEntityID = aEntityID;
+ mStartPos = aStartPos;
+ mResumeRequested = (mStartPos || !mEntityID.IsEmpty());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFtpChannel::GetEntityID(nsACString& entityID)
+{
+ if (mEntityID.IsEmpty())
+ return NS_ERROR_NOT_RESUMABLE;
+
+ entityID = mEntityID;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+NS_IMETHODIMP
+nsFtpChannel::GetProxyInfo(nsIProxyInfo** aProxyInfo)
+{
+ *aProxyInfo = ProxyInfo();
+ NS_IF_ADDREF(*aProxyInfo);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+nsresult
+nsFtpChannel::OpenContentStream(bool async, nsIInputStream **result,
+ nsIChannel** channel)
+{
+ if (!async)
+ return NS_ERROR_NOT_IMPLEMENTED;
+
+ nsFtpState *state = new nsFtpState();
+ if (!state)
+ return NS_ERROR_OUT_OF_MEMORY;
+ NS_ADDREF(state);
+
+ nsresult rv = state->Init(this);
+ if (NS_FAILED(rv)) {
+ NS_RELEASE(state);
+ return rv;
+ }
+
+ *result = state;
+ return NS_OK;
+}
+
+bool
+nsFtpChannel::GetStatusArg(nsresult status, nsString &statusArg)
+{
+ nsAutoCString host;
+ URI()->GetHost(host);
+ CopyUTF8toUTF16(host, statusArg);
+ return true;
+}
+
+void
+nsFtpChannel::OnCallbacksChanged()
+{
+ mFTPEventSink = nullptr;
+}
+
+//-----------------------------------------------------------------------------
+
+namespace {
+
+class FTPEventSinkProxy final : public nsIFTPEventSink
+{
+ ~FTPEventSinkProxy() {}
+
+public:
+ explicit FTPEventSinkProxy(nsIFTPEventSink* aTarget)
+ : mTarget(aTarget)
+ , mTargetThread(do_GetCurrentThread())
+ { }
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIFTPEVENTSINK
+
+ class OnFTPControlLogRunnable : public Runnable
+ {
+ public:
+ OnFTPControlLogRunnable(nsIFTPEventSink* aTarget,
+ bool aServer,
+ const char* aMessage)
+ : mTarget(aTarget)
+ , mServer(aServer)
+ , mMessage(aMessage)
+ { }
+
+ NS_DECL_NSIRUNNABLE
+
+ private:
+ nsCOMPtr<nsIFTPEventSink> mTarget;
+ bool mServer;
+ nsCString mMessage;
+ };
+
+private:
+ nsCOMPtr<nsIFTPEventSink> mTarget;
+ nsCOMPtr<nsIThread> mTargetThread;
+};
+
+NS_IMPL_ISUPPORTS(FTPEventSinkProxy, nsIFTPEventSink)
+
+NS_IMETHODIMP
+FTPEventSinkProxy::OnFTPControlLog(bool aServer, const char* aMsg)
+{
+ RefPtr<OnFTPControlLogRunnable> r =
+ new OnFTPControlLogRunnable(mTarget, aServer, aMsg);
+ return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
+}
+
+NS_IMETHODIMP
+FTPEventSinkProxy::OnFTPControlLogRunnable::Run()
+{
+ mTarget->OnFTPControlLog(mServer, mMessage.get());
+ return NS_OK;
+}
+
+} // namespace
+
+void
+nsFtpChannel::GetFTPEventSink(nsCOMPtr<nsIFTPEventSink> &aResult)
+{
+ if (!mFTPEventSink) {
+ nsCOMPtr<nsIFTPEventSink> ftpSink;
+ GetCallback(ftpSink);
+ if (ftpSink) {
+ mFTPEventSink = new FTPEventSinkProxy(ftpSink);
+ }
+ }
+ aResult = mFTPEventSink;
+}
+
+NS_IMETHODIMP
+nsFtpChannel::ForcePending(bool aForcePending)
+{
+ // Set true here so IsPending will return true.
+ // Required for callback diversion from child back to parent. In such cases
+ // OnStopRequest can be called in the parent before callbacks are diverted
+ // back from the child to the listener in the parent.
+ mForcePending = aForcePending;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFtpChannel::IsPending(bool *result)
+{
+ *result = Pending();
+ return NS_OK;
+}
+
+bool
+nsFtpChannel::Pending() const
+{
+ return nsBaseChannel::Pending() || mForcePending;
+}
+
+NS_IMETHODIMP
+nsFtpChannel::Suspend()
+{
+ LOG(("nsFtpChannel::Suspend [this=%p]\n", this));
+
+ nsresult rv = nsBaseChannel::Suspend();
+
+ nsresult rvParentChannel = NS_OK;
+ if (mParentChannel) {
+ rvParentChannel = mParentChannel->SuspendMessageDiversion();
+ }
+
+ return NS_FAILED(rv) ? rv : rvParentChannel;
+}
+
+NS_IMETHODIMP
+nsFtpChannel::Resume()
+{
+ LOG(("nsFtpChannel::Resume [this=%p]\n", this));
+
+ nsresult rv = nsBaseChannel::Resume();
+
+ nsresult rvParentChannel = NS_OK;
+ if (mParentChannel) {
+ rvParentChannel = mParentChannel->ResumeMessageDiversion();
+ }
+
+ return NS_FAILED(rv) ? rv : rvParentChannel;
+}
+
+//-----------------------------------------------------------------------------
+// AChannelHasDivertableParentChannelAsListener internal functions
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsFtpChannel::MessageDiversionStarted(ADivertableParentChannel *aParentChannel)
+{
+ MOZ_ASSERT(!mParentChannel);
+ mParentChannel = aParentChannel;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFtpChannel::MessageDiversionStop()
+{
+ LOG(("nsFtpChannel::MessageDiversionStop [this=%p]", this));
+ MOZ_ASSERT(mParentChannel);
+ mParentChannel = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFtpChannel::SuspendInternal()
+{
+ LOG(("nsFtpChannel::SuspendInternal [this=%p]\n", this));
+
+ return nsBaseChannel::Suspend();
+}
+
+NS_IMETHODIMP
+nsFtpChannel::ResumeInternal()
+{
+ LOG(("nsFtpChannel::ResumeInternal [this=%p]\n", this));
+
+ return nsBaseChannel::Resume();
+}
diff --git a/netwerk/protocol/ftp/nsFTPChannel.h b/netwerk/protocol/ftp/nsFTPChannel.h
new file mode 100644
index 000000000..549e577b3
--- /dev/null
+++ b/netwerk/protocol/ftp/nsFTPChannel.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et cindent: */
+/* 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 nsFTPChannel_h___
+#define nsFTPChannel_h___
+
+#include "nsBaseChannel.h"
+
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsIChannelWithDivertableParentListener.h"
+#include "nsIFTPChannel.h"
+#include "nsIForcePendingChannel.h"
+#include "nsIUploadChannel.h"
+#include "nsIProxyInfo.h"
+#include "nsIProxiedChannel.h"
+#include "nsIResumableChannel.h"
+
+class nsIURI;
+using mozilla::net::ADivertableParentChannel;
+
+class nsFtpChannel final : public nsBaseChannel,
+ public nsIFTPChannel,
+ public nsIUploadChannel,
+ public nsIResumableChannel,
+ public nsIProxiedChannel,
+ public nsIForcePendingChannel,
+ public nsIChannelWithDivertableParentListener
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIUPLOADCHANNEL
+ NS_DECL_NSIRESUMABLECHANNEL
+ NS_DECL_NSIPROXIEDCHANNEL
+ NS_DECL_NSICHANNELWITHDIVERTABLEPARENTLISTENER
+
+ nsFtpChannel(nsIURI *uri, nsIProxyInfo *pi)
+ : mProxyInfo(pi)
+ , mStartPos(0)
+ , mResumeRequested(false)
+ , mLastModifiedTime(0)
+ , mForcePending(false)
+ {
+ SetURI(uri);
+ }
+
+ nsIProxyInfo *ProxyInfo() {
+ return mProxyInfo;
+ }
+
+ void SetProxyInfo(nsIProxyInfo *pi)
+ {
+ mProxyInfo = pi;
+ }
+
+ NS_IMETHOD IsPending(bool *result) override;
+
+ // This is a short-cut to calling nsIRequest::IsPending().
+ // Overrides Pending in nsBaseChannel.
+ bool Pending() const override;
+
+ // Were we asked to resume a download?
+ bool ResumeRequested() { return mResumeRequested; }
+
+ // Download from this byte offset
+ uint64_t StartPos() { return mStartPos; }
+
+ // ID of the entity to resume downloading
+ const nsCString &EntityID() {
+ return mEntityID;
+ }
+ void SetEntityID(const nsCSubstring &entityID) {
+ mEntityID = entityID;
+ }
+
+ NS_IMETHOD GetLastModifiedTime(PRTime* lastModifiedTime) override {
+ *lastModifiedTime = mLastModifiedTime;
+ return NS_OK;
+ }
+
+ NS_IMETHOD SetLastModifiedTime(PRTime lastModifiedTime) override {
+ mLastModifiedTime = lastModifiedTime;
+ return NS_OK;
+ }
+
+ // Data stream to upload
+ nsIInputStream *UploadStream() {
+ return mUploadStream;
+ }
+
+ // Helper function for getting the nsIFTPEventSink.
+ void GetFTPEventSink(nsCOMPtr<nsIFTPEventSink> &aResult);
+
+ NS_IMETHOD Suspend() override;
+ NS_IMETHOD Resume() override;
+
+public:
+ NS_IMETHOD ForcePending(bool aForcePending) override;
+
+protected:
+ virtual ~nsFtpChannel() {}
+ virtual nsresult OpenContentStream(bool async, nsIInputStream **result,
+ nsIChannel** channel) override;
+ virtual bool GetStatusArg(nsresult status, nsString &statusArg) override;
+ virtual void OnCallbacksChanged() override;
+
+private:
+ nsCOMPtr<nsIProxyInfo> mProxyInfo;
+ nsCOMPtr<nsIFTPEventSink> mFTPEventSink;
+ nsCOMPtr<nsIInputStream> mUploadStream;
+ uint64_t mStartPos;
+ nsCString mEntityID;
+ bool mResumeRequested;
+ PRTime mLastModifiedTime;
+ bool mForcePending;
+ RefPtr<ADivertableParentChannel> mParentChannel;
+};
+
+#endif /* nsFTPChannel_h___ */
diff --git a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
new file mode 100644
index 000000000..d428b093c
--- /dev/null
+++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
@@ -0,0 +1,2256 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set tw=80 ts=4 sts=4 sw=4 et cin: */
+/* 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 <ctype.h>
+
+#include "prprf.h"
+#include "mozilla/Logging.h"
+#include "prtime.h"
+
+#include "nsIOService.h"
+#include "nsFTPChannel.h"
+#include "nsFtpConnectionThread.h"
+#include "nsFtpControlConnection.h"
+#include "nsFtpProtocolHandler.h"
+#include "netCore.h"
+#include "nsCRT.h"
+#include "nsEscape.h"
+#include "nsMimeTypes.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "nsIAsyncStreamCopier.h"
+#include "nsThreadUtils.h"
+#include "nsStreamUtils.h"
+#include "nsIURL.h"
+#include "nsISocketTransport.h"
+#include "nsIStreamListenerTee.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIStringBundle.h"
+#include "nsAuthInformationHolder.h"
+#include "nsIProtocolProxyService.h"
+#include "nsICancelable.h"
+#include "nsIOutputStream.h"
+#include "nsIPrompt.h"
+#include "nsIProtocolHandler.h"
+#include "nsIProxyInfo.h"
+#include "nsIRunnable.h"
+#include "nsISocketTransportService.h"
+#include "nsIURI.h"
+#include "nsILoadInfo.h"
+#include "nsNullPrincipal.h"
+#include "nsIAuthPrompt2.h"
+#include "nsIFTPChannelParentInternal.h"
+
+#ifdef MOZ_WIDGET_GONK
+#include "NetStatistics.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::net;
+
+extern LazyLogModule gFTPLog;
+#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
+#define LOG_INFO(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Info, args)
+
+// remove FTP parameters (starting with ";") from the path
+static void
+removeParamsFromPath(nsCString& path)
+{
+ int32_t index = path.FindChar(';');
+ if (index >= 0) {
+ path.SetLength(index);
+ }
+}
+
+NS_IMPL_ISUPPORTS_INHERITED(nsFtpState,
+ nsBaseContentStream,
+ nsIInputStreamCallback,
+ nsITransportEventSink,
+ nsIRequestObserver,
+ nsIProtocolProxyCallback)
+
+nsFtpState::nsFtpState()
+ : nsBaseContentStream(true)
+ , mState(FTP_INIT)
+ , mNextState(FTP_S_USER)
+ , mKeepRunning(true)
+ , mReceivedControlData(false)
+ , mTryingCachedControl(false)
+ , mRETRFailed(false)
+ , mFileSize(kJS_MAX_SAFE_UINTEGER)
+ , mServerType(FTP_GENERIC_TYPE)
+ , mAction(GET)
+ , mAnonymous(true)
+ , mRetryPass(false)
+ , mStorReplyReceived(false)
+ , mInternalError(NS_OK)
+ , mReconnectAndLoginAgain(false)
+ , mCacheConnection(true)
+ , mPort(21)
+ , mAddressChecked(false)
+ , mServerIsIPv6(false)
+ , mUseUTF8(false)
+ , mControlStatus(NS_OK)
+ , mDeferredCallbackPending(false)
+{
+ LOG_INFO(("FTP:(%x) nsFtpState created", this));
+
+ // make sure handler stays around
+ NS_ADDREF(gFtpHandler);
+}
+
+nsFtpState::~nsFtpState()
+{
+ LOG_INFO(("FTP:(%x) nsFtpState destroyed", this));
+
+ if (mProxyRequest)
+ mProxyRequest->Cancel(NS_ERROR_FAILURE);
+
+ // release reference to handler
+ nsFtpProtocolHandler *handler = gFtpHandler;
+ NS_RELEASE(handler);
+}
+
+// nsIInputStreamCallback implementation
+NS_IMETHODIMP
+nsFtpState::OnInputStreamReady(nsIAsyncInputStream *aInStream)
+{
+ LOG(("FTP:(%p) data stream ready\n", this));
+
+ // We are receiving a notification from our data stream, so just forward it
+ // on to our stream callback.
+ if (HasPendingCallback())
+ DispatchCallbackSync();
+
+ return NS_OK;
+}
+
+void
+nsFtpState::OnControlDataAvailable(const char *aData, uint32_t aDataLen)
+{
+ LOG(("FTP:(%p) control data available [%u]\n", this, aDataLen));
+ mControlConnection->WaitData(this); // queue up another call
+
+ if (!mReceivedControlData) {
+ // parameter can be null cause the channel fills them in.
+ OnTransportStatus(nullptr, NS_NET_STATUS_BEGIN_FTP_TRANSACTION, 0, 0);
+ mReceivedControlData = true;
+ }
+
+ // Sometimes we can get two responses in the same packet, eg from LIST.
+ // So we need to parse the response line by line
+
+ nsCString buffer = mControlReadCarryOverBuf;
+
+ // Clear the carryover buf - if we still don't have a line, then it will
+ // be reappended below
+ mControlReadCarryOverBuf.Truncate();
+
+ buffer.Append(aData, aDataLen);
+
+ const char* currLine = buffer.get();
+ while (*currLine && mKeepRunning) {
+ int32_t eolLength = strcspn(currLine, CRLF);
+ int32_t currLineLength = strlen(currLine);
+
+ // if currLine is empty or only contains CR or LF, then bail. we can
+ // sometimes get an ODA event with the full response line + CR without
+ // the trailing LF. the trailing LF might come in the next ODA event.
+ // because we are happy enough to process a response line ending only
+ // in CR, we need to take care to discard the extra LF (bug 191220).
+ if (eolLength == 0 && currLineLength <= 1)
+ break;
+
+ if (eolLength == currLineLength) {
+ mControlReadCarryOverBuf.Assign(currLine);
+ break;
+ }
+
+ // Append the current segment, including the LF
+ nsAutoCString line;
+ int32_t crlfLength = 0;
+
+ if ((currLineLength > eolLength) &&
+ (currLine[eolLength] == nsCRT::CR) &&
+ (currLine[eolLength+1] == nsCRT::LF)) {
+ crlfLength = 2; // CR +LF
+ } else {
+ crlfLength = 1; // + LF or CR
+ }
+
+ line.Assign(currLine, eolLength + crlfLength);
+
+ // Does this start with a response code?
+ bool startNum = (line.Length() >= 3 &&
+ isdigit(line[0]) &&
+ isdigit(line[1]) &&
+ isdigit(line[2]));
+
+ if (mResponseMsg.IsEmpty()) {
+ // If we get here, then we know that we have a complete line, and
+ // that it is the first one
+
+ NS_ASSERTION(line.Length() > 4 && startNum,
+ "Read buffer doesn't include response code");
+
+ mResponseCode = atoi(PromiseFlatCString(Substring(line,0,3)).get());
+ }
+
+ mResponseMsg.Append(line);
+
+ // This is the last line if its 3 numbers followed by a space
+ if (startNum && line[3] == ' ') {
+ // yup. last line, let's move on.
+ if (mState == mNextState) {
+ NS_ERROR("ftp read state mixup");
+ mInternalError = NS_ERROR_FAILURE;
+ mState = FTP_ERROR;
+ } else {
+ mState = mNextState;
+ }
+
+ nsCOMPtr<nsIFTPEventSink> ftpSink;
+ mChannel->GetFTPEventSink(ftpSink);
+ if (ftpSink)
+ ftpSink->OnFTPControlLog(true, mResponseMsg.get());
+
+ nsresult rv = Process();
+ mResponseMsg.Truncate();
+ if (NS_FAILED(rv)) {
+ CloseWithStatus(rv);
+ return;
+ }
+ }
+
+ currLine = currLine + eolLength + crlfLength;
+ }
+}
+
+void
+nsFtpState::OnControlError(nsresult status)
+{
+ NS_ASSERTION(NS_FAILED(status), "expecting error condition");
+
+ LOG(("FTP:(%p) CC(%p) error [%x was-cached=%u]\n",
+ this, mControlConnection.get(), status, mTryingCachedControl));
+
+ mControlStatus = status;
+ if (mReconnectAndLoginAgain && NS_SUCCEEDED(mInternalError)) {
+ mReconnectAndLoginAgain = false;
+ mAnonymous = false;
+ mControlStatus = NS_OK;
+ Connect();
+ } else if (mTryingCachedControl && NS_SUCCEEDED(mInternalError)) {
+ mTryingCachedControl = false;
+ Connect();
+ } else {
+ CloseWithStatus(status);
+ }
+}
+
+nsresult
+nsFtpState::EstablishControlConnection()
+{
+ NS_ASSERTION(!mControlConnection, "we already have a control connection");
+
+ nsresult rv;
+
+ LOG(("FTP:(%x) trying cached control\n", this));
+
+ // Look to see if we can use a cached control connection:
+ RefPtr<nsFtpControlConnection> connection;
+ // Don't use cached control if anonymous (bug #473371)
+ if (!mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
+ gFtpHandler->RemoveConnection(mChannel->URI(), getter_AddRefs(connection));
+
+ if (connection) {
+ mControlConnection.swap(connection);
+ if (mControlConnection->IsAlive())
+ {
+ // set stream listener of the control connection to be us.
+ mControlConnection->WaitData(this);
+
+ // read cached variables into us.
+ mServerType = mControlConnection->mServerType;
+ mPassword = mControlConnection->mPassword;
+ mPwd = mControlConnection->mPwd;
+ mUseUTF8 = mControlConnection->mUseUTF8;
+ mTryingCachedControl = true;
+
+ // we have to set charset to connection if server supports utf-8
+ if (mUseUTF8)
+ mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
+
+ // we're already connected to this server, skip login.
+ mState = FTP_S_PASV;
+ mResponseCode = 530; // assume the control connection was dropped.
+ mControlStatus = NS_OK;
+ mReceivedControlData = false; // For this request, we have not.
+
+ // if we succeed, return. Otherwise, we need to create a transport
+ rv = mControlConnection->Connect(mChannel->ProxyInfo(), this);
+ if (NS_SUCCEEDED(rv))
+ return rv;
+ }
+ LOG(("FTP:(%p) cached CC(%p) is unusable\n", this,
+ mControlConnection.get()));
+
+ mControlConnection->WaitData(nullptr);
+ mControlConnection = nullptr;
+ }
+
+ LOG(("FTP:(%p) creating CC\n", this));
+
+ mState = FTP_READ_BUF;
+ mNextState = FTP_S_USER;
+
+ nsAutoCString host;
+ rv = mChannel->URI()->GetAsciiHost(host);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mControlConnection = new nsFtpControlConnection(host, mPort);
+ if (!mControlConnection)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = mControlConnection->Connect(mChannel->ProxyInfo(), this);
+ if (NS_FAILED(rv)) {
+ LOG(("FTP:(%p) CC(%p) failed to connect [rv=%x]\n", this,
+ mControlConnection.get(), rv));
+ mControlConnection = nullptr;
+ return rv;
+ }
+
+ return mControlConnection->WaitData(this);
+}
+
+void
+nsFtpState::MoveToNextState(FTP_STATE nextState)
+{
+ if (NS_FAILED(mInternalError)) {
+ mState = FTP_ERROR;
+ LOG(("FTP:(%x) FAILED (%x)\n", this, mInternalError));
+ } else {
+ mState = FTP_READ_BUF;
+ mNextState = nextState;
+ }
+}
+
+nsresult
+nsFtpState::Process()
+{
+ nsresult rv = NS_OK;
+ bool processingRead = true;
+
+ while (mKeepRunning && processingRead) {
+ switch (mState) {
+ case FTP_COMMAND_CONNECT:
+ KillControlConnection();
+ LOG(("FTP:(%p) establishing CC", this));
+ mInternalError = EstablishControlConnection(); // sets mState
+ if (NS_FAILED(mInternalError)) {
+ mState = FTP_ERROR;
+ LOG(("FTP:(%p) FAILED\n", this));
+ } else {
+ LOG(("FTP:(%p) SUCCEEDED\n", this));
+ }
+ break;
+
+ case FTP_READ_BUF:
+ LOG(("FTP:(%p) Waiting for CC(%p)\n", this,
+ mControlConnection.get()));
+ processingRead = false;
+ break;
+
+ case FTP_ERROR: // xx needs more work to handle dropped control connection cases
+ if ((mTryingCachedControl && mResponseCode == 530 &&
+ mInternalError == NS_ERROR_FTP_PASV) ||
+ (mResponseCode == 425 &&
+ mInternalError == NS_ERROR_FTP_PASV)) {
+ // The user was logged out during an pasv operation
+ // we want to restart this request with a new control
+ // channel.
+ mState = FTP_COMMAND_CONNECT;
+ } else if (mResponseCode == 421 &&
+ mInternalError != NS_ERROR_FTP_LOGIN) {
+ // The command channel dropped for some reason.
+ // Fire it back up, unless we were trying to login
+ // in which case the server might just be telling us
+ // that the max number of users has been reached...
+ mState = FTP_COMMAND_CONNECT;
+ } else if (mAnonymous &&
+ mInternalError == NS_ERROR_FTP_LOGIN) {
+ // If the login was anonymous, and it failed, try again with a username
+ // Don't reuse old control connection, see #386167
+ mAnonymous = false;
+ mState = FTP_COMMAND_CONNECT;
+ } else {
+ LOG(("FTP:(%x) FTP_ERROR - calling StopProcessing\n", this));
+ rv = StopProcessing();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "StopProcessing failed.");
+ processingRead = false;
+ }
+ break;
+
+ case FTP_COMPLETE:
+ LOG(("FTP:(%x) COMPLETE\n", this));
+ rv = StopProcessing();
+ NS_ASSERTION(NS_SUCCEEDED(rv), "StopProcessing failed.");
+ processingRead = false;
+ break;
+
+// USER
+ case FTP_S_USER:
+ rv = S_user();
+
+ if (NS_FAILED(rv))
+ mInternalError = NS_ERROR_FTP_LOGIN;
+
+ MoveToNextState(FTP_R_USER);
+ break;
+
+ case FTP_R_USER:
+ mState = R_user();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FTP_LOGIN;
+
+ break;
+// PASS
+ case FTP_S_PASS:
+ rv = S_pass();
+
+ if (NS_FAILED(rv))
+ mInternalError = NS_ERROR_FTP_LOGIN;
+
+ MoveToNextState(FTP_R_PASS);
+ break;
+
+ case FTP_R_PASS:
+ mState = R_pass();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FTP_LOGIN;
+
+ break;
+// ACCT
+ case FTP_S_ACCT:
+ rv = S_acct();
+
+ if (NS_FAILED(rv))
+ mInternalError = NS_ERROR_FTP_LOGIN;
+
+ MoveToNextState(FTP_R_ACCT);
+ break;
+
+ case FTP_R_ACCT:
+ mState = R_acct();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FTP_LOGIN;
+
+ break;
+
+// SYST
+ case FTP_S_SYST:
+ rv = S_syst();
+
+ if (NS_FAILED(rv))
+ mInternalError = NS_ERROR_FTP_LOGIN;
+
+ MoveToNextState(FTP_R_SYST);
+ break;
+
+ case FTP_R_SYST:
+ mState = R_syst();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FTP_LOGIN;
+
+ break;
+
+// TYPE
+ case FTP_S_TYPE:
+ rv = S_type();
+
+ if (NS_FAILED(rv))
+ mInternalError = rv;
+
+ MoveToNextState(FTP_R_TYPE);
+ break;
+
+ case FTP_R_TYPE:
+ mState = R_type();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FAILURE;
+
+ break;
+// CWD
+ case FTP_S_CWD:
+ rv = S_cwd();
+
+ if (NS_FAILED(rv))
+ mInternalError = NS_ERROR_FTP_CWD;
+
+ MoveToNextState(FTP_R_CWD);
+ break;
+
+ case FTP_R_CWD:
+ mState = R_cwd();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FTP_CWD;
+ break;
+
+// LIST
+ case FTP_S_LIST:
+ rv = S_list();
+
+ if (rv == NS_ERROR_NOT_RESUMABLE) {
+ mInternalError = rv;
+ } else if (NS_FAILED(rv)) {
+ mInternalError = NS_ERROR_FTP_CWD;
+ }
+
+ MoveToNextState(FTP_R_LIST);
+ break;
+
+ case FTP_R_LIST:
+ mState = R_list();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FAILURE;
+
+ break;
+
+// SIZE
+ case FTP_S_SIZE:
+ rv = S_size();
+
+ if (NS_FAILED(rv))
+ mInternalError = rv;
+
+ MoveToNextState(FTP_R_SIZE);
+ break;
+
+ case FTP_R_SIZE:
+ mState = R_size();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FAILURE;
+
+ break;
+
+// REST
+ case FTP_S_REST:
+ rv = S_rest();
+
+ if (NS_FAILED(rv))
+ mInternalError = rv;
+
+ MoveToNextState(FTP_R_REST);
+ break;
+
+ case FTP_R_REST:
+ mState = R_rest();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FAILURE;
+
+ break;
+
+// MDTM
+ case FTP_S_MDTM:
+ rv = S_mdtm();
+ if (NS_FAILED(rv))
+ mInternalError = rv;
+ MoveToNextState(FTP_R_MDTM);
+ break;
+
+ case FTP_R_MDTM:
+ mState = R_mdtm();
+
+ // Don't want to overwrite a more explicit status code
+ if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
+ mInternalError = NS_ERROR_FAILURE;
+
+ break;
+
+// RETR
+ case FTP_S_RETR:
+ rv = S_retr();
+
+ if (NS_FAILED(rv))
+ mInternalError = rv;
+
+ MoveToNextState(FTP_R_RETR);
+ break;
+
+ case FTP_R_RETR:
+
+ mState = R_retr();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FAILURE;
+
+ break;
+
+// STOR
+ case FTP_S_STOR:
+ rv = S_stor();
+
+ if (NS_FAILED(rv))
+ mInternalError = rv;
+
+ MoveToNextState(FTP_R_STOR);
+ break;
+
+ case FTP_R_STOR:
+ mState = R_stor();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FAILURE;
+
+ break;
+
+// PASV
+ case FTP_S_PASV:
+ rv = S_pasv();
+
+ if (NS_FAILED(rv))
+ mInternalError = NS_ERROR_FTP_PASV;
+
+ MoveToNextState(FTP_R_PASV);
+ break;
+
+ case FTP_R_PASV:
+ mState = R_pasv();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FTP_PASV;
+
+ break;
+
+// PWD
+ case FTP_S_PWD:
+ rv = S_pwd();
+
+ if (NS_FAILED(rv))
+ mInternalError = NS_ERROR_FTP_PWD;
+
+ MoveToNextState(FTP_R_PWD);
+ break;
+
+ case FTP_R_PWD:
+ mState = R_pwd();
+
+ if (FTP_ERROR == mState)
+ mInternalError = NS_ERROR_FTP_PWD;
+
+ break;
+
+// FEAT for RFC2640 support
+ case FTP_S_FEAT:
+ rv = S_feat();
+
+ if (NS_FAILED(rv))
+ mInternalError = rv;
+
+ MoveToNextState(FTP_R_FEAT);
+ break;
+
+ case FTP_R_FEAT:
+ mState = R_feat();
+
+ // Don't want to overwrite a more explicit status code
+ if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
+ mInternalError = NS_ERROR_FAILURE;
+ break;
+
+// OPTS for some non-RFC2640-compliant servers support
+ case FTP_S_OPTS:
+ rv = S_opts();
+
+ if (NS_FAILED(rv))
+ mInternalError = rv;
+
+ MoveToNextState(FTP_R_OPTS);
+ break;
+
+ case FTP_R_OPTS:
+ mState = R_opts();
+
+ // Don't want to overwrite a more explicit status code
+ if (FTP_ERROR == mState && NS_SUCCEEDED(mInternalError))
+ mInternalError = NS_ERROR_FAILURE;
+ break;
+
+ default:
+ ;
+
+ }
+ }
+
+ return rv;
+}
+
+///////////////////////////////////
+// STATE METHODS
+///////////////////////////////////
+nsresult
+nsFtpState::S_user() {
+ // some servers on connect send us a 421 or 521. (84525) (141784)
+ if ((mResponseCode == 421) || (mResponseCode == 521))
+ return NS_ERROR_FAILURE;
+
+ nsresult rv;
+ nsAutoCString usernameStr("USER ");
+
+ mResponseMsg = "";
+
+ if (mAnonymous) {
+ mReconnectAndLoginAgain = true;
+ usernameStr.AppendLiteral("anonymous");
+ } else {
+ mReconnectAndLoginAgain = false;
+ if (mUsername.IsEmpty()) {
+
+ // No prompt for anonymous requests (bug #473371)
+ if (mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIAuthPrompt2> prompter;
+ NS_QueryAuthPrompt2(static_cast<nsIChannel*>(mChannel),
+ getter_AddRefs(prompter));
+ if (!prompter)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ RefPtr<nsAuthInformationHolder> info =
+ new nsAuthInformationHolder(nsIAuthInformation::AUTH_HOST,
+ EmptyString(),
+ EmptyCString());
+
+ bool retval;
+ rv = prompter->PromptAuth(mChannel, nsIAuthPrompt2::LEVEL_NONE,
+ info, &retval);
+
+ // if the user canceled or didn't supply a username we want to fail
+ if (NS_FAILED(rv) || !retval || info->User().IsEmpty())
+ return NS_ERROR_FAILURE;
+
+ mUsername = info->User();
+ mPassword = info->Password();
+ }
+ // XXX Is UTF-8 the best choice?
+ AppendUTF16toUTF8(mUsername, usernameStr);
+ }
+ usernameStr.Append(CRLF);
+
+ return SendFTPCommand(usernameStr);
+}
+
+FTP_STATE
+nsFtpState::R_user() {
+ mReconnectAndLoginAgain = false;
+ if (mResponseCode/100 == 3) {
+ // send off the password
+ return FTP_S_PASS;
+ }
+ if (mResponseCode/100 == 2) {
+ // no password required, we're already logged in
+ return FTP_S_SYST;
+ }
+ if (mResponseCode/100 == 5) {
+ // problem logging in. typically this means the server
+ // has reached it's user limit.
+ return FTP_ERROR;
+ }
+ // LOGIN FAILED
+ return FTP_ERROR;
+}
+
+
+nsresult
+nsFtpState::S_pass() {
+ nsresult rv;
+ nsAutoCString passwordStr("PASS ");
+
+ mResponseMsg = "";
+
+ if (mAnonymous) {
+ if (!mPassword.IsEmpty()) {
+ // XXX Is UTF-8 the best choice?
+ AppendUTF16toUTF8(mPassword, passwordStr);
+ } else {
+ nsXPIDLCString anonPassword;
+ bool useRealEmail = false;
+ nsCOMPtr<nsIPrefBranch> prefs =
+ do_GetService(NS_PREFSERVICE_CONTRACTID);
+ if (prefs) {
+ rv = prefs->GetBoolPref("advanced.mailftp", &useRealEmail);
+ if (NS_SUCCEEDED(rv) && useRealEmail) {
+ prefs->GetCharPref("network.ftp.anonymous_password",
+ getter_Copies(anonPassword));
+ }
+ }
+ if (!anonPassword.IsEmpty()) {
+ passwordStr.AppendASCII(anonPassword);
+ } else {
+ // We need to default to a valid email address - bug 101027
+ // example.com is reserved (rfc2606), so use that
+ passwordStr.AppendLiteral("mozilla@example.com");
+ }
+ }
+ } else {
+ if (mPassword.IsEmpty() || mRetryPass) {
+
+ // No prompt for anonymous requests (bug #473371)
+ if (mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIAuthPrompt2> prompter;
+ NS_QueryAuthPrompt2(static_cast<nsIChannel*>(mChannel),
+ getter_AddRefs(prompter));
+ if (!prompter)
+ return NS_ERROR_NOT_INITIALIZED;
+
+ RefPtr<nsAuthInformationHolder> info =
+ new nsAuthInformationHolder(nsIAuthInformation::AUTH_HOST |
+ nsIAuthInformation::ONLY_PASSWORD,
+ EmptyString(),
+ EmptyCString());
+
+ info->SetUserInternal(mUsername);
+
+ bool retval;
+ rv = prompter->PromptAuth(mChannel, nsIAuthPrompt2::LEVEL_NONE,
+ info, &retval);
+
+ // we want to fail if the user canceled. Note here that if they want
+ // a blank password, we will pass it along.
+ if (NS_FAILED(rv) || !retval)
+ return NS_ERROR_FAILURE;
+
+ mPassword = info->Password();
+ }
+ // XXX Is UTF-8 the best choice?
+ AppendUTF16toUTF8(mPassword, passwordStr);
+ }
+ passwordStr.Append(CRLF);
+
+ return SendFTPCommand(passwordStr);
+}
+
+FTP_STATE
+nsFtpState::R_pass() {
+ if (mResponseCode/100 == 3) {
+ // send account info
+ return FTP_S_ACCT;
+ }
+ if (mResponseCode/100 == 2) {
+ // logged in
+ return FTP_S_SYST;
+ }
+ if (mResponseCode == 503) {
+ // start over w/ the user command.
+ // note: the password was successful, and it's stored in mPassword
+ mRetryPass = false;
+ return FTP_S_USER;
+ }
+ if (mResponseCode/100 == 5 || mResponseCode==421) {
+ // There is no difference between a too-many-users error,
+ // a wrong-password error, or any other sort of error
+
+ if (!mAnonymous)
+ mRetryPass = true;
+
+ return FTP_ERROR;
+ }
+ // unexpected response code
+ return FTP_ERROR;
+}
+
+nsresult
+nsFtpState::S_pwd() {
+ return SendFTPCommand(NS_LITERAL_CSTRING("PWD" CRLF));
+}
+
+FTP_STATE
+nsFtpState::R_pwd() {
+ // Error response to PWD command isn't fatal, but don't cache the connection
+ // if CWD command is sent since correct mPwd is needed for further requests.
+ if (mResponseCode/100 != 2)
+ return FTP_S_TYPE;
+
+ nsAutoCString respStr(mResponseMsg);
+ int32_t pos = respStr.FindChar('"');
+ if (pos > -1) {
+ respStr.Cut(0, pos+1);
+ pos = respStr.FindChar('"');
+ if (pos > -1) {
+ respStr.Truncate(pos);
+ if (mServerType == FTP_VMS_TYPE)
+ ConvertDirspecFromVMS(respStr);
+ if (respStr.IsEmpty() || respStr.Last() != '/')
+ respStr.Append('/');
+ mPwd = respStr;
+ }
+ }
+ return FTP_S_TYPE;
+}
+
+nsresult
+nsFtpState::S_syst() {
+ return SendFTPCommand(NS_LITERAL_CSTRING("SYST" CRLF));
+}
+
+FTP_STATE
+nsFtpState::R_syst() {
+ if (mResponseCode/100 == 2) {
+ if (( mResponseMsg.Find("L8") > -1) ||
+ ( mResponseMsg.Find("UNIX") > -1) ||
+ ( mResponseMsg.Find("BSD") > -1) ||
+ ( mResponseMsg.Find("MACOS Peter's Server") > -1) ||
+ ( mResponseMsg.Find("MACOS WebSTAR FTP") > -1) ||
+ ( mResponseMsg.Find("MVS") > -1) ||
+ ( mResponseMsg.Find("OS/390") > -1) ||
+ ( mResponseMsg.Find("OS/400") > -1)) {
+ mServerType = FTP_UNIX_TYPE;
+ } else if (( mResponseMsg.Find("WIN32", true) > -1) ||
+ ( mResponseMsg.Find("windows", true) > -1)) {
+ mServerType = FTP_NT_TYPE;
+ } else if (mResponseMsg.Find("OS/2", true) > -1) {
+ mServerType = FTP_OS2_TYPE;
+ } else if (mResponseMsg.Find("VMS", true) > -1) {
+ mServerType = FTP_VMS_TYPE;
+ } else {
+ NS_ERROR("Server type list format unrecognized.");
+ // Guessing causes crashes.
+ // (Of course, the parsing code should be more robust...)
+ nsCOMPtr<nsIStringBundleService> bundleService =
+ do_GetService(NS_STRINGBUNDLE_CONTRACTID);
+ if (!bundleService)
+ return FTP_ERROR;
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ nsresult rv = bundleService->CreateBundle(NECKO_MSGS_URL,
+ getter_AddRefs(bundle));
+ if (NS_FAILED(rv))
+ return FTP_ERROR;
+
+ char16_t* ucs2Response = ToNewUnicode(mResponseMsg);
+ const char16_t *formatStrings[1] = { ucs2Response };
+ NS_NAMED_LITERAL_STRING(name, "UnsupportedFTPServer");
+
+ nsXPIDLString formattedString;
+ rv = bundle->FormatStringFromName(name.get(), formatStrings, 1,
+ getter_Copies(formattedString));
+ free(ucs2Response);
+ if (NS_FAILED(rv))
+ return FTP_ERROR;
+
+ // TODO(darin): this code should not be dictating UI like this!
+ nsCOMPtr<nsIPrompt> prompter;
+ mChannel->GetCallback(prompter);
+ if (prompter)
+ prompter->Alert(nullptr, formattedString.get());
+
+ // since we just alerted the user, clear mResponseMsg,
+ // which is displayed to the user.
+ mResponseMsg = "";
+ return FTP_ERROR;
+ }
+
+ return FTP_S_FEAT;
+ }
+
+ if (mResponseCode/100 == 5) {
+ // server didn't like the SYST command. Probably (500, 501, 502)
+ // No clue. We will just hope it is UNIX type server.
+ mServerType = FTP_UNIX_TYPE;
+
+ return FTP_S_FEAT;
+ }
+ return FTP_ERROR;
+}
+
+nsresult
+nsFtpState::S_acct() {
+ return SendFTPCommand(NS_LITERAL_CSTRING("ACCT noaccount" CRLF));
+}
+
+FTP_STATE
+nsFtpState::R_acct() {
+ if (mResponseCode/100 == 2)
+ return FTP_S_SYST;
+
+ return FTP_ERROR;
+}
+
+nsresult
+nsFtpState::S_type() {
+ return SendFTPCommand(NS_LITERAL_CSTRING("TYPE I" CRLF));
+}
+
+FTP_STATE
+nsFtpState::R_type() {
+ if (mResponseCode/100 != 2)
+ return FTP_ERROR;
+
+ return FTP_S_PASV;
+}
+
+nsresult
+nsFtpState::S_cwd() {
+ // Don't cache the connection if PWD command failed
+ if (mPwd.IsEmpty())
+ mCacheConnection = false;
+
+ nsAutoCString cwdStr;
+ if (mAction != PUT)
+ cwdStr = mPath;
+ if (cwdStr.IsEmpty() || cwdStr.First() != '/')
+ cwdStr.Insert(mPwd,0);
+ if (mServerType == FTP_VMS_TYPE)
+ ConvertDirspecToVMS(cwdStr);
+ cwdStr.Insert("CWD ",0);
+ cwdStr.Append(CRLF);
+
+ return SendFTPCommand(cwdStr);
+}
+
+FTP_STATE
+nsFtpState::R_cwd() {
+ if (mResponseCode/100 == 2) {
+ if (mAction == PUT)
+ return FTP_S_STOR;
+
+ return FTP_S_LIST;
+ }
+
+ return FTP_ERROR;
+}
+
+nsresult
+nsFtpState::S_size() {
+ nsAutoCString sizeBuf(mPath);
+ if (sizeBuf.IsEmpty() || sizeBuf.First() != '/')
+ sizeBuf.Insert(mPwd,0);
+ if (mServerType == FTP_VMS_TYPE)
+ ConvertFilespecToVMS(sizeBuf);
+ sizeBuf.Insert("SIZE ",0);
+ sizeBuf.Append(CRLF);
+
+ return SendFTPCommand(sizeBuf);
+}
+
+FTP_STATE
+nsFtpState::R_size() {
+ if (mResponseCode/100 == 2) {
+ PR_sscanf(mResponseMsg.get() + 4, "%llu", &mFileSize);
+ mChannel->SetContentLength(mFileSize);
+ }
+
+ // We may want to be able to resume this
+ return FTP_S_MDTM;
+}
+
+nsresult
+nsFtpState::S_mdtm() {
+ nsAutoCString mdtmBuf(mPath);
+ if (mdtmBuf.IsEmpty() || mdtmBuf.First() != '/')
+ mdtmBuf.Insert(mPwd,0);
+ if (mServerType == FTP_VMS_TYPE)
+ ConvertFilespecToVMS(mdtmBuf);
+ mdtmBuf.Insert("MDTM ",0);
+ mdtmBuf.Append(CRLF);
+
+ return SendFTPCommand(mdtmBuf);
+}
+
+FTP_STATE
+nsFtpState::R_mdtm() {
+ if (mResponseCode == 213) {
+ mResponseMsg.Cut(0,4);
+ mResponseMsg.Trim(" \t\r\n");
+ // yyyymmddhhmmss
+ if (mResponseMsg.Length() != 14) {
+ NS_ASSERTION(mResponseMsg.Length() == 14, "Unknown MDTM response");
+ } else {
+ mModTime = mResponseMsg;
+
+ // Save lastModified time for downloaded files.
+ nsAutoCString timeString;
+ nsresult error;
+ PRExplodedTime exTime;
+
+ mResponseMsg.Mid(timeString, 0, 4);
+ exTime.tm_year = timeString.ToInteger(&error);
+ mResponseMsg.Mid(timeString, 4, 2);
+ exTime.tm_month = timeString.ToInteger(&error) - 1; //january = 0
+ mResponseMsg.Mid(timeString, 6, 2);
+ exTime.tm_mday = timeString.ToInteger(&error);
+ mResponseMsg.Mid(timeString, 8, 2);
+ exTime.tm_hour = timeString.ToInteger(&error);
+ mResponseMsg.Mid(timeString, 10, 2);
+ exTime.tm_min = timeString.ToInteger(&error);
+ mResponseMsg.Mid(timeString, 12, 2);
+ exTime.tm_sec = timeString.ToInteger(&error);
+ exTime.tm_usec = 0;
+
+ exTime.tm_params.tp_gmt_offset = 0;
+ exTime.tm_params.tp_dst_offset = 0;
+
+ PR_NormalizeTime(&exTime, PR_GMTParameters);
+ exTime.tm_params = PR_LocalTimeParameters(&exTime);
+
+ PRTime time = PR_ImplodeTime(&exTime);
+ (void)mChannel->SetLastModifiedTime(time);
+ }
+ }
+
+ nsCString entityID;
+ entityID.Truncate();
+ entityID.AppendInt(int64_t(mFileSize));
+ entityID.Append('/');
+ entityID.Append(mModTime);
+ mChannel->SetEntityID(entityID);
+
+ // We weren't asked to resume
+ if (!mChannel->ResumeRequested())
+ return FTP_S_RETR;
+
+ //if (our entityID == supplied one (if any))
+ if (mSuppliedEntityID.IsEmpty() || entityID.Equals(mSuppliedEntityID))
+ return FTP_S_REST;
+
+ mInternalError = NS_ERROR_ENTITY_CHANGED;
+ mResponseMsg.Truncate();
+ return FTP_ERROR;
+}
+
+nsresult
+nsFtpState::SetContentType()
+{
+ // FTP directory URLs don't always end in a slash. Make sure they do.
+ // This check needs to be here rather than a more obvious place
+ // (e.g. LIST command processing) so that it ensures the terminating
+ // slash is appended for the new request case.
+
+ if (!mPath.IsEmpty() && mPath.Last() != '/') {
+ nsCOMPtr<nsIURL> url = (do_QueryInterface(mChannel->URI()));
+ nsAutoCString filePath;
+ if(NS_SUCCEEDED(url->GetFilePath(filePath))) {
+ filePath.Append('/');
+ url->SetFilePath(filePath);
+ }
+ }
+ return mChannel->SetContentType(
+ NS_LITERAL_CSTRING(APPLICATION_HTTP_INDEX_FORMAT));
+}
+
+nsresult
+nsFtpState::S_list() {
+ nsresult rv = SetContentType();
+ if (NS_FAILED(rv))
+ // XXX Invalid cast of FTP_STATE to nsresult -- FTP_ERROR has
+ // value < 0x80000000 and will pass NS_SUCCEEDED() (bug 778109)
+ return (nsresult)FTP_ERROR;
+
+ rv = mChannel->PushStreamConverter("text/ftp-dir",
+ APPLICATION_HTTP_INDEX_FORMAT);
+ if (NS_FAILED(rv)) {
+ // clear mResponseMsg which is displayed to the user.
+ // TODO: we should probably set this to something meaningful.
+ mResponseMsg = "";
+ return rv;
+ }
+
+ // dir listings aren't resumable
+ NS_ENSURE_TRUE(!mChannel->ResumeRequested(), NS_ERROR_NOT_RESUMABLE);
+
+ mChannel->SetEntityID(EmptyCString());
+
+ const char *listString;
+ if (mServerType == FTP_VMS_TYPE) {
+ listString = "LIST *.*;0" CRLF;
+ } else {
+ listString = "LIST" CRLF;
+ }
+
+ return SendFTPCommand(nsDependentCString(listString));
+}
+
+FTP_STATE
+nsFtpState::R_list() {
+ if (mResponseCode/100 == 1) {
+ // OK, time to start reading from the data connection.
+ if (mDataStream && HasPendingCallback())
+ mDataStream->AsyncWait(this, 0, 0, CallbackTarget());
+ return FTP_READ_BUF;
+ }
+
+ if (mResponseCode/100 == 2) {
+ //(DONE)
+ mNextState = FTP_COMPLETE;
+ return FTP_COMPLETE;
+ }
+ return FTP_ERROR;
+}
+
+nsresult
+nsFtpState::S_retr() {
+ nsAutoCString retrStr(mPath);
+ if (retrStr.IsEmpty() || retrStr.First() != '/')
+ retrStr.Insert(mPwd,0);
+ if (mServerType == FTP_VMS_TYPE)
+ ConvertFilespecToVMS(retrStr);
+ retrStr.Insert("RETR ",0);
+ retrStr.Append(CRLF);
+ return SendFTPCommand(retrStr);
+}
+
+FTP_STATE
+nsFtpState::R_retr() {
+ if (mResponseCode/100 == 2) {
+ //(DONE)
+ mNextState = FTP_COMPLETE;
+ return FTP_COMPLETE;
+ }
+
+ if (mResponseCode/100 == 1) {
+ if (mDataStream && HasPendingCallback())
+ mDataStream->AsyncWait(this, 0, 0, CallbackTarget());
+ return FTP_READ_BUF;
+ }
+
+ // These error codes are related to problems with the connection.
+ // If we encounter any at this point, do not try CWD and abort.
+ if (mResponseCode == 421 || mResponseCode == 425 || mResponseCode == 426)
+ return FTP_ERROR;
+
+ if (mResponseCode/100 == 5) {
+ mRETRFailed = true;
+ return FTP_S_PASV;
+ }
+
+ return FTP_S_CWD;
+}
+
+
+nsresult
+nsFtpState::S_rest() {
+
+ nsAutoCString restString("REST ");
+ // The int64_t cast is needed to avoid ambiguity
+ restString.AppendInt(int64_t(mChannel->StartPos()), 10);
+ restString.Append(CRLF);
+
+ return SendFTPCommand(restString);
+}
+
+FTP_STATE
+nsFtpState::R_rest() {
+ if (mResponseCode/100 == 4) {
+ // If REST fails, then we can't resume
+ mChannel->SetEntityID(EmptyCString());
+
+ mInternalError = NS_ERROR_NOT_RESUMABLE;
+ mResponseMsg.Truncate();
+
+ return FTP_ERROR;
+ }
+
+ return FTP_S_RETR;
+}
+
+nsresult
+nsFtpState::S_stor() {
+ NS_ENSURE_STATE(mChannel->UploadStream());
+
+ NS_ASSERTION(mAction == PUT, "Wrong state to be here");
+
+ nsCOMPtr<nsIURL> url = do_QueryInterface(mChannel->URI());
+ NS_ASSERTION(url, "I thought you were a nsStandardURL");
+
+ nsAutoCString storStr;
+ url->GetFilePath(storStr);
+ NS_ASSERTION(!storStr.IsEmpty(), "What does it mean to store a empty path");
+
+ // kill the first slash since we want to be relative to CWD.
+ if (storStr.First() == '/')
+ storStr.Cut(0,1);
+
+ if (mServerType == FTP_VMS_TYPE)
+ ConvertFilespecToVMS(storStr);
+
+ NS_UnescapeURL(storStr);
+ storStr.Insert("STOR ",0);
+ storStr.Append(CRLF);
+
+ return SendFTPCommand(storStr);
+}
+
+FTP_STATE
+nsFtpState::R_stor() {
+ if (mResponseCode/100 == 2) {
+ //(DONE)
+ mNextState = FTP_COMPLETE;
+ mStorReplyReceived = true;
+
+ // Call Close() if it was not called in nsFtpState::OnStoprequest()
+ if (!mUploadRequest && !IsClosed())
+ Close();
+
+ return FTP_COMPLETE;
+ }
+
+ if (mResponseCode/100 == 1) {
+ LOG(("FTP:(%x) writing on DT\n", this));
+ return FTP_READ_BUF;
+ }
+
+ mStorReplyReceived = true;
+ return FTP_ERROR;
+}
+
+
+nsresult
+nsFtpState::S_pasv() {
+ if (!mAddressChecked) {
+ // Find socket address
+ mAddressChecked = true;
+ mServerAddress.raw.family = AF_INET;
+ mServerAddress.inet.ip = htonl(INADDR_ANY);
+ mServerAddress.inet.port = htons(0);
+
+ nsITransport *controlSocket = mControlConnection->Transport();
+ if (!controlSocket)
+ // XXX Invalid cast of FTP_STATE to nsresult -- FTP_ERROR has
+ // value < 0x80000000 and will pass NS_SUCCEEDED() (bug 778109)
+ return (nsresult)FTP_ERROR;
+
+ nsCOMPtr<nsISocketTransport> sTrans = do_QueryInterface(controlSocket);
+ if (sTrans) {
+ nsresult rv = sTrans->GetPeerAddr(&mServerAddress);
+ if (NS_SUCCEEDED(rv)) {
+ if (!IsIPAddrAny(&mServerAddress))
+ mServerIsIPv6 = (mServerAddress.raw.family == AF_INET6) &&
+ !IsIPAddrV4Mapped(&mServerAddress);
+ else {
+ /*
+ * In case of SOCKS5 remote DNS resolution, we do
+ * not know the remote IP address. Still, if it is
+ * an IPV6 host, then the external address of the
+ * socks server should also be IPv6, and this is the
+ * self address of the transport.
+ */
+ NetAddr selfAddress;
+ rv = sTrans->GetSelfAddr(&selfAddress);
+ if (NS_SUCCEEDED(rv))
+ mServerIsIPv6 = (selfAddress.raw.family == AF_INET6) &&
+ !IsIPAddrV4Mapped(&selfAddress);
+ }
+ }
+ }
+ }
+
+ const char *string;
+ if (mServerIsIPv6) {
+ string = "EPSV" CRLF;
+ } else {
+ string = "PASV" CRLF;
+ }
+
+ return SendFTPCommand(nsDependentCString(string));
+
+}
+
+FTP_STATE
+nsFtpState::R_pasv() {
+ if (mResponseCode/100 != 2)
+ return FTP_ERROR;
+
+ nsresult rv;
+ int32_t port;
+
+ nsAutoCString responseCopy(mResponseMsg);
+ char *response = responseCopy.BeginWriting();
+
+ char *ptr = response;
+
+ // Make sure to ignore the address in the PASV response (bug 370559)
+
+ if (mServerIsIPv6) {
+ // The returned string is of the form
+ // text (|||ppp|)
+ // Where '|' can be any single character
+ char delim;
+ while (*ptr && *ptr != '(')
+ ptr++;
+ if (*ptr++ != '(')
+ return FTP_ERROR;
+ delim = *ptr++;
+ if (!delim || *ptr++ != delim ||
+ *ptr++ != delim ||
+ *ptr < '0' || *ptr > '9')
+ return FTP_ERROR;
+ port = 0;
+ do {
+ port = port * 10 + *ptr++ - '0';
+ } while (*ptr >= '0' && *ptr <= '9');
+ if (*ptr++ != delim || *ptr != ')')
+ return FTP_ERROR;
+ } else {
+ // The returned address string can be of the form
+ // (xxx,xxx,xxx,xxx,ppp,ppp) or
+ // xxx,xxx,xxx,xxx,ppp,ppp (without parens)
+ int32_t h0, h1, h2, h3, p0, p1;
+
+ int32_t fields = 0;
+ // First try with parens
+ while (*ptr && *ptr != '(')
+ ++ptr;
+ if (*ptr) {
+ ++ptr;
+ fields = PR_sscanf(ptr,
+ "%ld,%ld,%ld,%ld,%ld,%ld",
+ &h0, &h1, &h2, &h3, &p0, &p1);
+ }
+ if (!*ptr || fields < 6) {
+ // OK, lets try w/o parens
+ ptr = response;
+ while (*ptr && *ptr != ',')
+ ++ptr;
+ if (*ptr) {
+ // backup to the start of the digits
+ do {
+ ptr--;
+ } while ((ptr >=response) && (*ptr >= '0') && (*ptr <= '9'));
+ ptr++; // get back onto the numbers
+ fields = PR_sscanf(ptr,
+ "%ld,%ld,%ld,%ld,%ld,%ld",
+ &h0, &h1, &h2, &h3, &p0, &p1);
+ }
+ }
+
+ NS_ASSERTION(fields == 6, "Can't parse PASV response");
+ if (fields < 6)
+ return FTP_ERROR;
+
+ port = ((int32_t) (p0<<8)) + p1;
+ }
+
+ bool newDataConn = true;
+ if (mDataTransport) {
+ // Reuse this connection only if its still alive, and the port
+ // is the same
+ nsCOMPtr<nsISocketTransport> strans = do_QueryInterface(mDataTransport);
+ if (strans) {
+ int32_t oldPort;
+ nsresult rv = strans->GetPort(&oldPort);
+ if (NS_SUCCEEDED(rv)) {
+ if (oldPort == port) {
+ bool isAlive;
+ if (NS_SUCCEEDED(strans->IsAlive(&isAlive)) && isAlive)
+ newDataConn = false;
+ }
+ }
+ }
+
+ if (newDataConn) {
+ mDataTransport->Close(NS_ERROR_ABORT);
+ mDataTransport = nullptr;
+ mDataStream = nullptr;
+ }
+ }
+
+ if (newDataConn) {
+ // now we know where to connect our data channel
+ nsCOMPtr<nsISocketTransportService> sts =
+ do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
+ if (!sts)
+ return FTP_ERROR;
+
+ nsCOMPtr<nsISocketTransport> strans;
+
+ nsAutoCString host;
+ if (!IsIPAddrAny(&mServerAddress)) {
+ char buf[kIPv6CStrBufSize];
+ NetAddrToString(&mServerAddress, buf, sizeof(buf));
+ host.Assign(buf);
+ } else {
+ /*
+ * In case of SOCKS5 remote DNS resolving, the peer address
+ * fetched previously will be invalid (0.0.0.0): it is unknown
+ * to us. But we can pass on the original hostname to the
+ * connect for the data connection.
+ */
+ rv = mChannel->URI()->GetAsciiHost(host);
+ if (NS_FAILED(rv))
+ return FTP_ERROR;
+ }
+
+ rv = sts->CreateTransport(nullptr, 0, host,
+ port, mChannel->ProxyInfo(),
+ getter_AddRefs(strans)); // the data socket
+ if (NS_FAILED(rv))
+ return FTP_ERROR;
+ mDataTransport = strans;
+
+ strans->SetQoSBits(gFtpHandler->GetDataQoSBits());
+
+ LOG(("FTP:(%x) created DT (%s:%x)\n", this, host.get(), port));
+
+ // hook ourself up as a proxy for status notifications
+ rv = mDataTransport->SetEventSink(this, NS_GetCurrentThread());
+ NS_ENSURE_SUCCESS(rv, FTP_ERROR);
+
+ if (mAction == PUT) {
+ NS_ASSERTION(!mRETRFailed, "Failed before uploading");
+
+ // nsIUploadChannel requires the upload stream to support ReadSegments.
+ // therefore, we can open an unbuffered socket output stream.
+ nsCOMPtr<nsIOutputStream> output;
+ rv = mDataTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,
+ 0, 0, getter_AddRefs(output));
+ if (NS_FAILED(rv))
+ return FTP_ERROR;
+
+ // perform the data copy on the socket transport thread. we do this
+ // because "output" is a socket output stream, so the result is that
+ // all work will be done on the socket transport thread.
+ nsCOMPtr<nsIEventTarget> stEventTarget =
+ do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
+ if (!stEventTarget)
+ return FTP_ERROR;
+
+ nsCOMPtr<nsIAsyncStreamCopier> copier;
+ rv = NS_NewAsyncStreamCopier(getter_AddRefs(copier),
+ mChannel->UploadStream(),
+ output,
+ stEventTarget,
+ true, // upload stream is buffered
+ false); // output is NOT buffered
+ if (NS_FAILED(rv))
+ return FTP_ERROR;
+
+ rv = copier->AsyncCopy(this, nullptr);
+ if (NS_FAILED(rv))
+ return FTP_ERROR;
+
+ // hold a reference to the copier so we can cancel it if necessary.
+ mUploadRequest = copier;
+
+ // update the current working directory before sending the STOR
+ // command. this is needed since we might be reusing a control
+ // connection.
+ return FTP_S_CWD;
+ }
+
+ //
+ // else, we are reading from the data connection...
+ //
+
+ // open a buffered, asynchronous socket input stream
+ nsCOMPtr<nsIInputStream> input;
+ rv = mDataTransport->OpenInputStream(0,
+ nsIOService::gDefaultSegmentSize,
+ nsIOService::gDefaultSegmentCount,
+ getter_AddRefs(input));
+ NS_ENSURE_SUCCESS(rv, FTP_ERROR);
+ mDataStream = do_QueryInterface(input);
+ }
+
+ if (mRETRFailed || mPath.IsEmpty() || mPath.Last() == '/')
+ return FTP_S_CWD;
+ return FTP_S_SIZE;
+}
+
+nsresult
+nsFtpState::S_feat() {
+ return SendFTPCommand(NS_LITERAL_CSTRING("FEAT" CRLF));
+}
+
+FTP_STATE
+nsFtpState::R_feat() {
+ if (mResponseCode/100 == 2) {
+ if (mResponseMsg.Find(NS_LITERAL_CSTRING(CRLF " UTF8" CRLF), true) > -1) {
+ // This FTP server supports UTF-8 encoding
+ mChannel->SetContentCharset(NS_LITERAL_CSTRING("UTF-8"));
+ mUseUTF8 = true;
+ return FTP_S_OPTS;
+ }
+ }
+
+ mUseUTF8 = false;
+ return FTP_S_PWD;
+}
+
+nsresult
+nsFtpState::S_opts() {
+ // This command is for compatibility of old FTP spec (IETF Draft)
+ return SendFTPCommand(NS_LITERAL_CSTRING("OPTS UTF8 ON" CRLF));
+}
+
+FTP_STATE
+nsFtpState::R_opts() {
+ // Ignore error code because "OPTS UTF8 ON" is for compatibility of
+ // FTP server using IETF draft
+ return FTP_S_PWD;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIRequest methods:
+
+nsresult
+nsFtpState::Init(nsFtpChannel *channel)
+{
+ // parameter validation
+ NS_ASSERTION(channel, "FTP: needs a channel");
+
+ mChannel = channel; // a straight ref ptr to the channel
+
+ // initialize counter for network metering
+ mCountRecv = 0;
+
+#ifdef MOZ_WIDGET_GONK
+ nsCOMPtr<nsINetworkInfo> activeNetworkInfo;
+ GetActiveNetworkInfo(activeNetworkInfo);
+ mActiveNetworkInfo =
+ new nsMainThreadPtrHolder<nsINetworkInfo>(activeNetworkInfo);
+#endif
+
+ mKeepRunning = true;
+ mSuppliedEntityID = channel->EntityID();
+
+ if (channel->UploadStream())
+ mAction = PUT;
+
+ nsresult rv;
+ nsCOMPtr<nsIURL> url = do_QueryInterface(mChannel->URI());
+
+ nsAutoCString host;
+ if (url) {
+ rv = url->GetAsciiHost(host);
+ } else {
+ rv = mChannel->URI()->GetAsciiHost(host);
+ }
+ if (NS_FAILED(rv) || host.IsEmpty()) {
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ nsAutoCString path;
+ if (url) {
+ rv = url->GetFilePath(path);
+ } else {
+ rv = mChannel->URI()->GetPath(path);
+ }
+ if (NS_FAILED(rv))
+ return rv;
+
+ removeParamsFromPath(path);
+
+ // FTP parameters such as type=i are ignored
+ if (url) {
+ url->SetFilePath(path);
+ } else {
+ mChannel->URI()->SetPath(path);
+ }
+
+ // Skip leading slash
+ char *fwdPtr = path.BeginWriting();
+ if (!fwdPtr)
+ return NS_ERROR_OUT_OF_MEMORY;
+ if (*fwdPtr == '/')
+ fwdPtr++;
+ if (*fwdPtr != '\0') {
+ // now unescape it... %xx reduced inline to resulting character
+ int32_t len = NS_UnescapeURL(fwdPtr);
+ mPath.Assign(fwdPtr, len);
+
+#ifdef DEBUG
+ if (mPath.FindCharInSet(CRLF) >= 0)
+ NS_ERROR("NewURI() should've prevented this!!!");
+#endif
+ }
+
+ // pull any username and/or password out of the uri
+ nsAutoCString uname;
+ rv = mChannel->URI()->GetUsername(uname);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!uname.IsEmpty() && !uname.EqualsLiteral("anonymous")) {
+ mAnonymous = false;
+ CopyUTF8toUTF16(NS_UnescapeURL(uname), mUsername);
+
+ // return an error if we find a CR or LF in the username
+ if (uname.FindCharInSet(CRLF) >= 0)
+ return NS_ERROR_MALFORMED_URI;
+ }
+
+ nsAutoCString password;
+ rv = mChannel->URI()->GetPassword(password);
+ if (NS_FAILED(rv))
+ return rv;
+
+ CopyUTF8toUTF16(NS_UnescapeURL(password), mPassword);
+
+ // return an error if we find a CR or LF in the password
+ if (mPassword.FindCharInSet(CRLF) >= 0)
+ return NS_ERROR_MALFORMED_URI;
+
+ int32_t port;
+ rv = mChannel->URI()->GetPort(&port);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (port > 0)
+ mPort = port;
+
+ // Lookup Proxy information asynchronously if it isn't already set
+ // on the channel and if we aren't configured explicitly to go directly
+ nsCOMPtr<nsIProtocolProxyService> pps =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID);
+
+ if (pps && !mChannel->ProxyInfo()) {
+ pps->AsyncResolve(static_cast<nsIChannel*>(mChannel), 0, this,
+ getter_AddRefs(mProxyRequest));
+ }
+
+ return NS_OK;
+}
+
+void
+nsFtpState::Connect()
+{
+ mState = FTP_COMMAND_CONNECT;
+ mNextState = FTP_S_USER;
+
+ nsresult rv = Process();
+
+ // check for errors.
+ if (NS_FAILED(rv)) {
+ LOG(("FTP:Process() failed: %x\n", rv));
+ mInternalError = NS_ERROR_FAILURE;
+ mState = FTP_ERROR;
+ CloseWithStatus(mInternalError);
+ }
+}
+
+void
+nsFtpState::KillControlConnection()
+{
+ mControlReadCarryOverBuf.Truncate(0);
+
+ mAddressChecked = false;
+ mServerIsIPv6 = false;
+
+ // if everything went okay, save the connection.
+ // FIX: need a better way to determine if we can cache the connections.
+ // there are some errors which do not mean that we need to kill the connection
+ // e.g. fnf.
+
+ if (!mControlConnection)
+ return;
+
+ // kill the reference to ourselves in the control connection.
+ mControlConnection->WaitData(nullptr);
+
+ if (NS_SUCCEEDED(mInternalError) &&
+ NS_SUCCEEDED(mControlStatus) &&
+ mControlConnection->IsAlive() &&
+ mCacheConnection) {
+
+ LOG_INFO(("FTP:(%p) caching CC(%p)", this, mControlConnection.get()));
+
+ // Store connection persistent data
+ mControlConnection->mServerType = mServerType;
+ mControlConnection->mPassword = mPassword;
+ mControlConnection->mPwd = mPwd;
+ mControlConnection->mUseUTF8 = mUseUTF8;
+
+ nsresult rv = NS_OK;
+ // Don't cache controlconnection if anonymous (bug #473371)
+ if (!mChannel->HasLoadFlag(nsIRequest::LOAD_ANONYMOUS))
+ rv = gFtpHandler->InsertConnection(mChannel->URI(),
+ mControlConnection);
+ // Can't cache it? Kill it then.
+ mControlConnection->Disconnect(rv);
+ } else {
+ mControlConnection->Disconnect(NS_BINDING_ABORTED);
+ }
+
+ mControlConnection = nullptr;
+}
+
+class nsFtpAsyncAlert : public Runnable
+{
+public:
+ nsFtpAsyncAlert(nsIPrompt *aPrompter, nsString aResponseMsg)
+ : mPrompter(aPrompter)
+ , mResponseMsg(aResponseMsg)
+ {
+ MOZ_COUNT_CTOR(nsFtpAsyncAlert);
+ }
+protected:
+ virtual ~nsFtpAsyncAlert()
+ {
+ MOZ_COUNT_DTOR(nsFtpAsyncAlert);
+ }
+public:
+ NS_IMETHOD Run() override
+ {
+ if (mPrompter) {
+ mPrompter->Alert(nullptr, mResponseMsg.get());
+ }
+ return NS_OK;
+ }
+private:
+ nsCOMPtr<nsIPrompt> mPrompter;
+ nsString mResponseMsg;
+};
+
+
+nsresult
+nsFtpState::StopProcessing()
+{
+ // Only do this function once.
+ if (!mKeepRunning)
+ return NS_OK;
+ mKeepRunning = false;
+
+ LOG_INFO(("FTP:(%x) nsFtpState stopping", this));
+
+ if (NS_FAILED(mInternalError) && !mResponseMsg.IsEmpty()) {
+ // check to see if the control status is bad.
+ // web shell wont throw an alert. we better:
+
+ // XXX(darin): this code should not be dictating UI like this!
+ nsCOMPtr<nsIPrompt> prompter;
+ mChannel->GetCallback(prompter);
+ if (prompter) {
+ nsCOMPtr<nsIRunnable> alertEvent;
+ if (mUseUTF8) {
+ alertEvent = new nsFtpAsyncAlert(prompter,
+ NS_ConvertUTF8toUTF16(mResponseMsg));
+ } else {
+ alertEvent = new nsFtpAsyncAlert(prompter,
+ NS_ConvertASCIItoUTF16(mResponseMsg));
+ }
+ NS_DispatchToMainThread(alertEvent);
+ }
+ nsCOMPtr<nsIFTPChannelParentInternal> ftpChanP;
+ mChannel->GetCallback(ftpChanP);
+ if (ftpChanP) {
+ ftpChanP->SetErrorMsg(mResponseMsg.get(), mUseUTF8);
+ }
+ }
+
+ nsresult broadcastErrorCode = mControlStatus;
+ if (NS_SUCCEEDED(broadcastErrorCode))
+ broadcastErrorCode = mInternalError;
+
+ mInternalError = broadcastErrorCode;
+
+ KillControlConnection();
+
+ // XXX This can fire before we are done loading data. Is that a problem?
+ OnTransportStatus(nullptr, NS_NET_STATUS_END_FTP_TRANSACTION, 0, 0);
+
+ if (NS_FAILED(broadcastErrorCode))
+ CloseWithStatus(broadcastErrorCode);
+
+ return NS_OK;
+}
+
+nsresult
+nsFtpState::SendFTPCommand(const nsCSubstring& command)
+{
+ NS_ASSERTION(mControlConnection, "null control connection");
+
+ // we don't want to log the password:
+ nsAutoCString logcmd(command);
+ if (StringBeginsWith(command, NS_LITERAL_CSTRING("PASS ")))
+ logcmd = "PASS xxxxx";
+
+ LOG(("FTP:(%x) writing \"%s\"\n", this, logcmd.get()));
+
+ nsCOMPtr<nsIFTPEventSink> ftpSink;
+ mChannel->GetFTPEventSink(ftpSink);
+ if (ftpSink)
+ ftpSink->OnFTPControlLog(false, logcmd.get());
+
+ if (mControlConnection)
+ return mControlConnection->Write(command);
+
+ return NS_ERROR_FAILURE;
+}
+
+// Convert a unix-style filespec to VMS format
+// /foo/fred/barney/file.txt -> foo:[fred.barney]file.txt
+// /foo/file.txt -> foo:[000000]file.txt
+void
+nsFtpState::ConvertFilespecToVMS(nsCString& fileString)
+{
+ int ntok=1;
+ char *t, *nextToken;
+ nsAutoCString fileStringCopy;
+
+ // Get a writeable copy we can strtok with.
+ fileStringCopy = fileString;
+ t = nsCRT::strtok(fileStringCopy.BeginWriting(), "/", &nextToken);
+ if (t)
+ while (nsCRT::strtok(nextToken, "/", &nextToken))
+ ntok++; // count number of terms (tokens)
+ LOG(("FTP:(%x) ConvertFilespecToVMS ntok: %d\n", this, ntok));
+ LOG(("FTP:(%x) ConvertFilespecToVMS from: \"%s\"\n", this, fileString.get()));
+
+ if (fileString.First() == '/') {
+ // absolute filespec
+ // / -> []
+ // /a -> a (doesn't really make much sense)
+ // /a/b -> a:[000000]b
+ // /a/b/c -> a:[b]c
+ // /a/b/c/d -> a:[b.c]d
+ if (ntok == 1) {
+ if (fileString.Length() == 1) {
+ // Just a slash
+ fileString.Truncate();
+ fileString.AppendLiteral("[]");
+ } else {
+ // just copy the name part (drop the leading slash)
+ fileStringCopy = fileString;
+ fileString = Substring(fileStringCopy, 1,
+ fileStringCopy.Length()-1);
+ }
+ } else {
+ // Get another copy since the last one was written to.
+ fileStringCopy = fileString;
+ fileString.Truncate();
+ fileString.Append(nsCRT::strtok(fileStringCopy.BeginWriting(),
+ "/", &nextToken));
+ fileString.AppendLiteral(":[");
+ if (ntok > 2) {
+ for (int i=2; i<ntok; i++) {
+ if (i > 2) fileString.Append('.');
+ fileString.Append(nsCRT::strtok(nextToken,
+ "/", &nextToken));
+ }
+ } else {
+ fileString.AppendLiteral("000000");
+ }
+ fileString.Append(']');
+ fileString.Append(nsCRT::strtok(nextToken, "/", &nextToken));
+ }
+ } else {
+ // relative filespec
+ // a -> a
+ // a/b -> [.a]b
+ // a/b/c -> [.a.b]c
+ if (ntok == 1) {
+ // no slashes, just use the name as is
+ } else {
+ // Get another copy since the last one was written to.
+ fileStringCopy = fileString;
+ fileString.Truncate();
+ fileString.AppendLiteral("[.");
+ fileString.Append(nsCRT::strtok(fileStringCopy.BeginWriting(),
+ "/", &nextToken));
+ if (ntok > 2) {
+ for (int i=2; i<ntok; i++) {
+ fileString.Append('.');
+ fileString.Append(nsCRT::strtok(nextToken,
+ "/", &nextToken));
+ }
+ }
+ fileString.Append(']');
+ fileString.Append(nsCRT::strtok(nextToken, "/", &nextToken));
+ }
+ }
+ LOG(("FTP:(%x) ConvertFilespecToVMS to: \"%s\"\n", this, fileString.get()));
+}
+
+// Convert a unix-style dirspec to VMS format
+// /foo/fred/barney/rubble -> foo:[fred.barney.rubble]
+// /foo/fred -> foo:[fred]
+// /foo -> foo:[000000]
+// (null) -> (null)
+void
+nsFtpState::ConvertDirspecToVMS(nsCString& dirSpec)
+{
+ LOG(("FTP:(%x) ConvertDirspecToVMS from: \"%s\"\n", this, dirSpec.get()));
+ if (!dirSpec.IsEmpty()) {
+ if (dirSpec.Last() != '/')
+ dirSpec.Append('/');
+ // we can use the filespec routine if we make it look like a file name
+ dirSpec.Append('x');
+ ConvertFilespecToVMS(dirSpec);
+ dirSpec.Truncate(dirSpec.Length()-1);
+ }
+ LOG(("FTP:(%x) ConvertDirspecToVMS to: \"%s\"\n", this, dirSpec.get()));
+}
+
+// Convert an absolute VMS style dirspec to UNIX format
+void
+nsFtpState::ConvertDirspecFromVMS(nsCString& dirSpec)
+{
+ LOG(("FTP:(%x) ConvertDirspecFromVMS from: \"%s\"\n", this, dirSpec.get()));
+ if (dirSpec.IsEmpty()) {
+ dirSpec.Insert('.', 0);
+ } else {
+ dirSpec.Insert('/', 0);
+ dirSpec.ReplaceSubstring(":[", "/");
+ dirSpec.ReplaceChar('.', '/');
+ dirSpec.ReplaceChar(']', '/');
+ }
+ LOG(("FTP:(%x) ConvertDirspecFromVMS to: \"%s\"\n", this, dirSpec.get()));
+}
+
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsFtpState::OnTransportStatus(nsITransport *transport, nsresult status,
+ int64_t progress, int64_t progressMax)
+{
+ // Mix signals from both the control and data connections.
+
+ // Ignore data transfer events on the control connection.
+ if (mControlConnection && transport == mControlConnection->Transport()) {
+ switch (status) {
+ case NS_NET_STATUS_RESOLVING_HOST:
+ case NS_NET_STATUS_RESOLVED_HOST:
+ case NS_NET_STATUS_CONNECTING_TO:
+ case NS_NET_STATUS_CONNECTED_TO:
+ break;
+ default:
+ return NS_OK;
+ }
+ }
+
+ // Ignore the progressMax value from the socket. We know the true size of
+ // the file based on the response from our SIZE request. Additionally, only
+ // report the max progress based on where we started/resumed.
+ mChannel->OnTransportStatus(nullptr, status, progress,
+ mFileSize - mChannel->StartPos());
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsFtpState::OnStartRequest(nsIRequest *request, nsISupports *context)
+{
+ mStorReplyReceived = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFtpState::OnStopRequest(nsIRequest *request, nsISupports *context,
+ nsresult status)
+{
+ mUploadRequest = nullptr;
+
+ // Close() will be called when reply to STOR command is received
+ // see bug #389394
+ if (!mStorReplyReceived)
+ return NS_OK;
+
+ // We're done uploading. Let our consumer know that we're done.
+ Close();
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsFtpState::Available(uint64_t *result)
+{
+ if (mDataStream)
+ return mDataStream->Available(result);
+
+ return nsBaseContentStream::Available(result);
+}
+
+NS_IMETHODIMP
+nsFtpState::ReadSegments(nsWriteSegmentFun writer, void *closure,
+ uint32_t count, uint32_t *result)
+{
+ // Insert a thunk here so that the input stream passed to the writer is this
+ // input stream instead of mDataStream.
+
+ if (mDataStream) {
+ nsWriteSegmentThunk thunk = { this, writer, closure };
+ nsresult rv;
+ rv = mDataStream->ReadSegments(NS_WriteSegmentThunk, &thunk, count,
+ result);
+ if (NS_SUCCEEDED(rv)) {
+ CountRecvBytes(*result);
+ }
+ return rv;
+ }
+
+ return nsBaseContentStream::ReadSegments(writer, closure, count, result);
+}
+
+nsresult
+nsFtpState::SaveNetworkStats(bool enforce)
+{
+#ifdef MOZ_WIDGET_GONK
+ // Obtain app id
+ uint32_t appId;
+ bool isInBrowser;
+ NS_GetAppInfo(mChannel, &appId, &isInBrowser);
+
+ // Check if active network and appid are valid.
+ if (!mActiveNetworkInfo || appId == NECKO_NO_APP_ID) {
+ return NS_OK;
+ }
+
+ if (mCountRecv <= 0) {
+ // There is no traffic, no need to save.
+ return NS_OK;
+ }
+
+ // If |enforce| is false, the traffic amount is saved
+ // only when the total amount exceeds the predefined
+ // threshold.
+ if (!enforce && mCountRecv < NETWORK_STATS_THRESHOLD) {
+ return NS_OK;
+ }
+
+ // Create the event to save the network statistics.
+ // the event is then dispathed to the main thread.
+ RefPtr<Runnable> event =
+ new SaveNetworkStatsEvent(appId, isInBrowser, mActiveNetworkInfo,
+ mCountRecv, 0, false);
+ NS_DispatchToMainThread(event);
+
+ // Reset the counters after saving.
+ mCountRecv = 0;
+
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
+nsFtpState::CloseWithStatus(nsresult status)
+{
+ LOG(("FTP:(%p) close [%x]\n", this, status));
+
+ // Shutdown the control connection processing if we are being closed with an
+ // error. Note: This method may be called several times.
+ if (!IsClosed() && status != NS_BASE_STREAM_CLOSED && NS_FAILED(status)) {
+ if (NS_SUCCEEDED(mInternalError))
+ mInternalError = status;
+ StopProcessing();
+ }
+
+ if (mUploadRequest) {
+ mUploadRequest->Cancel(NS_ERROR_ABORT);
+ mUploadRequest = nullptr;
+ }
+
+ if (mDataTransport) {
+ // Save the network stats before data transport is closing.
+ SaveNetworkStats(true);
+
+ // Shutdown the data transport.
+ mDataTransport->Close(NS_ERROR_ABORT);
+ mDataTransport = nullptr;
+ }
+
+ mDataStream = nullptr;
+
+ return nsBaseContentStream::CloseWithStatus(status);
+}
+
+static nsresult
+CreateHTTPProxiedChannel(nsIChannel *channel, nsIProxyInfo *pi, nsIChannel **newChannel)
+{
+ nsresult rv;
+ nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIProtocolHandler> handler;
+ rv = ioService->GetProtocolHandler("http", getter_AddRefs(handler));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIProxiedProtocolHandler> pph = do_QueryInterface(handler, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIURI> uri;
+ channel->GetURI(getter_AddRefs(uri));
+
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ channel->GetLoadInfo(getter_AddRefs(loadInfo));
+
+ return pph->NewProxiedChannel2(uri, pi, 0, nullptr, loadInfo, newChannel);
+}
+
+NS_IMETHODIMP
+nsFtpState::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
+ nsIProxyInfo *pi, nsresult status)
+{
+ mProxyRequest = nullptr;
+
+ // failed status code just implies DIRECT processing
+
+ if (NS_SUCCEEDED(status)) {
+ nsAutoCString type;
+ if (pi && NS_SUCCEEDED(pi->GetType(type)) && type.EqualsLiteral("http")) {
+ // Proxy the FTP url via HTTP
+ // This would have been easier to just return a HTTP channel directly
+ // from nsIIOService::NewChannelFromURI(), but the proxy type cannot
+ // be reliabliy determined synchronously without jank due to pac, etc..
+ LOG(("FTP:(%p) Configured to use a HTTP proxy channel\n", this));
+
+ nsCOMPtr<nsIChannel> newChannel;
+ if (NS_SUCCEEDED(CreateHTTPProxiedChannel(channel, pi,
+ getter_AddRefs(newChannel))) &&
+ NS_SUCCEEDED(mChannel->Redirect(newChannel,
+ nsIChannelEventSink::REDIRECT_INTERNAL,
+ true))) {
+ LOG(("FTP:(%p) Redirected to use a HTTP proxy channel\n", this));
+ return NS_OK;
+ }
+ }
+ else if (pi) {
+ // Proxy using the FTP protocol routed through a socks proxy
+ LOG(("FTP:(%p) Configured to use a SOCKS proxy channel\n", this));
+ mChannel->SetProxyInfo(pi);
+ }
+ }
+
+ if (mDeferredCallbackPending) {
+ mDeferredCallbackPending = false;
+ OnCallbackPending();
+ }
+ return NS_OK;
+}
+
+void
+nsFtpState::OnCallbackPending()
+{
+ if (mState == FTP_INIT) {
+ if (mProxyRequest) {
+ mDeferredCallbackPending = true;
+ return;
+ }
+ Connect();
+ } else if (mDataStream) {
+ mDataStream->AsyncWait(this, 0, 0, CallbackTarget());
+ }
+}
+
diff --git a/netwerk/protocol/ftp/nsFtpConnectionThread.h b/netwerk/protocol/ftp/nsFtpConnectionThread.h
new file mode 100644
index 000000000..dd48da562
--- /dev/null
+++ b/netwerk/protocol/ftp/nsFtpConnectionThread.h
@@ -0,0 +1,231 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 __nsFtpConnectionThread__h_
+#define __nsFtpConnectionThread__h_
+
+#include "nsBaseContentStream.h"
+
+#include "nsString.h"
+#include "nsCOMPtr.h"
+#include "nsIAsyncInputStream.h"
+#include "nsAutoPtr.h"
+#include "nsITransport.h"
+#include "mozilla/net/DNS.h"
+#include "nsFtpControlConnection.h"
+#include "nsIProtocolProxyCallback.h"
+
+#ifdef MOZ_WIDGET_GONK
+#include "nsINetworkInterface.h"
+#include "nsProxyRelease.h"
+#endif
+
+// ftp server types
+#define FTP_GENERIC_TYPE 0
+#define FTP_UNIX_TYPE 1
+#define FTP_VMS_TYPE 8
+#define FTP_NT_TYPE 9
+#define FTP_OS2_TYPE 11
+
+// ftp states
+typedef enum _FTP_STATE {
+///////////////////////
+//// Internal states
+ FTP_INIT,
+ FTP_COMMAND_CONNECT,
+ FTP_READ_BUF,
+ FTP_ERROR,
+ FTP_COMPLETE,
+
+///////////////////////
+//// Command channel connection setup states
+ FTP_S_USER, FTP_R_USER,
+ FTP_S_PASS, FTP_R_PASS,
+ FTP_S_SYST, FTP_R_SYST,
+ FTP_S_ACCT, FTP_R_ACCT,
+ FTP_S_TYPE, FTP_R_TYPE,
+ FTP_S_CWD, FTP_R_CWD,
+ FTP_S_SIZE, FTP_R_SIZE,
+ FTP_S_MDTM, FTP_R_MDTM,
+ FTP_S_REST, FTP_R_REST,
+ FTP_S_RETR, FTP_R_RETR,
+ FTP_S_STOR, FTP_R_STOR,
+ FTP_S_LIST, FTP_R_LIST,
+ FTP_S_PASV, FTP_R_PASV,
+ FTP_S_PWD, FTP_R_PWD,
+ FTP_S_FEAT, FTP_R_FEAT,
+ FTP_S_OPTS, FTP_R_OPTS
+} FTP_STATE;
+
+// higher level ftp actions
+typedef enum _FTP_ACTION {GET, PUT} FTP_ACTION;
+
+class nsFtpChannel;
+class nsICancelable;
+class nsIProxyInfo;
+class nsIStreamListener;
+
+// The nsFtpState object is the content stream for the channel. It implements
+// nsIInputStreamCallback, so it can read data from the control connection. It
+// implements nsITransportEventSink so it can mix status events from both the
+// control connection and the data connection.
+
+class nsFtpState final : public nsBaseContentStream,
+ public nsIInputStreamCallback,
+ public nsITransportEventSink,
+ public nsIRequestObserver,
+ public nsFtpControlConnectionListener,
+ public nsIProtocolProxyCallback
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIINPUTSTREAMCALLBACK
+ NS_DECL_NSITRANSPORTEVENTSINK
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSIPROTOCOLPROXYCALLBACK
+
+ // Override input stream methods:
+ NS_IMETHOD CloseWithStatus(nsresult status) override;
+ NS_IMETHOD Available(uint64_t *result) override;
+ NS_IMETHOD ReadSegments(nsWriteSegmentFun fun, void *closure,
+ uint32_t count, uint32_t *result) override;
+
+ // nsFtpControlConnectionListener methods:
+ virtual void OnControlDataAvailable(const char *data, uint32_t dataLen) override;
+ virtual void OnControlError(nsresult status) override;
+
+ nsFtpState();
+ nsresult Init(nsFtpChannel *channel);
+
+protected:
+ // Notification from nsBaseContentStream::AsyncWait
+ virtual void OnCallbackPending() override;
+
+private:
+ virtual ~nsFtpState();
+
+ ///////////////////////////////////
+ // BEGIN: STATE METHODS
+ nsresult S_user(); FTP_STATE R_user();
+ nsresult S_pass(); FTP_STATE R_pass();
+ nsresult S_syst(); FTP_STATE R_syst();
+ nsresult S_acct(); FTP_STATE R_acct();
+
+ nsresult S_type(); FTP_STATE R_type();
+ nsresult S_cwd(); FTP_STATE R_cwd();
+
+ nsresult S_size(); FTP_STATE R_size();
+ nsresult S_mdtm(); FTP_STATE R_mdtm();
+ nsresult S_list(); FTP_STATE R_list();
+
+ nsresult S_rest(); FTP_STATE R_rest();
+ nsresult S_retr(); FTP_STATE R_retr();
+ nsresult S_stor(); FTP_STATE R_stor();
+ nsresult S_pasv(); FTP_STATE R_pasv();
+ nsresult S_pwd(); FTP_STATE R_pwd();
+ nsresult S_feat(); FTP_STATE R_feat();
+ nsresult S_opts(); FTP_STATE R_opts();
+ // END: STATE METHODS
+ ///////////////////////////////////
+
+ // internal methods
+ void MoveToNextState(FTP_STATE nextState);
+ nsresult Process();
+
+ void KillControlConnection();
+ nsresult StopProcessing();
+ nsresult EstablishControlConnection();
+ nsresult SendFTPCommand(const nsCSubstring& command);
+ void ConvertFilespecToVMS(nsCString& fileSpec);
+ void ConvertDirspecToVMS(nsCString& fileSpec);
+ void ConvertDirspecFromVMS(nsCString& fileSpec);
+ nsresult BuildStreamConverter(nsIStreamListener** convertStreamListener);
+ nsresult SetContentType();
+
+ /**
+ * This method is called to kick-off the FTP state machine. mState is
+ * reset to FTP_COMMAND_CONNECT, and the FTP state machine progresses from
+ * there. This method is initially called (indirectly) from the channel's
+ * AsyncOpen implementation.
+ */
+ void Connect();
+
+ ///////////////////////////////////
+ // Private members
+
+ // ****** state machine vars
+ FTP_STATE mState; // the current state
+ FTP_STATE mNextState; // the next state
+ bool mKeepRunning; // thread event loop boolean
+ int32_t mResponseCode; // the last command response code
+ nsCString mResponseMsg; // the last command response text
+
+ // ****** channel/transport/stream vars
+ RefPtr<nsFtpControlConnection> mControlConnection; // cacheable control connection (owns mCPipe)
+ bool mReceivedControlData;
+ bool mTryingCachedControl; // retrying the password
+ bool mRETRFailed; // Did we already try a RETR and it failed?
+ uint64_t mFileSize;
+ nsCString mModTime;
+
+ // ****** consumer vars
+ RefPtr<nsFtpChannel> mChannel; // our owning FTP channel we pass through our events
+ nsCOMPtr<nsIProxyInfo> mProxyInfo;
+
+ // ****** connection cache vars
+ int32_t mServerType; // What kind of server are we talking to
+
+ // ****** protocol interpretation related state vars
+ nsString mUsername; // username
+ nsString mPassword; // password
+ FTP_ACTION mAction; // the higher level action (GET/PUT)
+ bool mAnonymous; // try connecting anonymous (default)
+ bool mRetryPass; // retrying the password
+ bool mStorReplyReceived; // FALSE if waiting for STOR
+ // completion status from server
+ nsresult mInternalError; // represents internal state errors
+ bool mReconnectAndLoginAgain;
+ bool mCacheConnection;
+
+ // ****** URI vars
+ int32_t mPort; // the port to connect to
+ nsString mFilename; // url filename (if any)
+ nsCString mPath; // the url's path
+ nsCString mPwd; // login Path
+
+ // ****** other vars
+ nsCOMPtr<nsITransport> mDataTransport;
+ nsCOMPtr<nsIAsyncInputStream> mDataStream;
+ nsCOMPtr<nsIRequest> mUploadRequest;
+ bool mAddressChecked;
+ bool mServerIsIPv6;
+ bool mUseUTF8;
+
+ mozilla::net::NetAddr mServerAddress;
+
+ // ***** control read gvars
+ nsresult mControlStatus;
+ nsCString mControlReadCarryOverBuf;
+
+ nsCString mSuppliedEntityID;
+
+ nsCOMPtr<nsICancelable> mProxyRequest;
+ bool mDeferredCallbackPending;
+
+// These members are used for network per-app metering (bug 855948)
+// Currently, they are only available on gonk.
+ uint64_t mCountRecv;
+#ifdef MOZ_WIDGET_GONK
+ nsMainThreadPtrHandle<nsINetworkInfo> mActiveNetworkInfo;
+#endif
+ nsresult SaveNetworkStats(bool);
+ void CountRecvBytes(uint64_t recvBytes)
+ {
+ mCountRecv += recvBytes;
+ SaveNetworkStats(false);
+ }
+};
+
+#endif //__nsFtpConnectionThread__h_
diff --git a/netwerk/protocol/ftp/nsFtpControlConnection.cpp b/netwerk/protocol/ftp/nsFtpControlConnection.cpp
new file mode 100644
index 000000000..ab55cd4f6
--- /dev/null
+++ b/netwerk/protocol/ftp/nsFtpControlConnection.cpp
@@ -0,0 +1,189 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsIOService.h"
+#include "nsFtpControlConnection.h"
+#include "nsFtpProtocolHandler.h"
+#include "mozilla/Logging.h"
+#include "nsIInputStream.h"
+#include "nsISocketTransportService.h"
+#include "nsISocketTransport.h"
+#include "nsThreadUtils.h"
+#include "nsIOutputStream.h"
+#include "nsNetCID.h"
+#include <algorithm>
+
+using namespace mozilla;
+using namespace mozilla::net;
+
+extern LazyLogModule gFTPLog;
+#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
+#define LOG_INFO(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Info, args)
+
+//
+// nsFtpControlConnection implementation ...
+//
+
+NS_IMPL_ISUPPORTS(nsFtpControlConnection, nsIInputStreamCallback)
+
+NS_IMETHODIMP
+nsFtpControlConnection::OnInputStreamReady(nsIAsyncInputStream *stream)
+{
+ char data[4096];
+
+ // Consume data whether we have a listener or not.
+ uint64_t avail64;
+ uint32_t avail = 0;
+ nsresult rv = stream->Available(&avail64);
+ if (NS_SUCCEEDED(rv)) {
+ avail = (uint32_t)std::min(avail64, (uint64_t)sizeof(data));
+
+ uint32_t n;
+ rv = stream->Read(data, avail, &n);
+ if (NS_SUCCEEDED(rv))
+ avail = n;
+ }
+
+ // It's important that we null out mListener before calling one of its
+ // methods as it may call WaitData, which would queue up another read.
+
+ RefPtr<nsFtpControlConnectionListener> listener;
+ listener.swap(mListener);
+
+ if (!listener)
+ return NS_OK;
+
+ if (NS_FAILED(rv)) {
+ listener->OnControlError(rv);
+ } else {
+ listener->OnControlDataAvailable(data, avail);
+ }
+
+ return NS_OK;
+}
+
+nsFtpControlConnection::nsFtpControlConnection(const nsCSubstring& host,
+ uint32_t port)
+ : mServerType(0), mSessionId(gFtpHandler->GetSessionId())
+ , mUseUTF8(false), mHost(host), mPort(port)
+{
+ LOG_INFO(("FTP:CC created @%p", this));
+}
+
+nsFtpControlConnection::~nsFtpControlConnection()
+{
+ LOG_INFO(("FTP:CC destroyed @%p", this));
+}
+
+bool
+nsFtpControlConnection::IsAlive()
+{
+ if (!mSocket)
+ return false;
+
+ bool isAlive = false;
+ mSocket->IsAlive(&isAlive);
+ return isAlive;
+}
+nsresult
+nsFtpControlConnection::Connect(nsIProxyInfo* proxyInfo,
+ nsITransportEventSink* eventSink)
+{
+ if (mSocket)
+ return NS_OK;
+
+ // build our own
+ nsresult rv;
+ nsCOMPtr<nsISocketTransportService> sts =
+ do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = sts->CreateTransport(nullptr, 0, mHost, mPort, proxyInfo,
+ getter_AddRefs(mSocket)); // the command transport
+ if (NS_FAILED(rv))
+ return rv;
+
+ mSocket->SetQoSBits(gFtpHandler->GetControlQoSBits());
+
+ // proxy transport events back to current thread
+ if (eventSink)
+ mSocket->SetEventSink(eventSink, NS_GetCurrentThread());
+
+ // open buffered, blocking output stream to socket. so long as commands
+ // do not exceed 1024 bytes in length, the writing thread (the main thread)
+ // will not block. this should be OK.
+ rv = mSocket->OpenOutputStream(nsITransport::OPEN_BLOCKING, 1024, 1,
+ getter_AddRefs(mSocketOutput));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // open buffered, non-blocking/asynchronous input stream to socket.
+ nsCOMPtr<nsIInputStream> inStream;
+ rv = mSocket->OpenInputStream(0,
+ nsIOService::gDefaultSegmentSize,
+ nsIOService::gDefaultSegmentCount,
+ getter_AddRefs(inStream));
+ if (NS_SUCCEEDED(rv))
+ mSocketInput = do_QueryInterface(inStream);
+
+ return rv;
+}
+
+nsresult
+nsFtpControlConnection::WaitData(nsFtpControlConnectionListener *listener)
+{
+ LOG(("FTP:(%p) wait data [listener=%p]\n", this, listener));
+
+ // If listener is null, then simply disconnect the listener. Otherwise,
+ // ensure that we are listening.
+ if (!listener) {
+ mListener = nullptr;
+ return NS_OK;
+ }
+
+ NS_ENSURE_STATE(mSocketInput);
+
+ mListener = listener;
+ return mSocketInput->AsyncWait(this, 0, 0, NS_GetCurrentThread());
+}
+
+nsresult
+nsFtpControlConnection::Disconnect(nsresult status)
+{
+ if (!mSocket)
+ return NS_OK; // already disconnected
+
+ LOG_INFO(("FTP:(%p) CC disconnecting (%x)", this, status));
+
+ if (NS_FAILED(status)) {
+ // break cyclic reference!
+ mSocket->Close(status);
+ mSocket = nullptr;
+ mSocketInput->AsyncWait(nullptr, 0, 0, nullptr); // clear any observer
+ mSocketInput = nullptr;
+ mSocketOutput = nullptr;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsFtpControlConnection::Write(const nsCSubstring& command)
+{
+ NS_ENSURE_STATE(mSocketOutput);
+
+ uint32_t len = command.Length();
+ uint32_t cnt;
+ nsresult rv = mSocketOutput->Write(command.Data(), len, &cnt);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (len != cnt)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
diff --git a/netwerk/protocol/ftp/nsFtpControlConnection.h b/netwerk/protocol/ftp/nsFtpControlConnection.h
new file mode 100644
index 000000000..5301bb9ce
--- /dev/null
+++ b/netwerk/protocol/ftp/nsFtpControlConnection.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set et ts=4 sts=4 sw=4 cin: */
+/* 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 nsFtpControlConnection_h___
+#define nsFtpControlConnection_h___
+
+#include "nsCOMPtr.h"
+
+#include "nsISocketTransport.h"
+#include "nsIAsyncInputStream.h"
+#include "nsAutoPtr.h"
+#include "nsString.h"
+#include "mozilla/Attributes.h"
+
+class nsIOutputStream;
+class nsIProxyInfo;
+class nsITransportEventSink;
+
+class nsFtpControlConnectionListener : public nsISupports {
+public:
+ /**
+ * Called when a chunk of data arrives on the control connection.
+ * @param data
+ * The new data or null if an error occurred.
+ * @param dataLen
+ * The data length in bytes.
+ */
+ virtual void OnControlDataAvailable(const char *data, uint32_t dataLen) = 0;
+
+ /**
+ * Called when an error occurs on the control connection.
+ * @param status
+ * A failure code providing more info about the error.
+ */
+ virtual void OnControlError(nsresult status) = 0;
+};
+
+class nsFtpControlConnection final : public nsIInputStreamCallback
+{
+ ~nsFtpControlConnection();
+
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINPUTSTREAMCALLBACK
+
+ nsFtpControlConnection(const nsCSubstring& host, uint32_t port);
+
+ nsresult Connect(nsIProxyInfo* proxyInfo, nsITransportEventSink* eventSink);
+ nsresult Disconnect(nsresult status);
+ nsresult Write(const nsCSubstring& command);
+
+ bool IsAlive();
+
+ nsITransport *Transport() { return mSocket; }
+
+ /**
+ * Call this function to be notified asynchronously when there is data
+ * available for the socket. The listener passed to this method replaces
+ * any existing listener, and the listener can be null to disconnect the
+ * previous listener.
+ */
+ nsresult WaitData(nsFtpControlConnectionListener *listener);
+
+ uint32_t mServerType; // what kind of server is it.
+ nsString mPassword;
+ int32_t mSuspendedWrite;
+ nsCString mPwd;
+ uint32_t mSessionId;
+ bool mUseUTF8;
+
+private:
+ nsCString mHost;
+ uint32_t mPort;
+
+ nsCOMPtr<nsISocketTransport> mSocket;
+ nsCOMPtr<nsIOutputStream> mSocketOutput;
+ nsCOMPtr<nsIAsyncInputStream> mSocketInput;
+
+ RefPtr<nsFtpControlConnectionListener> mListener;
+};
+
+#endif
diff --git a/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp b/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp
new file mode 100644
index 000000000..46b12767a
--- /dev/null
+++ b/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp
@@ -0,0 +1,426 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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/.
+ *
+ *
+ * This Original Code has been modified by IBM Corporation.
+ * Modifications made by IBM described herein are
+ * Copyright (c) International Business Machines
+ * Corporation, 2000
+ *
+ * Modifications to Mozilla code or documentation
+ * identified per MPL Section 3.3
+ *
+ * Date Modified by Description of modification
+ * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
+ * use in OS2
+ */
+
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/FTPChannelChild.h"
+using namespace mozilla;
+using namespace mozilla::net;
+
+#include "nsFtpProtocolHandler.h"
+#include "nsFTPChannel.h"
+#include "nsIStandardURL.h"
+#include "mozilla/Logging.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIObserverService.h"
+#include "nsEscape.h"
+#include "nsAlgorithm.h"
+
+//-----------------------------------------------------------------------------
+
+//
+// Log module for FTP Protocol logging...
+//
+// To enable logging (see prlog.h for full details):
+//
+// set MOZ_LOG=nsFtp:5
+// set MOZ_LOG_FILE=ftp.log
+//
+// This enables LogLevel::Debug level information and places all output in
+// the file ftp.log.
+//
+LazyLogModule gFTPLog("nsFtp");
+#undef LOG
+#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args)
+
+//-----------------------------------------------------------------------------
+
+#define IDLE_TIMEOUT_PREF "network.ftp.idleConnectionTimeout"
+#define IDLE_CONNECTION_LIMIT 8 /* TODO pref me */
+
+#define QOS_DATA_PREF "network.ftp.data.qos"
+#define QOS_CONTROL_PREF "network.ftp.control.qos"
+
+nsFtpProtocolHandler *gFtpHandler = nullptr;
+
+//-----------------------------------------------------------------------------
+
+nsFtpProtocolHandler::nsFtpProtocolHandler()
+ : mIdleTimeout(-1)
+ , mSessionId(0)
+ , mControlQoSBits(0x00)
+ , mDataQoSBits(0x00)
+{
+ LOG(("FTP:creating handler @%x\n", this));
+
+ gFtpHandler = this;
+}
+
+nsFtpProtocolHandler::~nsFtpProtocolHandler()
+{
+ LOG(("FTP:destroying handler @%x\n", this));
+
+ NS_ASSERTION(mRootConnectionList.Length() == 0, "why wasn't Observe called?");
+
+ gFtpHandler = nullptr;
+}
+
+NS_IMPL_ISUPPORTS(nsFtpProtocolHandler,
+ nsIProtocolHandler,
+ nsIProxiedProtocolHandler,
+ nsIObserver,
+ nsISupportsWeakReference)
+
+nsresult
+nsFtpProtocolHandler::Init()
+{
+ if (IsNeckoChild())
+ NeckoChild::InitNeckoChild();
+
+ if (mIdleTimeout == -1) {
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &mIdleTimeout);
+ if (NS_FAILED(rv))
+ mIdleTimeout = 5*60; // 5 minute default
+
+ rv = branch->AddObserver(IDLE_TIMEOUT_PREF, this, true);
+ if (NS_FAILED(rv)) return rv;
+
+ int32_t val;
+ rv = branch->GetIntPref(QOS_DATA_PREF, &val);
+ if (NS_SUCCEEDED(rv))
+ mDataQoSBits = (uint8_t) clamped(val, 0, 0xff);
+
+ rv = branch->AddObserver(QOS_DATA_PREF, this, true);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = branch->GetIntPref(QOS_CONTROL_PREF, &val);
+ if (NS_SUCCEEDED(rv))
+ mControlQoSBits = (uint8_t) clamped(val, 0, 0xff);
+
+ rv = branch->AddObserver(QOS_CONTROL_PREF, this, true);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (observerService) {
+ observerService->AddObserver(this,
+ "network:offline-about-to-go-offline",
+ true);
+
+ observerService->AddObserver(this,
+ "net:clear-active-logins",
+ true);
+ }
+
+ return NS_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// nsIProtocolHandler methods:
+
+NS_IMETHODIMP
+nsFtpProtocolHandler::GetScheme(nsACString &result)
+{
+ result.AssignLiteral("ftp");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFtpProtocolHandler::GetDefaultPort(int32_t *result)
+{
+ *result = 21;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFtpProtocolHandler::GetProtocolFlags(uint32_t *result)
+{
+ *result = URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP |
+ URI_LOADABLE_BY_ANYONE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFtpProtocolHandler::NewURI(const nsACString &aSpec,
+ const char *aCharset,
+ nsIURI *aBaseURI,
+ nsIURI **result)
+{
+ nsAutoCString spec(aSpec);
+ spec.Trim(" \t\n\r"); // Match NS_IsAsciiWhitespace instead of HTML5
+
+ char *fwdPtr = spec.BeginWriting();
+
+ // now unescape it... %xx reduced inline to resulting character
+
+ int32_t len = NS_UnescapeURL(fwdPtr);
+
+ // NS_UnescapeURL() modified spec's buffer, truncate to ensure
+ // spec knows its new length.
+ spec.Truncate(len);
+
+ // return an error if we find a NUL, CR, or LF in the path
+ if (spec.FindCharInSet(CRLF) >= 0 || spec.FindChar('\0') >= 0)
+ return NS_ERROR_MALFORMED_URI;
+
+ nsresult rv;
+ nsCOMPtr<nsIStandardURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, 21, aSpec, aCharset, aBaseURI);
+ if (NS_FAILED(rv)) return rv;
+
+ return CallQueryInterface(url, result);
+}
+
+NS_IMETHODIMP
+nsFtpProtocolHandler::NewChannel2(nsIURI* url,
+ nsILoadInfo* aLoadInfo,
+ nsIChannel** result)
+{
+ return NewProxiedChannel2(url, nullptr, 0, nullptr, aLoadInfo, result);
+}
+
+NS_IMETHODIMP
+nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result)
+{
+ return NewChannel2(url, nullptr, result);
+}
+
+NS_IMETHODIMP
+nsFtpProtocolHandler::NewProxiedChannel2(nsIURI* uri, nsIProxyInfo* proxyInfo,
+ uint32_t proxyResolveFlags,
+ nsIURI *proxyURI,
+ nsILoadInfo* aLoadInfo,
+ nsIChannel* *result)
+{
+ NS_ENSURE_ARG_POINTER(uri);
+ RefPtr<nsBaseChannel> channel;
+ if (IsNeckoChild())
+ channel = new FTPChannelChild(uri);
+ else
+ channel = new nsFtpChannel(uri, proxyInfo);
+
+ nsresult rv = channel->Init();
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // set the loadInfo on the new channel
+ rv = channel->SetLoadInfo(aLoadInfo);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ channel.forget(result);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsFtpProtocolHandler::NewProxiedChannel(nsIURI* uri, nsIProxyInfo* proxyInfo,
+ uint32_t proxyResolveFlags,
+ nsIURI *proxyURI,
+ nsIChannel* *result)
+{
+ return NewProxiedChannel2(uri, proxyInfo, proxyResolveFlags,
+ proxyURI, nullptr /*loadinfo*/,
+ result);
+}
+
+NS_IMETHODIMP
+nsFtpProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
+{
+ *_retval = (port == 21 || port == 22);
+ return NS_OK;
+}
+
+// connection cache methods
+
+void
+nsFtpProtocolHandler::Timeout(nsITimer *aTimer, void *aClosure)
+{
+ LOG(("FTP:timeout reached for %p\n", aClosure));
+
+ bool found = gFtpHandler->mRootConnectionList.RemoveElement(aClosure);
+ if (!found) {
+ NS_ERROR("timerStruct not found");
+ return;
+ }
+
+ timerStruct* s = (timerStruct*)aClosure;
+ delete s;
+}
+
+nsresult
+nsFtpProtocolHandler::RemoveConnection(nsIURI *aKey, nsFtpControlConnection* *_retval)
+{
+ NS_ASSERTION(_retval, "null pointer");
+ NS_ASSERTION(aKey, "null pointer");
+
+ *_retval = nullptr;
+
+ nsAutoCString spec;
+ aKey->GetPrePath(spec);
+
+ LOG(("FTP:removing connection for %s\n", spec.get()));
+
+ timerStruct* ts = nullptr;
+ uint32_t i;
+ bool found = false;
+
+ for (i=0;i<mRootConnectionList.Length();++i) {
+ ts = mRootConnectionList[i];
+ if (strcmp(spec.get(), ts->key) == 0) {
+ found = true;
+ mRootConnectionList.RemoveElementAt(i);
+ break;
+ }
+ }
+
+ if (!found)
+ return NS_ERROR_FAILURE;
+
+ // swap connection ownership
+ ts->conn.forget(_retval);
+ delete ts;
+
+ return NS_OK;
+}
+
+nsresult
+nsFtpProtocolHandler::InsertConnection(nsIURI *aKey, nsFtpControlConnection *aConn)
+{
+ NS_ASSERTION(aConn, "null pointer");
+ NS_ASSERTION(aKey, "null pointer");
+
+ if (aConn->mSessionId != mSessionId)
+ return NS_ERROR_FAILURE;
+
+ nsAutoCString spec;
+ aKey->GetPrePath(spec);
+
+ LOG(("FTP:inserting connection for %s\n", spec.get()));
+
+ nsresult rv;
+ nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ timerStruct* ts = new timerStruct();
+ if (!ts)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = timer->InitWithFuncCallback(nsFtpProtocolHandler::Timeout,
+ ts,
+ mIdleTimeout*1000,
+ nsITimer::TYPE_REPEATING_SLACK);
+ if (NS_FAILED(rv)) {
+ delete ts;
+ return rv;
+ }
+
+ ts->key = ToNewCString(spec);
+ if (!ts->key) {
+ delete ts;
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ // ts->conn is a RefPtr
+ ts->conn = aConn;
+ ts->timer = timer;
+
+ //
+ // limit number of idle connections. if limit is reached, then prune
+ // eldest connection with matching key. if none matching, then prune
+ // eldest connection.
+ //
+ if (mRootConnectionList.Length() == IDLE_CONNECTION_LIMIT) {
+ uint32_t i;
+ for (i=0;i<mRootConnectionList.Length();++i) {
+ timerStruct *candidate = mRootConnectionList[i];
+ if (strcmp(candidate->key, ts->key) == 0) {
+ mRootConnectionList.RemoveElementAt(i);
+ delete candidate;
+ break;
+ }
+ }
+ if (mRootConnectionList.Length() == IDLE_CONNECTION_LIMIT) {
+ timerStruct *eldest = mRootConnectionList[0];
+ mRootConnectionList.RemoveElementAt(0);
+ delete eldest;
+ }
+ }
+
+ mRootConnectionList.AppendElement(ts);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsIObserver
+
+NS_IMETHODIMP
+nsFtpProtocolHandler::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ LOG(("FTP:observing [%s]\n", aTopic));
+
+ if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
+ nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(aSubject);
+ if (!branch) {
+ NS_ERROR("no prefbranch");
+ return NS_ERROR_UNEXPECTED;
+ }
+ int32_t val;
+ nsresult rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &val);
+ if (NS_SUCCEEDED(rv))
+ mIdleTimeout = val;
+
+ rv = branch->GetIntPref(QOS_DATA_PREF, &val);
+ if (NS_SUCCEEDED(rv))
+ mDataQoSBits = (uint8_t) clamped(val, 0, 0xff);
+
+ rv = branch->GetIntPref(QOS_CONTROL_PREF, &val);
+ if (NS_SUCCEEDED(rv))
+ mControlQoSBits = (uint8_t) clamped(val, 0, 0xff);
+ } else if (!strcmp(aTopic, "network:offline-about-to-go-offline")) {
+ ClearAllConnections();
+ } else if (!strcmp(aTopic, "net:clear-active-logins")) {
+ ClearAllConnections();
+ mSessionId++;
+ } else {
+ NS_NOTREACHED("unexpected topic");
+ }
+
+ return NS_OK;
+}
+
+void
+nsFtpProtocolHandler::ClearAllConnections()
+{
+ uint32_t i;
+ for (i=0;i<mRootConnectionList.Length();++i)
+ delete mRootConnectionList[i];
+ mRootConnectionList.Clear();
+}
diff --git a/netwerk/protocol/ftp/nsFtpProtocolHandler.h b/netwerk/protocol/ftp/nsFtpProtocolHandler.h
new file mode 100644
index 000000000..0bc3b0dfd
--- /dev/null
+++ b/netwerk/protocol/ftp/nsFtpProtocolHandler.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 nsFtpProtocolHandler_h__
+#define nsFtpProtocolHandler_h__
+
+#include "nsFtpControlConnection.h"
+#include "nsIProxiedProtocolHandler.h"
+#include "nsTArray.h"
+#include "nsITimer.h"
+#include "nsIObserver.h"
+#include "nsWeakReference.h"
+
+//-----------------------------------------------------------------------------
+
+class nsFtpProtocolHandler final : public nsIProxiedProtocolHandler
+ , public nsIObserver
+ , public nsSupportsWeakReference
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIPROTOCOLHANDLER
+ NS_DECL_NSIPROXIEDPROTOCOLHANDLER
+ NS_DECL_NSIOBSERVER
+
+ nsFtpProtocolHandler();
+
+ nsresult Init();
+
+ // FTP Connection list access
+ nsresult InsertConnection(nsIURI *aKey, nsFtpControlConnection *aConn);
+ nsresult RemoveConnection(nsIURI *aKey, nsFtpControlConnection **aConn);
+ uint32_t GetSessionId() { return mSessionId; }
+
+ uint8_t GetDataQoSBits() { return mDataQoSBits; }
+ uint8_t GetControlQoSBits() { return mControlQoSBits; }
+
+private:
+ virtual ~nsFtpProtocolHandler();
+
+ // Stuff for the timer callback function
+ struct timerStruct {
+ nsCOMPtr<nsITimer> timer;
+ RefPtr<nsFtpControlConnection> conn;
+ char *key;
+
+ timerStruct() : key(nullptr) {}
+
+ ~timerStruct() {
+ if (timer)
+ timer->Cancel();
+ if (key)
+ free(key);
+ if (conn) {
+ conn->Disconnect(NS_ERROR_ABORT);
+ }
+ }
+ };
+
+ static void Timeout(nsITimer *aTimer, void *aClosure);
+ void ClearAllConnections();
+
+ nsTArray<timerStruct*> mRootConnectionList;
+
+ int32_t mIdleTimeout;
+
+ // When "clear active logins" is performed, all idle connection are dropped
+ // and mSessionId is incremented. When nsFtpState wants to insert idle
+ // connection we refuse to cache if its mSessionId is different (i.e.
+ // control connection had been created before last "clear active logins" was
+ // performed.
+ uint32_t mSessionId;
+
+ uint8_t mControlQoSBits;
+ uint8_t mDataQoSBits;
+};
+
+//-----------------------------------------------------------------------------
+
+extern nsFtpProtocolHandler *gFtpHandler;
+
+#include "mozilla/Logging.h"
+extern mozilla::LazyLogModule gFTPLog;
+
+#endif // !nsFtpProtocolHandler_h__
diff --git a/netwerk/protocol/ftp/nsIFTPChannel.idl b/netwerk/protocol/ftp/nsIFTPChannel.idl
new file mode 100644
index 000000000..de8298384
--- /dev/null
+++ b/netwerk/protocol/ftp/nsIFTPChannel.idl
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsISupports.idl"
+
+/**
+ * This interface may be used to determine if a channel is a FTP channel.
+ */
+[scriptable, uuid(07f0d5cd-1fd5-4aa3-b6fc-665bdc5dbf9f)]
+interface nsIFTPChannel : nsISupports
+{
+ attribute PRTime lastModifiedTime;
+};
+
+/**
+ * This interface may be defined as a notification callback on the FTP
+ * channel. It allows a consumer to receive a log of the FTP control
+ * connection conversation.
+ */
+[scriptable, uuid(455d4234-0330-43d2-bbfb-99afbecbfeb0)]
+interface nsIFTPEventSink : nsISupports
+{
+ /**
+ * XXX document this method! (see bug 328915)
+ */
+ void OnFTPControlLog(in boolean server, in string msg);
+};
diff --git a/netwerk/protocol/ftp/nsIFTPChannelParentInternal.idl b/netwerk/protocol/ftp/nsIFTPChannelParentInternal.idl
new file mode 100644
index 000000000..2642c804b
--- /dev/null
+++ b/netwerk/protocol/ftp/nsIFTPChannelParentInternal.idl
@@ -0,0 +1,15 @@
+/* -*- 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 "nsISupports.idl"
+
+/**
+ * This is an internal interface for FTP parent channel.
+ */
+[builtinclass, uuid(87b58410-83cb-42a7-b57b-27c07ef828d7)]
+interface nsIFTPChannelParentInternal : nsISupports
+{
+ void setErrorMsg(in string msg, in boolean useUTF8);
+};
diff --git a/netwerk/protocol/ftp/test/frametest/contents.html b/netwerk/protocol/ftp/test/frametest/contents.html
new file mode 100644
index 000000000..077b8d8f7
--- /dev/null
+++ b/netwerk/protocol/ftp/test/frametest/contents.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<h2>Click a link to the left</h2>
+</body>
+</html>
diff --git a/netwerk/protocol/ftp/test/frametest/index.html b/netwerk/protocol/ftp/test/frametest/index.html
new file mode 100644
index 000000000..78c0dad58
--- /dev/null
+++ b/netwerk/protocol/ftp/test/frametest/index.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<title>FTP Frameset Test</title>
+
+</head>
+
+<frameset cols="30%,70%" name="ftp_main_frame">
+ <frame src="menu.html" name="ftp_menu" scrolling="yes" marginwidth="0" marginheight="0" noresize>
+ <frame src="contents.html" name="ftp_content" scrolling="YES" marginwidth="0" marginheight="0" noresize>
+</frameset>
+
+</html>
+
diff --git a/netwerk/protocol/ftp/test/frametest/menu.html b/netwerk/protocol/ftp/test/frametest/menu.html
new file mode 100644
index 000000000..afe3afcf2
--- /dev/null
+++ b/netwerk/protocol/ftp/test/frametest/menu.html
@@ -0,0 +1,373 @@
+<html>
+
+<script language="javascript">
+
+<!--
+
+function add_location(url)
+{
+ // faster to do this in one assignment, but who really cares.
+
+ var string = '<LI> <a href=\"javascript: ftp_open(\'';
+ string += url;
+ string += '\')\"';
+ string += 'onmouseover=\"window.status=\'ftp://';
+ string += url;
+ string += '\'; return true; \">';
+ string += url;
+ string += '</a>';
+ document.writeln(string);
+}
+
+function ftp_open(url)
+{
+ url = 'ftp://' + url;
+ parent.ftp_content.location=url;
+}
+
+// I like this format.
+document.writeln('<pre>');
+
+document.writeln('<br><OL><b>Company sites</b>');
+ add_location('ftp.mozilla.org');
+ add_location('ftp.sun.com');
+ add_location('ftp.iplanet.com');
+ add_location('ftp.netscape.com');
+ add_location('ftp.apple.com');
+ add_location('ftp.microsoft.com');
+ add_location('ftp.netmanage.com');
+
+document.writeln('</OL><br><OL><b>Misc sites</b>');
+ add_location('cal044202.student.utwente.nl');
+ add_location('download.intel.com');
+ add_location('fddisunsite.oit.unc.edu');
+ add_location('ftp.abas.de');
+ add_location('ftp.acm.org');
+ add_location('ftp.acomp.hu');
+ add_location('ftp.acri.fr');
+ add_location('ftp.alaska.edu');
+ add_location('ftp.altera.com');
+ add_location('ftp.amsat.org');
+ add_location('ftp.amtp.cam.ac.uk');
+ add_location('ftp.ar.freebsd.org');
+ add_location('ftp.ari.net');
+ add_location('ftp.arl.mil');
+ add_location('ftp.astro.ulg.ac.be');
+ add_location('ftp.avery-zweckform.com');
+ add_location('ftp.awi-bremerhaven.de');
+ add_location('ftp.axime-is.fr');
+ add_location('ftp.ba.cnr.it');
+ add_location('ftp.bath.ac.uk');
+ add_location('ftp.bic.mni.mcgill.ca');
+ add_location('ftp.biomed.ruhr-uni-bochum.de');
+ add_location('ftp.boerde.de');
+ add_location('ftp.bond.edu.au');
+ add_location('ftp.boulder.ibm.com');
+ add_location('ftp.brics.dk');
+ add_location('ftp.bris.ac.uk');
+ add_location('ftp.cablelabs.com');
+ add_location('ftp.cac.psu.edu');
+ add_location('ftp.cadpoint.se');
+ add_location('ftp.cas.cz');
+ add_location('ftp.cciw.ca');
+ add_location('ftp.ccs.queensu.ca');
+ add_location('ftp.ccsi.com');
+ add_location('ftp.cdrom.com');
+ add_location('ftp.cea.fr');
+ add_location('ftp.celestial.com');
+ add_location('ftp.cert.fr');
+ add_location('ftp.cgd.ucar.edu');
+ add_location('ftp.chiba-u.ac.jp');
+ add_location('ftp.cis.ksu.edu');
+ add_location('ftp.citi2.fr');
+ add_location('ftp.cityline.net');
+ add_location('ftp.cnam.fr');
+ add_location('ftp.cohesive.com');
+ add_location('ftp.contrib.net');
+ add_location('ftp.create.ucsb.edu');
+ add_location('ftp.cronyx.ru');
+ add_location('ftp.cs.arizona.edu');
+ add_location('ftp.cs.colorado.edu');
+ add_location('ftp.cs.concordia.ca');
+ add_location('ftp.cs.helsinki.fi');
+ add_location('ftp.cs.jhu.edu');
+ add_location('ftp.cs.monash.edu.au');
+ add_location('ftp.cs.ohiou.edu');
+ add_location('ftp.cs.rug.nl');
+ add_location('ftp.cs.toronto.edu');
+ add_location('ftp.cs.umanitoba.ca');
+ add_location('ftp.cs.uni-dortmund.de');
+ add_location('ftp.cs.vu.nl');
+ add_location('ftp.cse.cuhk.edu.hk');
+ add_location('ftp.cse.unsw.edu.au');
+ add_location('ftp.csse.monash.edu.au');
+ add_location('ftp.csus.edu');
+ add_location('ftp.cullasaja.com');
+ add_location('ftp.daimi.au.dk');
+ add_location('ftp.dcs.qmw.ac.uk');
+ add_location('ftp.delorie.com');
+ add_location('ftp.dementia.org');
+ add_location('ftp.dfki.uni-kl.de');
+ add_location('ftp.dgs.monash.edu.au');
+ add_location('ftp.dis.strath.ac.uk');
+ add_location('ftp.dosis.uni-dortmund.de');
+ add_location('ftp.duke.edu');
+ add_location('ftp.duplexx.com');
+ add_location('ftp.ece.ucdavis.edu');
+ add_location('ftp.ee.lbl.gov');
+ add_location('ftp.ee.rochester.edu');
+ add_location('ftp.ee.uts.edu.au');
+ add_location('ftp.efrei.fr');
+ add_location('ftp.elet.polimi.it');
+ add_location('ftp.elite.net');
+ add_location('ftp.embl-hamburg.de');
+ add_location('ftp.eng.buffalo.edu');
+ add_location('ftp.engr.uark.edu');
+ add_location('ftp.eni.co.jp');
+ add_location('ftp.enst-bretagne.fr');
+ add_location('ftp.epix.net');
+ add_location('ftp.eskimo.com');
+ add_location('ftp.essential.org');
+ add_location('ftp.eunet.fi');
+ add_location('ftp.eurexpo.com');
+ add_location('ftp.ex.ac.uk');
+ add_location('ftp.faximum.com');
+ add_location('ftp.fernuni-hagen.de');
+ add_location('ftp.fh-dortmund.de');
+ add_location('ftp.fit.qut.edu.au');
+ add_location('ftp.forum.swarthmore.edu');
+ add_location('ftp.fsu.edu');
+ add_location('ftp.ftp.epson.com');
+ add_location('ftp.fu-berlin.de');
+ add_location('ftp.fujixerox.co.jp');
+ add_location('ftp.game.org');
+ add_location('ftp.ge.ucl.ac.uk');
+ add_location('ftp.genetics.wisc.edu');
+ add_location('ftp.geo.uu.nl');
+ add_location('ftp.geom.umn.edu');
+ add_location('ftp.gfdl.gov');
+ add_location('ftp.gigo.com');
+ add_location('ftp.giss.nasa.gov');
+ add_location('ftp.globalnet.co.uk');
+ add_location('ftp.gnu.org');
+ add_location('ftp.gnu.vbs.at');
+ add_location('ftp.gps.caltech.edu');
+ add_location('ftp.grau-wzs.de');
+ add_location('ftp.gsoc.dlr.de');
+ add_location('ftp.gutenberg.org');
+ add_location('ftp.hawaii.edu');
+ add_location('ftp.hep.net');
+ add_location('ftp.hgc.edu');
+ add_location('ftp.hgmp.mrc.ac.uk');
+ add_location('ftp.hugin.dk');
+ add_location('ftp.ic.tsu.ru');
+ add_location('ftp.icce.rug.nl');
+ add_location('ftp.icon-stl.net');
+ add_location('ftp.icor.fr');
+ add_location('ftp.ics.uci.edu');
+ add_location('ftp.idsia.ch');
+ add_location('ftp.ifm.liu.se');
+ add_location('ftp.ifm.uni-kiel.de');
+ add_location('ftp.iglou.com');
+ add_location('ftp.ign.fr');
+ add_location('ftp.imag.fr');
+ add_location('ftp.inel.gov');
+ add_location('ftp.inf.ethz.ch');
+ add_location('ftp.inf.puc-rio.br');
+ add_location('ftp.infoflex.se');
+ add_location('ftp.informatik.rwth-aachen.de');
+ add_location('ftp.informatik.uni-bremen.de');
+ add_location('ftp.informatik.uni-hannover.de');
+ add_location('ftp.infoscandic.se');
+ add_location('ftp.intel.com');
+ add_location('ftp.intergraph.com');
+ add_location('ftp.ionet.net');
+ add_location('ftp.ipc.chiba-u.ac.jp');
+ add_location('ftp.ips.cs.tu-bs.de');
+ add_location('ftp.iram.rwth-aachen.de');
+ add_location('ftp.is.co.za');
+ add_location('ftp.isoc.org');
+ add_location('ftp.iteso.mx');
+ add_location('ftp.ivd.uni-stuttgart.de');
+ add_location('ftp.iway.fr');
+ add_location('ftp.jcu.edu.au');
+ add_location('ftp.jhuapl.edu');
+ add_location('ftp.jpix.ad.jp');
+ add_location('ftp.karlin.mff.cuni.cz');
+ add_location('ftp.kfu.com');
+ add_location('ftp.kfunigraz.ac.at');
+ add_location('ftp.khm.de');
+ add_location('ftp.ki.se');
+ add_location('ftp.komkon.org');
+ add_location('ftp.laas.fr');
+ add_location('ftp.lanl.gov');
+ add_location('ftp.lantronix.com');
+ add_location('ftp.lava.net');
+ add_location('ftp.lcs.mit.edu');
+ add_location('ftp.legend.co.uk');
+ add_location('ftp.leidenuniv.nl');
+ add_location('ftp.let.rug.nl');
+ add_location('ftp.linux.co.uk');
+ add_location('ftp.linux.unife.it');
+ add_location('ftp.liv.ac.uk');
+ add_location('ftp.livingston.com');
+ add_location('ftp.lnt.e-technik.tu-muenchen.de');
+ add_location('ftp.lsu.edu');
+ add_location('ftp.lth.se');
+ add_location('ftp.lysator.liu.se');
+ add_location('ftp.mailbase.ac.uk');
+ add_location('ftp.mainstream.net');
+ add_location('ftp.maricopa.edu');
+ add_location('ftp.math.fu-berlin.de');
+ add_location('ftp.math.hr');
+ add_location('ftp.math.utah.edu');
+ add_location('ftp.mathematik.uni-marburg.de');
+ add_location('ftp.maths.tcd.ie');
+ add_location('ftp.maths.usyd.edu.au');
+ add_location('ftp.mathworks.com');
+ add_location('ftp.mbb.ki.se');
+ add_location('ftp.mbt.ru');
+ add_location('ftp.mcs.net');
+ add_location('ftp.mcs.vuw.ac.nz');
+ add_location('ftp.media.mit.edu');
+ add_location('ftp.meme.com');
+ add_location('ftp.merl.com');
+ add_location('ftp.microport.com');
+ add_location('ftp.mms.de');
+ add_location('ftp.mpce.mq.edu.au');
+ add_location('ftp.mpgn.com');
+ add_location('ftp.mpipf-muenchen.mpg.de');
+ add_location('ftp.mscf.uky.edu');
+ add_location('ftp.natinst.com');
+ add_location('ftp.ncsa.uiuc.edu');
+ add_location('ftp.net-tel.co.uk');
+ add_location('ftp.net.cmu.edu');
+ add_location('ftp.netsw.org');
+ add_location('ftp.new-york.net');
+ add_location('ftp.nis.net');
+ add_location('ftp.nlm.nih.gov');
+ add_location('ftp.nmt.edu');
+ add_location('ftp.noao.edu');
+ add_location('ftp.ntnu.no');
+ add_location('ftp.nwu.edu');
+ add_location('ftp.nysaes.cornell.edu');
+ add_location('ftp.observ.u-bordeaux.fr');
+ add_location('ftp.oit.unc.edu');
+ add_location('ftp.oldenbourg.de');
+ add_location('ftp.omg.unb.ca');
+ add_location('ftp.onecall.net');
+ add_location('ftp.ornl.gov');
+ add_location('ftp.ozone.fmi.fi');
+ add_location('ftp.pacific.net.hk');
+ add_location('ftp.panix.com');
+ add_location('ftp.pcnet.com');
+ add_location('ftp.phred.org');
+ add_location('ftp.pnl.gov');
+ add_location('ftp.prairienet.org');
+ add_location('ftp.proxad.net');
+ add_location('ftp.proximity.com.au');
+ add_location('ftp.psg.com');
+ add_location('ftp.psy.uq.edu.au');
+ add_location('ftp.psychologie.uni-freiburg.de');
+ add_location('ftp.pwr.wroc.pl');
+ add_location('ftp.python.org');
+ add_location('ftp.quantum.de');
+ add_location('ftp.ra.phy.cam.ac.uk');
+ add_location('ftp.rasip.fer.hr');
+ add_location('ftp.rbgkew.org.uk');
+ add_location('ftp.rcsb.org');
+ add_location('ftp.realtime.net');
+ add_location('ftp.red-bean.com');
+ add_location('ftp.redac.co.uk');
+ add_location('ftp.redac.fr');
+ add_location('ftp.rediris.es');
+ add_location('ftp.rgn.it');
+ add_location('ftp.rice.edu');
+ add_location('ftp.rkk.hu');
+ add_location('ftp.robelle.com');
+ add_location('ftp.rose.hp.com');
+ add_location('ftp.rt66.com');
+ add_location('ftp.ruhr-uni-bochum.de');
+ add_location('ftp.rz.uni-frankfurt.de');
+ add_location('ftp.sat.dundee.ac.uk');
+ add_location('ftp.saugus.net');
+ add_location('ftp.schrodinger.com');
+ add_location('ftp.science-computing.de');
+ add_location('ftp.science.unitn.it');
+ add_location('ftp.sco.com');
+ add_location('ftp.scs.leeds.ac.uk');
+ add_location('ftp.scsr.nevada.edu');
+ add_location('ftp.sd.monash.edu.au');
+ add_location('ftp.sdv.fr');
+ add_location('ftp.selapo.vwl.uni-muenchen.de');
+ add_location('ftp.serv.net');
+ add_location('ftp.sgi.leeds.ac.uk');
+ add_location('ftp.shore.net');
+ add_location('ftp.socsci.auc.dk');
+ add_location('ftp.space.net');
+ add_location('ftp.spec.org');
+ add_location('ftp.stallion.com');
+ add_location('ftp.starnet.de');
+ add_location('ftp.stat.math.ethz.ch');
+ add_location('ftp.stat.umn.edu');
+ add_location('ftp.std.com');
+ add_location('ftp.structchem.uni-essen.de');
+ add_location('ftp.sunsite.org.uk');
+ add_location('ftp.syd.dms.csiro.au');
+ add_location('ftp.tapr.org');
+ add_location('ftp.teco.uni-karlsruhe.de');
+ add_location('ftp.tenon.com');
+ add_location('ftp.tierzucht.uni-kiel.de');
+ add_location('ftp.tnt.uni-hannover.de');
+ add_location('ftp.tu-clausthal.de');
+ add_location('ftp.uci.edu');
+ add_location('ftp.ucsd.edu');
+ add_location('ftp.udel.edu');
+ add_location('ftp.uec.ac.jp');
+ add_location('ftp.uibk.ac.at');
+ add_location('ftp.uit.co.uk');
+ add_location('ftp.uji.es');
+ add_location('ftp.uke.uni-hamburg.de');
+ add_location('ftp.ulcc.ac.uk');
+ add_location('ftp.um.es');
+ add_location('ftp.umi.cs.tu-bs.de');
+ add_location('ftp.uni-augsburg.de');
+ add_location('ftp.uni-dortmund.de');
+ add_location('ftp.uni-hannover.de');
+ add_location('ftp.uni-magdeburg.de');
+ add_location('ftp.unidata.ucar.edu');
+ add_location('ftp.unige.ch');
+ add_location('ftp.univ-aix.fr');
+ add_location('ftp.upc.es');
+ add_location('ftp.uradio.ku.dk');
+ add_location('ftp.uralexpress.ru');
+ add_location('ftp.urc.ac.ru');
+ add_location('ftp.ut.ee');
+ add_location('ftp.uunet.ca');
+ add_location('ftp.uwo.ca');
+ add_location('ftp.vaxxine.com');
+ add_location('ftp.visi.com');
+ add_location('ftp.vub.ac.be');
+ add_location('ftp.wfu.edu');
+ add_location('ftp.win.tue.nl');
+ add_location('ftp.wolfe.net');
+ add_location('sunsite.cnlab-switch.ch');
+ add_location('sunsite.sut.ac.jp');
+ add_location('ftp.cuhk.edu.hk');
+ add_location('ftp.cetis.hvu.nl');
+ add_location('ftp.clinet.fi');
+ add_location('ftp.gamma.ru');
+ add_location('ftp.itv.se');
+ add_location('ftp.cs.rpi.edu');
+ add_location('ftp.carrier.kiev.ua');
+ add_location('ftp.rosnet.ru');
+ add_location('ftp.nsk.su');
+ add_location('ftp.southcom.com.au');
+
+// -->
+
+</script>
+<body>
+<br><br><br>
+</body>
+</html>