Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/streamconv/converters/nsFTPDirListingConv.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 "nsFTPDirListingConv.h"
7
#include "nsMemory.h"
8
#include "plstr.h"
9
#include "mozilla/Logging.h"
10
#include "nsCOMPtr.h"
11
#include "nsEscape.h"
12
#include "nsStringStream.h"
13
#include "nsIStreamListener.h"
14
#include "nsCRT.h"
15
#include "nsIChannel.h"
16
#include "nsIURI.h"
17
#include "nsIURIMutator.h"
18
19
#include "ParseFTPList.h"
20
#include <algorithm>
21
22
#include "mozilla/UniquePtrExtensions.h"
23
#include "mozilla/Unused.h"
24
25
//
26
// Log module for FTP dir listing stream converter logging...
27
//
28
// To enable logging (see prlog.h for full details):
29
//
30
//    set MOZ_LOG=nsFTPDirListConv:5
31
//    set MOZ_LOG_FILE=network.log
32
//
33
// This enables LogLevel::Debug level information and places all output in
34
// the file network.log.
35
//
36
static mozilla::LazyLogModule gFTPDirListConvLog("nsFTPDirListingConv");
37
using namespace mozilla;
38
39
// nsISupports implementation
40
NS_IMPL_ISUPPORTS(nsFTPDirListingConv,
41
                  nsIStreamConverter,
42
                  nsIStreamListener,
43
                  nsIRequestObserver)
