diff options
Diffstat (limited to 'netwerk/protocol/ftp/nsFtpProtocolHandler.cpp')
-rw-r--r-- | netwerk/protocol/ftp/nsFtpProtocolHandler.cpp | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp b/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp new file mode 100644 index 0000000000..46b12767a3 --- /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(); +} |