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