netwerk/protocol/ftp/nsFtpProtocolHandler.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 5 *
michael@0 6 *
michael@0 7 * This Original Code has been modified by IBM Corporation.
michael@0 8 * Modifications made by IBM described herein are
michael@0 9 * Copyright (c) International Business Machines
michael@0 10 * Corporation, 2000
michael@0 11 *
michael@0 12 * Modifications to Mozilla code or documentation
michael@0 13 * identified per MPL Section 3.3
michael@0 14 *
michael@0 15 * Date Modified by Description of modification
michael@0 16 * 03/27/2000 IBM Corp. Added PR_CALLBACK for Optlink
michael@0 17 * use in OS2
michael@0 18 */
michael@0 19
michael@0 20 #include "mozilla/net/NeckoChild.h"
michael@0 21 #include "mozilla/net/FTPChannelChild.h"
michael@0 22 using namespace mozilla;
michael@0 23 using namespace mozilla::net;
michael@0 24
michael@0 25 #include "nsFtpProtocolHandler.h"
michael@0 26 #include "nsFTPChannel.h"
michael@0 27 #include "nsIStandardURL.h"
michael@0 28 #include "prlog.h"
michael@0 29 #include "nsIPrefService.h"
michael@0 30 #include "nsIPrefBranch.h"
michael@0 31 #include "nsIObserverService.h"
michael@0 32 #include "nsEscape.h"
michael@0 33 #include "nsAlgorithm.h"
michael@0 34 #include "nsICacheSession.h"
michael@0 35
michael@0 36 //-----------------------------------------------------------------------------
michael@0 37
michael@0 38 #if defined(PR_LOGGING)
michael@0 39 //
michael@0 40 // Log module for FTP Protocol logging...
michael@0 41 //
michael@0 42 // To enable logging (see prlog.h for full details):
michael@0 43 //
michael@0 44 // set NSPR_LOG_MODULES=nsFtp:5
michael@0 45 // set NSPR_LOG_FILE=nspr.log
michael@0 46 //
michael@0 47 // this enables PR_LOG_DEBUG level information and places all output in
michael@0 48 // the file nspr.log
michael@0 49 //
michael@0 50 PRLogModuleInfo* gFTPLog = nullptr;
michael@0 51 #endif
michael@0 52 #undef LOG
michael@0 53 #define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args)
michael@0 54
michael@0 55 //-----------------------------------------------------------------------------
michael@0 56
michael@0 57 #define IDLE_TIMEOUT_PREF "network.ftp.idleConnectionTimeout"
michael@0 58 #define IDLE_CONNECTION_LIMIT 8 /* TODO pref me */
michael@0 59
michael@0 60 #define QOS_DATA_PREF "network.ftp.data.qos"
michael@0 61 #define QOS_CONTROL_PREF "network.ftp.control.qos"
michael@0 62
michael@0 63 nsFtpProtocolHandler *gFtpHandler = nullptr;
michael@0 64
michael@0 65 //-----------------------------------------------------------------------------
michael@0 66
michael@0 67 nsFtpProtocolHandler::nsFtpProtocolHandler()
michael@0 68 : mIdleTimeout(-1)
michael@0 69 , mSessionId(0)
michael@0 70 , mControlQoSBits(0x00)
michael@0 71 , mDataQoSBits(0x00)
michael@0 72 {
michael@0 73 #if defined(PR_LOGGING)
michael@0 74 if (!gFTPLog)
michael@0 75 gFTPLog = PR_NewLogModule("nsFtp");
michael@0 76 #endif
michael@0 77 LOG(("FTP:creating handler @%x\n", this));
michael@0 78
michael@0 79 gFtpHandler = this;
michael@0 80 }
michael@0 81
michael@0 82 nsFtpProtocolHandler::~nsFtpProtocolHandler()
michael@0 83 {
michael@0 84 LOG(("FTP:destroying handler @%x\n", this));
michael@0 85
michael@0 86 NS_ASSERTION(mRootConnectionList.Length() == 0, "why wasn't Observe called?");
michael@0 87
michael@0 88 gFtpHandler = nullptr;
michael@0 89 }
michael@0 90
michael@0 91 NS_IMPL_ISUPPORTS(nsFtpProtocolHandler,
michael@0 92 nsIProtocolHandler,
michael@0 93 nsIProxiedProtocolHandler,
michael@0 94 nsIObserver,
michael@0 95 nsISupportsWeakReference)
michael@0 96
michael@0 97 nsresult
michael@0 98 nsFtpProtocolHandler::Init()
michael@0 99 {
michael@0 100 if (IsNeckoChild())
michael@0 101 NeckoChild::InitNeckoChild();
michael@0 102
michael@0 103 if (mIdleTimeout == -1) {
michael@0 104 nsresult rv;
michael@0 105 nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
michael@0 106 if (NS_FAILED(rv)) return rv;
michael@0 107
michael@0 108 rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &mIdleTimeout);
michael@0 109 if (NS_FAILED(rv))
michael@0 110 mIdleTimeout = 5*60; // 5 minute default
michael@0 111
michael@0 112 rv = branch->AddObserver(IDLE_TIMEOUT_PREF, this, true);
michael@0 113 if (NS_FAILED(rv)) return rv;
michael@0 114
michael@0 115 int32_t val;
michael@0 116 rv = branch->GetIntPref(QOS_DATA_PREF, &val);
michael@0 117 if (NS_SUCCEEDED(rv))
michael@0 118 mDataQoSBits = (uint8_t) clamped(val, 0, 0xff);
michael@0 119
michael@0 120 rv = branch->AddObserver(QOS_DATA_PREF, this, true);
michael@0 121 if (NS_FAILED(rv)) return rv;
michael@0 122
michael@0 123 rv = branch->GetIntPref(QOS_CONTROL_PREF, &val);
michael@0 124 if (NS_SUCCEEDED(rv))
michael@0 125 mControlQoSBits = (uint8_t) clamped(val, 0, 0xff);
michael@0 126
michael@0 127 rv = branch->AddObserver(QOS_CONTROL_PREF, this, true);
michael@0 128 if (NS_FAILED(rv)) return rv;
michael@0 129 }
michael@0 130
michael@0 131 nsCOMPtr<nsIObserverService> observerService =
michael@0 132 mozilla::services::GetObserverService();
michael@0 133 if (observerService) {
michael@0 134 observerService->AddObserver(this,
michael@0 135 "network:offline-about-to-go-offline",
michael@0 136 true);
michael@0 137
michael@0 138 observerService->AddObserver(this,
michael@0 139 "net:clear-active-logins",
michael@0 140 true);
michael@0 141 }
michael@0 142
michael@0 143 return NS_OK;
michael@0 144 }
michael@0 145
michael@0 146
michael@0 147 //-----------------------------------------------------------------------------
michael@0 148 // nsIProtocolHandler methods:
michael@0 149
michael@0 150 NS_IMETHODIMP
michael@0 151 nsFtpProtocolHandler::GetScheme(nsACString &result)
michael@0 152 {
michael@0 153 result.AssignLiteral("ftp");
michael@0 154 return NS_OK;
michael@0 155 }
michael@0 156
michael@0 157 NS_IMETHODIMP
michael@0 158 nsFtpProtocolHandler::GetDefaultPort(int32_t *result)
michael@0 159 {
michael@0 160 *result = 21;
michael@0 161 return NS_OK;
michael@0 162 }
michael@0 163
michael@0 164 NS_IMETHODIMP
michael@0 165 nsFtpProtocolHandler::GetProtocolFlags(uint32_t *result)
michael@0 166 {
michael@0 167 *result = URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP |
michael@0 168 URI_LOADABLE_BY_ANYONE;
michael@0 169 return NS_OK;
michael@0 170 }
michael@0 171
michael@0 172 NS_IMETHODIMP
michael@0 173 nsFtpProtocolHandler::NewURI(const nsACString &aSpec,
michael@0 174 const char *aCharset,
michael@0 175 nsIURI *aBaseURI,
michael@0 176 nsIURI **result)
michael@0 177 {
michael@0 178 nsAutoCString spec(aSpec);
michael@0 179 spec.Trim(" \t\n\r"); // Match NS_IsAsciiWhitespace instead of HTML5
michael@0 180
michael@0 181 char *fwdPtr = spec.BeginWriting();
michael@0 182
michael@0 183 // now unescape it... %xx reduced inline to resulting character
michael@0 184
michael@0 185 int32_t len = NS_UnescapeURL(fwdPtr);
michael@0 186
michael@0 187 // NS_UnescapeURL() modified spec's buffer, truncate to ensure
michael@0 188 // spec knows its new length.
michael@0 189 spec.Truncate(len);
michael@0 190
michael@0 191 // return an error if we find a NUL, CR, or LF in the path
michael@0 192 if (spec.FindCharInSet(CRLF) >= 0 || spec.FindChar('\0') >= 0)
michael@0 193 return NS_ERROR_MALFORMED_URI;
michael@0 194
michael@0 195 nsresult rv;
michael@0 196 nsCOMPtr<nsIStandardURL> url = do_CreateInstance(NS_STANDARDURL_CONTRACTID, &rv);
michael@0 197 if (NS_FAILED(rv)) return rv;
michael@0 198
michael@0 199 rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, 21, aSpec, aCharset, aBaseURI);
michael@0 200 if (NS_FAILED(rv)) return rv;
michael@0 201
michael@0 202 return CallQueryInterface(url, result);
michael@0 203 }
michael@0 204
michael@0 205 NS_IMETHODIMP
michael@0 206 nsFtpProtocolHandler::NewChannel(nsIURI* url, nsIChannel* *result)
michael@0 207 {
michael@0 208 return NewProxiedChannel(url, nullptr, 0, nullptr, result);
michael@0 209 }
michael@0 210
michael@0 211 NS_IMETHODIMP
michael@0 212 nsFtpProtocolHandler::NewProxiedChannel(nsIURI* uri, nsIProxyInfo* proxyInfo,
michael@0 213 uint32_t proxyResolveFlags,
michael@0 214 nsIURI *proxyURI,
michael@0 215 nsIChannel* *result)
michael@0 216 {
michael@0 217 NS_ENSURE_ARG_POINTER(uri);
michael@0 218 nsRefPtr<nsBaseChannel> channel;
michael@0 219 if (IsNeckoChild())
michael@0 220 channel = new FTPChannelChild(uri);
michael@0 221 else
michael@0 222 channel = new nsFtpChannel(uri, proxyInfo);
michael@0 223
michael@0 224 nsresult rv = channel->Init();
michael@0 225 if (NS_FAILED(rv)) {
michael@0 226 return rv;
michael@0 227 }
michael@0 228
michael@0 229 channel.forget(result);
michael@0 230 return rv;
michael@0 231 }
michael@0 232
michael@0 233 NS_IMETHODIMP
michael@0 234 nsFtpProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval)
michael@0 235 {
michael@0 236 *_retval = (port == 21 || port == 22);
michael@0 237 return NS_OK;
michael@0 238 }
michael@0 239
michael@0 240 // connection cache methods
michael@0 241
michael@0 242 void
michael@0 243 nsFtpProtocolHandler::Timeout(nsITimer *aTimer, void *aClosure)
michael@0 244 {
michael@0 245 LOG(("FTP:timeout reached for %p\n", aClosure));
michael@0 246
michael@0 247 bool found = gFtpHandler->mRootConnectionList.RemoveElement(aClosure);
michael@0 248 if (!found) {
michael@0 249 NS_ERROR("timerStruct not found");
michael@0 250 return;
michael@0 251 }
michael@0 252
michael@0 253 timerStruct* s = (timerStruct*)aClosure;
michael@0 254 delete s;
michael@0 255 }
michael@0 256
michael@0 257 nsresult
michael@0 258 nsFtpProtocolHandler::RemoveConnection(nsIURI *aKey, nsFtpControlConnection* *_retval)
michael@0 259 {
michael@0 260 NS_ASSERTION(_retval, "null pointer");
michael@0 261 NS_ASSERTION(aKey, "null pointer");
michael@0 262
michael@0 263 *_retval = nullptr;
michael@0 264
michael@0 265 nsAutoCString spec;
michael@0 266 aKey->GetPrePath(spec);
michael@0 267
michael@0 268 LOG(("FTP:removing connection for %s\n", spec.get()));
michael@0 269
michael@0 270 timerStruct* ts = nullptr;
michael@0 271 uint32_t i;
michael@0 272 bool found = false;
michael@0 273
michael@0 274 for (i=0;i<mRootConnectionList.Length();++i) {
michael@0 275 ts = mRootConnectionList[i];
michael@0 276 if (strcmp(spec.get(), ts->key) == 0) {
michael@0 277 found = true;
michael@0 278 mRootConnectionList.RemoveElementAt(i);
michael@0 279 break;
michael@0 280 }
michael@0 281 }
michael@0 282
michael@0 283 if (!found)
michael@0 284 return NS_ERROR_FAILURE;
michael@0 285
michael@0 286 // swap connection ownership
michael@0 287 *_retval = ts->conn;
michael@0 288 ts->conn = nullptr;
michael@0 289 delete ts;
michael@0 290
michael@0 291 return NS_OK;
michael@0 292 }
michael@0 293
michael@0 294 nsresult
michael@0 295 nsFtpProtocolHandler::InsertConnection(nsIURI *aKey, nsFtpControlConnection *aConn)
michael@0 296 {
michael@0 297 NS_ASSERTION(aConn, "null pointer");
michael@0 298 NS_ASSERTION(aKey, "null pointer");
michael@0 299
michael@0 300 if (aConn->mSessionId != mSessionId)
michael@0 301 return NS_ERROR_FAILURE;
michael@0 302
michael@0 303 nsAutoCString spec;
michael@0 304 aKey->GetPrePath(spec);
michael@0 305
michael@0 306 LOG(("FTP:inserting connection for %s\n", spec.get()));
michael@0 307
michael@0 308 nsresult rv;
michael@0 309 nsCOMPtr<nsITimer> timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
michael@0 310 if (NS_FAILED(rv)) return rv;
michael@0 311
michael@0 312 timerStruct* ts = new timerStruct();
michael@0 313 if (!ts)
michael@0 314 return NS_ERROR_OUT_OF_MEMORY;
michael@0 315
michael@0 316 rv = timer->InitWithFuncCallback(nsFtpProtocolHandler::Timeout,
michael@0 317 ts,
michael@0 318 mIdleTimeout*1000,
michael@0 319 nsITimer::TYPE_REPEATING_SLACK);
michael@0 320 if (NS_FAILED(rv)) {
michael@0 321 delete ts;
michael@0 322 return rv;
michael@0 323 }
michael@0 324
michael@0 325 ts->key = ToNewCString(spec);
michael@0 326 if (!ts->key) {
michael@0 327 delete ts;
michael@0 328 return NS_ERROR_OUT_OF_MEMORY;
michael@0 329 }
michael@0 330
michael@0 331 NS_ADDREF(aConn);
michael@0 332 ts->conn = aConn;
michael@0 333 ts->timer = timer;
michael@0 334
michael@0 335 //
michael@0 336 // limit number of idle connections. if limit is reached, then prune
michael@0 337 // eldest connection with matching key. if none matching, then prune
michael@0 338 // eldest connection.
michael@0 339 //
michael@0 340 if (mRootConnectionList.Length() == IDLE_CONNECTION_LIMIT) {
michael@0 341 uint32_t i;
michael@0 342 for (i=0;i<mRootConnectionList.Length();++i) {
michael@0 343 timerStruct *candidate = mRootConnectionList[i];
michael@0 344 if (strcmp(candidate->key, ts->key) == 0) {
michael@0 345 mRootConnectionList.RemoveElementAt(i);
michael@0 346 delete candidate;
michael@0 347 break;
michael@0 348 }
michael@0 349 }
michael@0 350 if (mRootConnectionList.Length() == IDLE_CONNECTION_LIMIT) {
michael@0 351 timerStruct *eldest = mRootConnectionList[0];
michael@0 352 mRootConnectionList.RemoveElementAt(0);
michael@0 353 delete eldest;
michael@0 354 }
michael@0 355 }
michael@0 356
michael@0 357 mRootConnectionList.AppendElement(ts);
michael@0 358 return NS_OK;
michael@0 359 }
michael@0 360
michael@0 361 //-----------------------------------------------------------------------------
michael@0 362 // nsIObserver
michael@0 363
michael@0 364 NS_IMETHODIMP
michael@0 365 nsFtpProtocolHandler::Observe(nsISupports *aSubject,
michael@0 366 const char *aTopic,
michael@0 367 const char16_t *aData)
michael@0 368 {
michael@0 369 LOG(("FTP:observing [%s]\n", aTopic));
michael@0 370
michael@0 371 if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
michael@0 372 nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(aSubject);
michael@0 373 if (!branch) {
michael@0 374 NS_ERROR("no prefbranch");
michael@0 375 return NS_ERROR_UNEXPECTED;
michael@0 376 }
michael@0 377 int32_t val;
michael@0 378 nsresult rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &val);
michael@0 379 if (NS_SUCCEEDED(rv))
michael@0 380 mIdleTimeout = val;
michael@0 381
michael@0 382 rv = branch->GetIntPref(QOS_DATA_PREF, &val);
michael@0 383 if (NS_SUCCEEDED(rv))
michael@0 384 mDataQoSBits = (uint8_t) clamped(val, 0, 0xff);
michael@0 385
michael@0 386 rv = branch->GetIntPref(QOS_CONTROL_PREF, &val);
michael@0 387 if (NS_SUCCEEDED(rv))
michael@0 388 mControlQoSBits = (uint8_t) clamped(val, 0, 0xff);
michael@0 389 } else if (!strcmp(aTopic, "network:offline-about-to-go-offline")) {
michael@0 390 ClearAllConnections();
michael@0 391 } else if (!strcmp(aTopic, "net:clear-active-logins")) {
michael@0 392 ClearAllConnections();
michael@0 393 mSessionId++;
michael@0 394 } else {
michael@0 395 NS_NOTREACHED("unexpected topic");
michael@0 396 }
michael@0 397
michael@0 398 return NS_OK;
michael@0 399 }
michael@0 400
michael@0 401 void
michael@0 402 nsFtpProtocolHandler::ClearAllConnections()
michael@0 403 {
michael@0 404 uint32_t i;
michael@0 405 for (i=0;i<mRootConnectionList.Length();++i)
michael@0 406 delete mRootConnectionList[i];
michael@0 407 mRootConnectionList.Clear();
michael@0 408 }

mercurial