/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 | } |