44
45
46
// nsIStreamConverter implementation
47
NS_IMETHODIMP
48
nsFTPDirListingConv::Convert(nsIInputStream *aFromStream,
49
                             const char *aFromType,
50
                             const char *aToType,
51
0
                             nsISupports *aCtxt, nsIInputStream **_retval) {
52
0
    return NS_ERROR_NOT_IMPLEMENTED;
53
0
}
54
55
56
// Stream converter service calls this to initialize the actual stream converter (us).
57
NS_IMETHODIMP
58
nsFTPDirListingConv::AsyncConvertData(const char *aFromType, const char *aToType,
59
0
                                      nsIStreamListener *aListener, nsISupports *aCtxt) {
60
0
    NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into FTP dir listing converter");
61
0
62
0
    // hook up our final listener. this guy gets the various On*() calls we want to throw
63
0
    // at him.
64
0
    mFinalListener = aListener;
65
0
    NS_ADDREF(mFinalListener);
66
0
67
0
    MOZ_LOG(gFTPDirListConvLog, LogLevel::Debug,
68
0
        ("nsFTPDirListingConv::AsyncConvertData() converting FROM raw, TO application/http-index-format\n"));
69
0
70
0
    return NS_OK;
71
0
}
72
73
74
// nsIStreamListener implementation
75
NS_IMETHODIMP
76
nsFTPDirListingConv::OnDataAvailable(nsIRequest* request, nsISupports *ctxt,
77
0
                                  nsIInputStream *inStr, uint64_t sourceOffset, uint32_t count) {
78
0
    NS_ASSERTION(request, "FTP dir listing stream converter needs a request");
79
0
80
0
    nsresult rv;
81
0
82
0
    nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
83
0
    NS_ENSURE_SUCCESS(rv, rv);
84
0
85
0
    uint32_t read, streamLen;
86
0
87
0
    uint64_t streamLen64;
88
0
    rv = inStr->Available(&streamLen64);
89
0
    NS_ENSURE_SUCCESS(rv, rv);
90
0
    streamLen = (uint32_t)std::min(streamLen64, uint64_t(UINT32_MAX - 1));
91
0
92
0
    auto buffer = MakeUniqueFallible<char[]>(streamLen + 1);
93
0
    NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
94
0
95
0
    rv = inStr->Read(buffer.get(), streamLen, &read);
96
0
    NS_ENSURE_SUCCESS(rv, rv);
97
0
98
0
    // the dir listings are ascii text, null terminate this sucker.
99
0
    buffer[streamLen] = '\0';
100
0
101
0
    MOZ_LOG(gFTPDirListConvLog, LogLevel::Debug, ("nsFTPDirListingConv::OnData(request = %p, ctxt = %p, inStr = %p, sourceOffset = %" PRIu64 ", count = %u)\n", request, ctxt, inStr, sourceOffset, count));
102
0
103
0
    if (!mBuffer.IsEmpty()) {
104
0
        // we have data left over from a previous OnDataAvailable() call.
105
0
        // combine the buffers so we don't lose any data.
106
0
        mBuffer.Append(buffer.get());
107
0
108
0
        buffer = MakeUniqueFallible<char[]>(mBuffer.Length()+1);
109
0
        NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
110
0
111
0
        strncpy(buffer.get(), mBuffer.get(), mBuffer.Length()+1);
112
0
        mBuffer.Truncate();
113
0
    }
114
0
115
0
    MOZ_LOG(gFTPDirListConvLog, LogLevel::Debug, ("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, buffer.get()) );
116
0
117
0
    nsAutoCString indexFormat;
118
0
    if (!mSentHeading) {
119
0
        // build up the 300: line
120
0
        nsCOMPtr<nsIURI> uri;
121
0
        rv = channel->GetURI(getter_AddRefs(uri));
122
0
        NS_ENSURE_SUCCESS(rv, rv);
123
0
124
0
        rv = GetHeaders(indexFormat, uri);
125
0
        NS_ENSURE_SUCCESS(rv, rv);
126
0
127
0
        mSentHeading = true;
128
0
    }
129
0
130
0
    char *line = buffer.get();
131
0
    line = DigestBufferLines(line, indexFormat);
132
0
133
0
    MOZ_LOG(gFTPDirListConvLog, LogLevel::Debug, ("::OnData() sending the following %d bytes...\n\n%s\n\n",
134
0
        indexFormat.Length(), indexFormat.get()) );
135
0
136
0
    // if there's any data left over, buffer it.
137
0
    if (line && *line) {
138
0
        mBuffer.Append(line);
139
0
        MOZ_LOG(gFTPDirListConvLog, LogLevel::Debug, ("::OnData() buffering the following %zu bytes...\n\n%s\n\n",
140
0
            strlen(line), line) );
141
0
    }
142
0
143
0
    // send the converted data out.
144
0
    nsCOMPtr<nsIInputStream> inputData;
145
0
146
0
    rv = NS_NewCStringInputStream(getter_AddRefs(inputData), indexFormat);
147
0
    NS_ENSURE_SUCCESS(rv, rv);
148
0
149
0
    rv = mFinalListener->OnDataAvailable(request, ctxt, inputData, 0, indexFormat.Length());
150
0
151
0
    return rv;
152
0
}
153
154
155
// nsIRequestObserver implementation
156
NS_IMETHODIMP
157
0
nsFTPDirListingConv::OnStartRequest(nsIRequest* request, nsISupports *ctxt) {
158
0
    // we don't care about start. move along... but start masqeurading
159
0
    // as the http-index channel now.
160
0
    return mFinalListener->OnStartRequest(request, ctxt);
161
0
}
162
163
NS_IMETHODIMP
164
nsFTPDirListingConv::OnStopRequest(nsIRequest* request, nsISupports *ctxt,
165
0
                                   nsresult aStatus) {
166
0
    // we don't care about stop. move along...
167
0
168
0
    return mFinalListener->OnStopRequest(request, ctxt, aStatus);
169
0
}
170
171
172
// nsFTPDirListingConv methods
173
0
nsFTPDirListingConv::nsFTPDirListingConv() {
174
0
    mFinalListener      = nullptr;
175
0
    mSentHeading        = false;
176
0
}
177
178
0
nsFTPDirListingConv::~nsFTPDirListingConv() {
179
0
    NS_IF_RELEASE(mFinalListener);
180
0
}
181
182
nsresult
183
nsFTPDirListingConv::GetHeaders(nsACString& headers,
184
                                nsIURI* uri)
185
0
{
186
0
    nsresult rv = NS_OK;
187
0
    // build up 300 line
188
0
    headers.AppendLiteral("300: ");
189
0
190
0
    // Bug 111117 - don't print the password
191
0
    nsAutoCString pw;
192
0
    nsAutoCString spec;
193
0
    uri->GetPassword(pw);
194
0
    if (!pw.IsEmpty()) {
195
0
         nsCOMPtr<nsIURI> noPassURI;
196
0
         rv = NS_MutateURI(uri)
197
0
                .SetPassword(EmptyCString())
198
0
                .Finalize(noPassURI);
199
0
         if (NS_FAILED(rv)) return rv;
200
0
         rv = noPassURI->GetAsciiSpec(spec);
201
0
         if (NS_FAILED(rv)) return rv;
202
0
         headers.Append(spec);
203
0
    } else {
204
0
        rv = uri->GetAsciiSpec(spec);
205
0
        if (NS_FAILED(rv)) return rv;
206
0
207
0
        headers.Append(spec);
208
0
    }
209
0
    headers.Append(char(nsCRT::LF));
210
0
    // END 300:
211
0
212
0
    // build up the column heading; 200:
213
0
    headers.AppendLiteral("200: filename content-length last-modified file-type\n");
214
0
    // END 200:
215
0
    return rv;
216
0
}
217
218
char *
219
0
nsFTPDirListingConv::DigestBufferLines(char *aBuffer, nsCString &aString) {
220
0
    char *line = aBuffer;
221
0
    char *eol;
222
0
    bool cr = false;
223
0
224
0
    list_state state;
225
0
226
0
    // while we have new lines, parse 'em into application/http-index-format.
227
0
    while ( line && (eol = PL_strchr(line, nsCRT::LF)) ) {
228
0
        // yank any carriage returns too.
229
0
        if (eol > line && *(eol-1) == nsCRT::CR) {
230
0
            eol--;
231
0
            *eol = '\0';
232
0
            cr = true;
233
0
        } else {
234
0
            *eol = '\0';
235
0
            cr = false;
236
0
        }
237
0
238
0
        list_result result;
239
0
240
0
        int type = ParseFTPList(line, &state, &result );
241
0
242
0
        // if it is other than a directory, file, or link -OR- if it is a
243
0
        // directory named . or .., skip over this line.
244
0
        if ((type != 'd' && type != 'f' && type != 'l') ||
245
0
            (result.fe_type == 'd' && result.fe_fname[0] == '.' &&
246
0
            (result.fe_fnlen == 1 || (result.fe_fnlen == 2 &&  result.fe_fname[1] == '.'))) )
247
0
        {
248
0
            if (cr)
249
0
                line = eol+2;
250
0
            else
251
0
                line = eol+1;
252
0
253
0
            continue;
254
0
        }
255
0
256
0
        // blast the index entry into the indexFormat buffer as a 201: line.
257
0
        aString.AppendLiteral("201: ");
258
0
        // FILENAME
259
0
260
0
        // parsers for styles 'U' and 'W' handle sequence " -> " themself
261
0
  if (state.lstyle != 'U' && state.lstyle != 'W') {
262
0
            const char* offset = strstr(result.fe_fname, " -> ");
263
0
            if (offset) {
264
0
                result.fe_fnlen = offset - result.fe_fname;
265
0
            }
266
0
        }
267
0
268
0
        nsAutoCString buf;
269
0
        aString.Append('\"');
270
0
        aString.Append(NS_EscapeURL(Substring(result.fe_fname,
271
0
                                              result.fe_fname+result.fe_fnlen),
272
0
                                    esc_Minimal|esc_OnlyASCII|esc_Forced,buf));
273
0
        aString.AppendLiteral("\" ");
274
0
275
0
        // CONTENT LENGTH
276
0
277
0
        if (type != 'd')
278
0
        {
279
0
            for (char& fe : result.fe_size)
280
0
            {
281
0
                if (fe != '\0')
282
0
                    aString.Append((const char*)&fe, 1);
283
0
            }
284
0
285
0
            aString.Append(' ');
286
0
        }
287
0
        else
288
0
            aString.AppendLiteral("0 ");
289
0
290
0
291
0
        // MODIFIED DATE
292
0
        char buffer[256] = "";
293
0
294
0
        // ParseFTPList can return time structure with invalid values.
295
0
        // PR_NormalizeTime will set all values into valid limits.
296
0
        result.fe_time.tm_params.tp_gmt_offset = 0;
297
0
        result.fe_time.tm_params.tp_dst_offset = 0;
298
0
        PR_NormalizeTime(&result.fe_time, PR_GMTParameters);
299
0
300
0
        // Note: The below is the RFC822/1123 format, as required by
301
0
        // the application/http-index-format specs
302
0
        // viewers of such a format can then reformat this into the
303
0
        // current locale (or anything else they choose)
304
0
        PR_FormatTimeUSEnglish(buffer, sizeof(buffer),
305
0
                               "%a, %d %b %Y %H:%M:%S", &result.fe_time );
306
0
307
0
        nsAutoCString escaped;
308
0
        Unused << NS_WARN_IF(!NS_Escape(nsDependentCString(buffer), escaped, url_Path));
309
0
        aString.Append(escaped);
310
0
        aString.Append(' ');
311
0
312
0
        // ENTRY TYPE
313
0
        if (type == 'd')
314
0
            aString.AppendLiteral("DIRECTORY");
315
0
        else if (type == 'l')
316
0
            aString.AppendLiteral("SYMBOLIC-LINK");
317
0
        else
318
0
            aString.AppendLiteral("FILE");
319
0
320
0
        aString.Append(' ');
321
0
322
0
        aString.Append(char(nsCRT::LF)); // complete this line
323
0
        // END 201:
324
0
325
0
        if (cr)
326
0
            line = eol+2;
327
0
        else
328
0
            line = eol+1;
329
0
    } // end while(eol)
330
0
331
0
    return line;
332
0
}
333
334
nsresult
335
NS_NewFTPDirListingConv(nsFTPDirListingConv** aFTPDirListingConv)
336
0
{
337
0
    MOZ_ASSERT(aFTPDirListingConv != nullptr, "null ptr");
338
0
    if (! aFTPDirListingConv)
339
0
        return NS_ERROR_NULL_POINTER;
340
0
341
0
    *aFTPDirListingConv = new nsFTPDirListingConv();
342
0
    if (! *aFTPDirListingConv)
343
0
        return NS_ERROR_OUT_OF_MEMORY;
344
0
345
0
    NS_ADDREF(*aFTPDirListingConv);
346
0
    return NS_OK;
347
0
}