netwerk/streamconv/converters/nsFTPDirListingConv.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

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

mercurial