1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,408 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.8 + * 1.9 + * 1.10 + * This Original Code has been modified by IBM Corporation. 1.11 + * Modifications made by IBM described herein are 1.12 + * Copyright (c) International Business Machines 1.13 + * Corporation, 2000 1.14 + * 1.15 + * Modifications to Mozilla code or documentation 1.16 + * identified per MPL Section 3.3 1.17 + * 1.18 + * Date Modified by Description of modification 1.19 + * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink 1.20 + * use in OS2 1.21 + */ 1.22 + 1.23 +#include "mozilla/net/NeckoChild.h" 1.24 +#include "mozilla/net/FTPChannelChild.h" 1.25 +using namespace mozilla; 1.26 +using namespace mozilla::net; 1.27 + 1.28 +#include "nsFtpProtocolHandler.h" 1.29 +#include "nsFTPChannel.h" 1.30 +#include "nsIStandardURL.h" 1.31 +#include "prlog.h" 1.32 +#include "nsIPrefService.h" 1.33 +#include "nsIPrefBranch.h" 1.34 +#include "nsIObserverService.h" 1.35 +#include "nsEscape.h" 1.36 +#include "nsAlgorithm.h" 1.37 +#include "nsICacheSession.h" 1.38 + 1.39 +//----------------------------------------------------------------------------- 1.40 + 1.41 +#if defined(PR_LOGGING) 1.42 +// 1.43 +// Log module for FTP Protocol logging... 1.44 +// 1.45 +// To enable logging (see prlog.h for full details): 1.46 +// 1.47 +// set NSPR_LOG_MODULES=nsFtp:5 1.48 +// set NSPR_LOG_FILE=nspr.log 1.49 +// 1.50 +// this enables PR_LOG_DEBUG level information and places all output in 1.51 +// the file nspr.log 1.52 +// 1.53 +PRLogModuleInfo* gFTPLog = nullptr; 1.54 +#endif 1.55 +#undef LOG 1.56 +#define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args) 1.57 + 1.58 +//----------------------------------------------------------------------------- 1.59 + 1.60 +#define IDLE_TIMEOUT_PREF "network.ftp.idleConnectionTimeout" 1.61 +#define IDLE_CONNECTION_LIMIT 8 /* TODO pref me */ 1.62 + 1.63 +#define QOS_DATA_PREF "network.ftp.data.qos" 1.64 +#define QOS_CONTROL_PREF "network.ftp.control.qos" 1.65 + 1.66 +nsFtpProtocolHandler *gFtpHandler = nullptr; 1.67 + 1.68 +//----------------------------------------------------------------------------- 1.69 + 1.70 +nsFtpProtocolHandler::nsFtpProtocolHandler() 1.71 + : mIdleTimeout(-1) 1.72 + , mSessionId(0) 1.73 + , mControlQoSBits(0x00) 1.74 + , mDataQoSBits(0x00) 1.75 +{ 1.76 +#if defined(PR_LOGGING) 1.77 + if (!gFTPLog) 1.78 + gFTPLog = PR_NewLogModule("nsFtp"); 1.79 +#endif 1.80 + LOG(("FTP:creating handler @%x\n", this)); 1.81 + 1.82 + gFtpHandler = this; 1.83 +} 1.84 + 1.85 +nsFtpProtocolHandler::~nsFtpProtocolHandler() 1.86 +{ 1.87 + LOG(("FTP:destroying handler @%x\n", this)); 1.88 + 1.89 + NS_ASSERTION(mRootConnectionList.Length() == 0, "why wasn't Observe called?"); 1.90 + 1.91 + gFtpHandler = nullptr; 1.92 +} 1.93 + 1.94 +NS_IMPL_ISUPPORTS(nsFtpProtocolHandler, 1.95 + nsIProtocolHandler, 1.96 + nsIProxiedProtocolHandler, 1.97 + nsIObserver, 1.98 + nsISupportsWeakReference) 1.99 + 1.100 +nsresult 1.101 +nsFtpProtocolHandler::Init() 1.102 +{ 1.103 + if (IsNeckoChild()) 1.104 + NeckoChild::InitNeckoChild(); 1.105 + 1.106 + if (mIdleTimeout == -1) { 1.107 + nsresult rv; 1.108 + nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); 1.109 + if (NS_FAILED(rv)) return rv; 1.110 + 1.111 + rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &mIdleTimeout); 1.112 + if (NS_FAILED(rv)) 1.113 + mIdleTimeout = 5*60; // 5 minute default 1.114 + 1.115 + rv = branch->AddObserver(IDLE_TIMEOUT_PREF, this, true); 1.116 + if (NS_FAILED(rv)) return rv; 1.117 + 1.118 + int32_t val; 1.119 + rv = branch->GetIntPref(QOS_DATA_PREF, &val); 1.120 + if (NS_SUCCEEDED(rv)) 1.121 + mDataQoSBits = (uint8_t) clamped(val, 0, 0xff); 1.122 + 1.123 + rv = branch->AddObserver(QOS_DATA_PREF, this, true); 1.124 + if (NS_FAILED(rv)) return rv; 1.125 + 1.126 + rv = branch->GetIntPref(QOS_CONTROL_PREF, &val); 1.127 + if (NS_SUCCEEDED(rv)) 1.128 + mControlQoSBits = (uint8_t) clamped(val, 0, 0xff); 1.129 + 1.130 + rv = branch->AddObserver(QOS_CONTROL_PREF, this, true); 1.131 + if (NS_FAILED(rv)) return rv; 1.132 + } 1.133 + 1.134 + nsCOMPtr<nsIObserverService> observerService = 1.135 + mozilla::services::GetObserverService(); 1.136 + if (observerService) { 1.137 + observerService->AddObserver(this, 1.138 + "network:offline-about-to-go-offline", 1.139 + true); 1.140 + 1.141 + observerService->AddObserver(this, 1.142 + "net:clear-active-logins", 1.143 + true); 1.144 + } 1.145 + 1.146 + return NS_OK; 1.147 +} 1.148 + 1.149 + 1.150 +//----------------------------------------------------------------------------- 1.151 +// nsIProtocolHandler methods: 1.152 + 1.153 +NS_IMETHODIMP 1.154 +nsFtpProtocolHandler::GetScheme(nsACString &result) 1.155 +{ 1.156 + result.AssignLiteral("ftp"); 1.157 + return NS_OK; 1.158 +} 1.159 + 1.160 +NS_IMETHODIMP 1.161 +nsFtpProtocolHandler::GetDefaultPort(int32_t *result) 1.162 +{ 1.163 + *result = 21; 1.164 + return NS_OK; 1.165 +} 1.166 + 1.167 +NS_IMETHODIMP 1.168 +nsFtpProtocolHandler::GetProtocolFlags(uint32_t *result) 1.169 +{ 1.170 + *result = URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP | 1.171 + URI_LOADABLE_BY_ANYONE; 1.172 + return NS_OK; 1.173 +} 1.174 + 1.175 +NS_IMETHODIMP 1.176 +nsFtpProtocolHandler::NewURI(const nsACString &aSpec, 1.177 + const char *aCharset, 1.178 + nsIURI *aBaseURI, 1.179 + nsIURI **result) 1.180 +{ 1.181 + nsAutoCString spec(aSpec); 1.182 + spec.Trim(" \t\n\r"); // Match NS_IsAsciiWhitespace instead of HTML5 1.183 + 1.184 + char *fwdPtr = spec.BeginWriting(); 1.185 + 1.186 + // now unescape it... %xx reduced inline to resulting character 1.187 + 1.188 + int32_t len = NS_UnescapeURL(fwdPtr); 1.189 + 1.190 + // NS_UnescapeURL() modified spec's buffer, truncate to ensure 1.191 + // spec knows its new length. 1.192 + spec.Truncate(len); 1.193 + 1.194 + // return an error if we find a NUL, CR, or LF in the path 1.195 + if (spec.FindCharInSet(CRLF) >= 0 || spec.FindChar('\0') >= 0) 1.196 + return NS_ERROR_MALFORMED_URI; 1.197 + 1.198 + nsresult rv; 1.199 + nsCOMPtr<nsIStandardURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv); 1.200 + if (NS_FAILED(rv)) return rv; 1.201 + 1.202 + rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, 21, aSpec, aCharset, aBaseURI); 1.203 + if (NS_FAILED(rv)) return rv; 1.204 + 1.205 + return CallQueryInterface(url, result); 1.206 +} 1.207 + 1.208 +NS_IMETHODIMP 1.209 +nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result) 1.210 +{ 1.211 + return NewProxiedChannel(url, nullptr, 0, nullptr, result); 1.212 +} 1.213 + 1.214 +NS_IMETHODIMP 1.215 +nsFtpProtocolHandler::NewProxiedChannel(nsIURI* uri, nsIProxyInfo* proxyInfo, 1.216 + uint32_t proxyResolveFlags, 1.217 + nsIURI *proxyURI, 1.218 + nsIChannel* *result) 1.219 +{ 1.220 + NS_ENSURE_ARG_POINTER(uri); 1.221 + nsRefPtr<nsBaseChannel> channel; 1.222 + if (IsNeckoChild()) 1.223 + channel = new FTPChannelChild(uri); 1.224 + else 1.225 + channel = new nsFtpChannel(uri, proxyInfo); 1.226 + 1.227 + nsresult rv = channel->Init(); 1.228 + if (NS_FAILED(rv)) { 1.229 + return rv; 1.230 + } 1.231 + 1.232 + channel.forget(result); 1.233 + return rv; 1.234 +} 1.235 + 1.236 +NS_IMETHODIMP 1.237 +nsFtpProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) 1.238 +{ 1.239 + *_retval = (port == 21 || port == 22); 1.240 + return NS_OK; 1.241 +} 1.242 + 1.243 +// connection cache methods 1.244 + 1.245 +void 1.246 +nsFtpProtocolHandler::Timeout(nsITimer *aTimer, void *aClosure) 1.247 +{ 1.248 + LOG(("FTP:timeout reached for %p\n", aClosure)); 1.249 + 1.250 + bool found = gFtpHandler->mRootConnectionList.RemoveElement(aClosure); 1.251 + if (!found) { 1.252 + NS_ERROR("timerStruct not found"); 1.253 + return; 1.254 + } 1.255 + 1.256 + timerStruct* s = (timerStruct*)aClosure; 1.257 + delete s; 1.258 +} 1.259 + 1.260 +nsresult 1.261 +nsFtpProtocolHandler::RemoveConnection(nsIURI *aKey, nsFtpControlConnection* *_retval) 1.262 +{ 1.263 + NS_ASSERTION(_retval, "null pointer"); 1.264 + NS_ASSERTION(aKey, "null pointer"); 1.265 + 1.266 + *_retval = nullptr; 1.267 + 1.268 + nsAutoCString spec; 1.269 + aKey->GetPrePath(spec); 1.270 + 1.271 + LOG(("FTP:removing connection for %s\n", spec.get())); 1.272 + 1.273 + timerStruct* ts = nullptr; 1.274 + uint32_t i; 1.275 + bool found = false; 1.276 + 1.277 + for (i=0;i<mRootConnectionList.Length();++i) { 1.278 + ts = mRootConnectionList[i]; 1.279 + if (strcmp(spec.get(), ts->key) == 0) { 1.280 + found = true; 1.281 + mRootConnectionList.RemoveElementAt(i); 1.282 + break; 1.283 + } 1.284 + } 1.285 + 1.286 + if (!found) 1.287 + return NS_ERROR_FAILURE; 1.288 + 1.289 + // swap connection ownership 1.290 + *_retval = ts->conn; 1.291 + ts->conn = nullptr; 1.292 + delete ts; 1.293 + 1.294 + return NS_OK; 1.295 +} 1.296 + 1.297 +nsresult 1.298 +nsFtpProtocolHandler::InsertConnection(nsIURI *aKey, nsFtpControlConnection *aConn) 1.299 +{ 1.300 + NS_ASSERTION(aConn, "null pointer"); 1.301 + NS_ASSERTION(aKey, "null pointer"); 1.302 + 1.303 + if (aConn->mSessionId != mSessionId) 1.304 + return NS_ERROR_FAILURE; 1.305 + 1.306 + nsAutoCString spec; 1.307 + aKey->GetPrePath(spec); 1.308 + 1.309 + LOG(("FTP:inserting connection for %s\n", spec.get())); 1.310 + 1.311 + nsresult rv; 1.312 + nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv); 1.313 + if (NS_FAILED(rv)) return rv; 1.314 + 1.315 + timerStruct* ts = new timerStruct(); 1.316 + if (!ts) 1.317 + return NS_ERROR_OUT_OF_MEMORY; 1.318 + 1.319 + rv = timer->InitWithFuncCallback(nsFtpProtocolHandler::Timeout, 1.320 + ts, 1.321 + mIdleTimeout*1000, 1.322 + nsITimer::TYPE_REPEATING_SLACK); 1.323 + if (NS_FAILED(rv)) { 1.324 + delete ts; 1.325 + return rv; 1.326 + } 1.327 + 1.328 + ts->key = ToNewCString(spec); 1.329 + if (!ts->key) { 1.330 + delete ts; 1.331 + return NS_ERROR_OUT_OF_MEMORY; 1.332 + } 1.333 + 1.334 + NS_ADDREF(aConn); 1.335 + ts->conn = aConn; 1.336 + ts->timer = timer; 1.337 + 1.338 + // 1.339 + // limit number of idle connections. if limit is reached, then prune 1.340 + // eldest connection with matching key. if none matching, then prune 1.341 + // eldest connection. 1.342 + // 1.343 + if (mRootConnectionList.Length() == IDLE_CONNECTION_LIMIT) { 1.344 + uint32_t i; 1.345 + for (i=0;i<mRootConnectionList.Length();++i) { 1.346 + timerStruct *candidate = mRootConnectionList[i]; 1.347 + if (strcmp(candidate->key, ts->key) == 0) { 1.348 + mRootConnectionList.RemoveElementAt(i); 1.349 + delete candidate; 1.350 + break; 1.351 + } 1.352 + } 1.353 + if (mRootConnectionList.Length() == IDLE_CONNECTION_LIMIT) { 1.354 + timerStruct *eldest = mRootConnectionList[0]; 1.355 + mRootConnectionList.RemoveElementAt(0); 1.356 + delete eldest; 1.357 + } 1.358 + } 1.359 + 1.360 + mRootConnectionList.AppendElement(ts); 1.361 + return NS_OK; 1.362 +} 1.363 + 1.364 +//----------------------------------------------------------------------------- 1.365 +// nsIObserver 1.366 + 1.367 +NS_IMETHODIMP 1.368 +nsFtpProtocolHandler::Observe(nsISupports *aSubject, 1.369 + const char *aTopic, 1.370 + const char16_t *aData) 1.371 +{ 1.372 + LOG(("FTP:observing [%s]\n", aTopic)); 1.373 + 1.374 + if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { 1.375 + nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(aSubject); 1.376 + if (!branch) { 1.377 + NS_ERROR("no prefbranch"); 1.378 + return NS_ERROR_UNEXPECTED; 1.379 + } 1.380 + int32_t val; 1.381 + nsresult rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &val); 1.382 + if (NS_SUCCEEDED(rv)) 1.383 + mIdleTimeout = val; 1.384 + 1.385 + rv = branch->GetIntPref(QOS_DATA_PREF, &val); 1.386 + if (NS_SUCCEEDED(rv)) 1.387 + mDataQoSBits = (uint8_t) clamped(val, 0, 0xff); 1.388 + 1.389 + rv = branch->GetIntPref(QOS_CONTROL_PREF, &val); 1.390 + if (NS_SUCCEEDED(rv)) 1.391 + mControlQoSBits = (uint8_t) clamped(val, 0, 0xff); 1.392 + } else if (!strcmp(aTopic, "network:offline-about-to-go-offline")) { 1.393 + ClearAllConnections(); 1.394 + } else if (!strcmp(aTopic, "net:clear-active-logins")) { 1.395 + ClearAllConnections(); 1.396 + mSessionId++; 1.397 + } else { 1.398 + NS_NOTREACHED("unexpected topic"); 1.399 + } 1.400 + 1.401 + return NS_OK; 1.402 +} 1.403 + 1.404 +void 1.405 +nsFtpProtocolHandler::ClearAllConnections() 1.406 +{ 1.407 + uint32_t i; 1.408 + for (i=0;i<mRootConnectionList.Length();++i) 1.409 + delete mRootConnectionList[i]; 1.410 + mRootConnectionList.Clear(); 1.411 +}