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