Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
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/. */
6 #include "nsFTPDirListingConv.h"
7 #include "nsMemory.h"
8 #include "plstr.h"
9 #include "prlog.h"
10 #include "nsCOMPtr.h"
11 #include "nsEscape.h"
12 #include "nsStringStream.h"
13 #include "nsIStreamListener.h"
14 #include "nsCRT.h"
15 #include "nsAutoPtr.h"
16 #include "nsIChannel.h"
17 #include "nsIURI.h"
19 #include "ParseFTPList.h"
20 #include <algorithm>
22 #if defined(PR_LOGGING)
23 //
24 // Log module for FTP dir listing stream converter logging...
25 //
26 // To enable logging (see prlog.h for full details):
27 //
28 // set NSPR_LOG_MODULES=nsFTPDirListConv:5
29 // set NSPR_LOG_FILE=nspr.log
30 //
31 // this enables PR_LOG_DEBUG level information and places all output in
32 // the file nspr.log
33 //
34 PRLogModuleInfo* gFTPDirListConvLog = nullptr;
36 #endif /* PR_LOGGING */
38 // nsISupports implementation
39 NS_IMPL_ISUPPORTS(nsFTPDirListingConv,
40 nsIStreamConverter,
41 nsIStreamListener,
42 nsIRequestObserver)
45 // nsIStreamConverter implementation
46 NS_IMETHODIMP
47 nsFTPDirListingConv::Convert(nsIInputStream *aFromStream,
48 const char *aFromType,
49 const char *aToType,
50 nsISupports *aCtxt, nsIInputStream **_retval) {
51 return NS_ERROR_NOT_IMPLEMENTED;
52 }
55 // Stream converter service calls this to initialize the actual stream converter (us).
56 NS_IMETHODIMP
57 nsFTPDirListingConv::AsyncConvertData(const char *aFromType, const char *aToType,
58 nsIStreamListener *aListener, nsISupports *aCtxt) {
59 NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into FTP dir listing converter");
61 // hook up our final listener. this guy gets the various On*() calls we want to throw
62 // at him.
63 mFinalListener = aListener;
64 NS_ADDREF(mFinalListener);
66 PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG,
67 ("nsFTPDirListingConv::AsyncConvertData() converting FROM raw, TO application/http-index-format\n"));
69 return NS_OK;
70 }
73 // nsIStreamListener implementation
74 NS_IMETHODIMP
75 nsFTPDirListingConv::OnDataAvailable(nsIRequest* request, nsISupports *ctxt,
76 nsIInputStream *inStr, uint64_t sourceOffset, uint32_t count) {
77 NS_ASSERTION(request, "FTP dir listing stream converter needs a request");
79 nsresult rv;
81 nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv);
82 NS_ENSURE_SUCCESS(rv, rv);
84 uint32_t read, streamLen;
86 uint64_t streamLen64;
87 rv = inStr->Available(&streamLen64);
88 NS_ENSURE_SUCCESS(rv, rv);
89 streamLen = (uint32_t)std::min(streamLen64, uint64_t(UINT32_MAX - 1));
91 nsAutoArrayPtr<char> buffer(new char[streamLen + 1]);
92 NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
94 rv = inStr->Read(buffer, streamLen, &read);
95 NS_ENSURE_SUCCESS(rv, rv);
97 // the dir listings are ascii text, null terminate this sucker.
98 buffer[streamLen] = '\0';
100 PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("nsFTPDirListingConv::OnData(request = %x, ctxt = %x, inStr = %x, sourceOffset = %llu, count = %u)\n", request, ctxt, inStr, sourceOffset, count));
102 if (!mBuffer.IsEmpty()) {
103 // we have data left over from a previous OnDataAvailable() call.
104 // combine the buffers so we don't lose any data.
105 mBuffer.Append(buffer);
107 buffer = new char[mBuffer.Length()+1];
108 NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY);
110 strncpy(buffer, mBuffer.get(), mBuffer.Length()+1);
111 mBuffer.Truncate();
112 }
114 #ifndef DEBUG_dougt
115 PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, buffer.get()) );
116 #else
117 printf("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, buffer);
118 #endif // DEBUG_dougt
120 nsAutoCString indexFormat;
121 if (!mSentHeading) {
122 // build up the 300: line
123 nsCOMPtr<nsIURI> uri;
124 rv = channel->GetURI(getter_AddRefs(uri));
125 NS_ENSURE_SUCCESS(rv, rv);
127 rv = GetHeaders(indexFormat, uri);
128 NS_ENSURE_SUCCESS(rv, rv);
130 mSentHeading = true;
131 }
133 char *line = buffer;
134 line = DigestBufferLines(line, indexFormat);
136 #ifndef DEBUG_dougt
137 PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() sending the following %d bytes...\n\n%s\n\n",
138 indexFormat.Length(), indexFormat.get()) );
139 #else
140 char *unescData = ToNewCString(indexFormat);
141 NS_ENSURE_TRUE(unescData, NS_ERROR_OUT_OF_MEMORY);
143 nsUnescape(unescData);
144 printf("::OnData() sending the following %d bytes...\n\n%s\n\n", indexFormat.Length(), unescData);
145 nsMemory::Free(unescData);
146 #endif // DEBUG_dougt
148 // if there's any data left over, buffer it.
149 if (line && *line) {
150 mBuffer.Append(line);
151 PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() buffering the following %d bytes...\n\n%s\n\n",
152 strlen(line), line) );
153 }
155 // send the converted data out.
156 nsCOMPtr<nsIInputStream> inputData;
158 rv = NS_NewCStringInputStream(getter_AddRefs(inputData), indexFormat);
159 NS_ENSURE_SUCCESS(rv, rv);
161 rv = mFinalListener->OnDataAvailable(request, ctxt, inputData, 0, indexFormat.Length());
163 return rv;
164 }
167 // nsIRequestObserver implementation
168 NS_IMETHODIMP
169 nsFTPDirListingConv::OnStartRequest(nsIRequest* request, nsISupports *ctxt) {
170 // we don't care about start. move along... but start masqeurading
171 // as the http-index channel now.
172 return mFinalListener->OnStartRequest(request, ctxt);
173 }
175 NS_IMETHODIMP
176 nsFTPDirListingConv::OnStopRequest(nsIRequest* request, nsISupports *ctxt,
177 nsresult aStatus) {
178 // we don't care about stop. move along...
180 return mFinalListener->OnStopRequest(request, ctxt, aStatus);
181 }
184 // nsFTPDirListingConv methods
185 nsFTPDirListingConv::nsFTPDirListingConv() {
186 mFinalListener = nullptr;
187 mSentHeading = false;
188 }
190 nsFTPDirListingConv::~nsFTPDirListingConv() {
191 NS_IF_RELEASE(mFinalListener);
192 }
194 nsresult
195 nsFTPDirListingConv::Init() {
196 #if defined(PR_LOGGING)
197 //
198 // Initialize the global PRLogModule for FTP Protocol logging
199 // if necessary...
200 //
201 if (nullptr == gFTPDirListConvLog) {
202 gFTPDirListConvLog = PR_NewLogModule("nsFTPDirListingConv");
203 }
204 #endif /* PR_LOGGING */
206 return NS_OK;
207 }
209 nsresult
210 nsFTPDirListingConv::GetHeaders(nsACString& headers,
211 nsIURI* uri)
212 {
213 nsresult rv = NS_OK;
214 // build up 300 line
215 headers.AppendLiteral("300: ");
217 // Bug 111117 - don't print the password
218 nsAutoCString pw;
219 nsAutoCString spec;
220 uri->GetPassword(pw);
221 if (!pw.IsEmpty()) {
222 rv = uri->SetPassword(EmptyCString());
223 if (NS_FAILED(rv)) return rv;
224 rv = uri->GetAsciiSpec(spec);
225 if (NS_FAILED(rv)) return rv;
226 headers.Append(spec);
227 rv = uri->SetPassword(pw);
228 if (NS_FAILED(rv)) return rv;
229 } else {
230 rv = uri->GetAsciiSpec(spec);
231 if (NS_FAILED(rv)) return rv;
233 headers.Append(spec);
234 }
235 headers.Append(char(nsCRT::LF));
236 // END 300:
238 // build up the column heading; 200:
239 headers.AppendLiteral("200: filename content-length last-modified file-type\n");
240 // END 200:
241 return rv;
242 }
244 char *
245 nsFTPDirListingConv::DigestBufferLines(char *aBuffer, nsCString &aString) {
246 char *line = aBuffer;
247 char *eol;
248 bool cr = false;
250 list_state state;
252 // while we have new lines, parse 'em into application/http-index-format.
253 while ( line && (eol = PL_strchr(line, nsCRT::LF)) ) {
254 // yank any carriage returns too.
255 if (eol > line && *(eol-1) == nsCRT::CR) {
256 eol--;
257 *eol = '\0';
258 cr = true;
259 } else {
260 *eol = '\0';
261 cr = false;
262 }
264 list_result result;
266 int type = ParseFTPList(line, &state, &result );
268 // if it is other than a directory, file, or link -OR- if it is a
269 // directory named . or .., skip over this line.
270 if ((type != 'd' && type != 'f' && type != 'l') ||
271 (result.fe_type == 'd' && result.fe_fname[0] == '.' &&
272 (result.fe_fnlen == 1 || (result.fe_fnlen == 2 && result.fe_fname[1] == '.'))) )
273 {
274 if (cr)
275 line = eol+2;
276 else
277 line = eol+1;
279 continue;
280 }
282 // blast the index entry into the indexFormat buffer as a 201: line.
283 aString.AppendLiteral("201: ");
284 // FILENAME
286 // parsers for styles 'U' and 'W' handle sequence " -> " themself
287 if (state.lstyle != 'U' && state.lstyle != 'W') {
288 const char* offset = strstr(result.fe_fname, " -> ");
289 if (offset) {
290 result.fe_fnlen = offset - result.fe_fname;
291 }
292 }
294 nsAutoCString buf;
295 aString.Append('\"');
296 aString.Append(NS_EscapeURL(Substring(result.fe_fname,
297 result.fe_fname+result.fe_fnlen),
298 esc_Minimal|esc_OnlyASCII|esc_Forced,buf));
299 aString.AppendLiteral("\" ");
301 // CONTENT LENGTH
303 if (type != 'd')
304 {
305 for (int i = 0; i < int(sizeof(result.fe_size)); ++i)
306 {
307 if (result.fe_size[i] != '\0')
308 aString.Append((const char*)&result.fe_size[i], 1);
309 }
311 aString.Append(' ');
312 }
313 else
314 aString.AppendLiteral("0 ");
317 // MODIFIED DATE
318 char buffer[256] = "";
319 // Note: The below is the RFC822/1123 format, as required by
320 // the application/http-index-format specs
321 // viewers of such a format can then reformat this into the
322 // current locale (or anything else they choose)
323 PR_FormatTimeUSEnglish(buffer, sizeof(buffer),
324 "%a, %d %b %Y %H:%M:%S", &result.fe_time );
326 char *escapedDate = nsEscape(buffer, url_Path);
327 aString.Append(escapedDate);
328 nsMemory::Free(escapedDate);
329 aString.Append(' ');
331 // ENTRY TYPE
332 if (type == 'd')
333 aString.AppendLiteral("DIRECTORY");
334 else if (type == 'l')
335 aString.AppendLiteral("SYMBOLIC-LINK");
336 else
337 aString.AppendLiteral("FILE");
339 aString.Append(' ');
341 aString.Append(char(nsCRT::LF)); // complete this line
342 // END 201:
344 if (cr)
345 line = eol+2;
346 else
347 line = eol+1;
348 } // end while(eol)
350 return line;
351 }
353 nsresult
354 NS_NewFTPDirListingConv(nsFTPDirListingConv** aFTPDirListingConv)
355 {
356 NS_PRECONDITION(aFTPDirListingConv != nullptr, "null ptr");
357 if (! aFTPDirListingConv)
358 return NS_ERROR_NULL_POINTER;
360 *aFTPDirListingConv = new nsFTPDirListingConv();
361 if (! *aFTPDirListingConv)
362 return NS_ERROR_OUT_OF_MEMORY;
364 NS_ADDREF(*aFTPDirListingConv);
365 return (*aFTPDirListingConv)->Init();
366 }