Coverage Report

Created: 2018-09-25 14:53

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