1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/streamconv/converters/nsFTPDirListingConv.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,366 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsFTPDirListingConv.h" 1.10 +#include "nsMemory.h" 1.11 +#include "plstr.h" 1.12 +#include "prlog.h" 1.13 +#include "nsCOMPtr.h" 1.14 +#include "nsEscape.h" 1.15 +#include "nsStringStream.h" 1.16 +#include "nsIStreamListener.h" 1.17 +#include "nsCRT.h" 1.18 +#include "nsAutoPtr.h" 1.19 +#include "nsIChannel.h" 1.20 +#include "nsIURI.h" 1.21 + 1.22 +#include "ParseFTPList.h" 1.23 +#include <algorithm> 1.24 + 1.25 +#if defined(PR_LOGGING) 1.26 +// 1.27 +// Log module for FTP dir listing stream converter logging... 1.28 +// 1.29 +// To enable logging (see prlog.h for full details): 1.30 +// 1.31 +// set NSPR_LOG_MODULES=nsFTPDirListConv:5 1.32 +// set NSPR_LOG_FILE=nspr.log 1.33 +// 1.34 +// this enables PR_LOG_DEBUG level information and places all output in 1.35 +// the file nspr.log 1.36 +// 1.37 +PRLogModuleInfo* gFTPDirListConvLog = nullptr; 1.38 + 1.39 +#endif /* PR_LOGGING */ 1.40 + 1.41 +// nsISupports implementation 1.42 +NS_IMPL_ISUPPORTS(nsFTPDirListingConv, 1.43 + nsIStreamConverter, 1.44 + nsIStreamListener, 1.45 + nsIRequestObserver) 1.46 + 1.47 + 1.48 +// nsIStreamConverter implementation 1.49 +NS_IMETHODIMP 1.50 +nsFTPDirListingConv::Convert(nsIInputStream *aFromStream, 1.51 + const char *aFromType, 1.52 + const char *aToType, 1.53 + nsISupports *aCtxt, nsIInputStream **_retval) { 1.54 + return NS_ERROR_NOT_IMPLEMENTED; 1.55 +} 1.56 + 1.57 + 1.58 +// Stream converter service calls this to initialize the actual stream converter (us). 1.59 +NS_IMETHODIMP 1.60 +nsFTPDirListingConv::AsyncConvertData(const char *aFromType, const char *aToType, 1.61 + nsIStreamListener *aListener, nsISupports *aCtxt) { 1.62 + NS_ASSERTION(aListener && aFromType && aToType, "null pointer passed into FTP dir listing converter"); 1.63 + 1.64 + // hook up our final listener. this guy gets the various On*() calls we want to throw 1.65 + // at him. 1.66 + mFinalListener = aListener; 1.67 + NS_ADDREF(mFinalListener); 1.68 + 1.69 + PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, 1.70 + ("nsFTPDirListingConv::AsyncConvertData() converting FROM raw, TO application/http-index-format\n")); 1.71 + 1.72 + return NS_OK; 1.73 +} 1.74 + 1.75 + 1.76 +// nsIStreamListener implementation 1.77 +NS_IMETHODIMP 1.78 +nsFTPDirListingConv::OnDataAvailable(nsIRequest* request, nsISupports *ctxt, 1.79 + nsIInputStream *inStr, uint64_t sourceOffset, uint32_t count) { 1.80 + NS_ASSERTION(request, "FTP dir listing stream converter needs a request"); 1.81 + 1.82 + nsresult rv; 1.83 + 1.84 + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv); 1.85 + NS_ENSURE_SUCCESS(rv, rv); 1.86 + 1.87 + uint32_t read, streamLen; 1.88 + 1.89 + uint64_t streamLen64; 1.90 + rv = inStr->Available(&streamLen64); 1.91 + NS_ENSURE_SUCCESS(rv, rv); 1.92 + streamLen = (uint32_t)std::min(streamLen64, uint64_t(UINT32_MAX - 1)); 1.93 + 1.94 + nsAutoArrayPtr<char> buffer(new char[streamLen + 1]); 1.95 + NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); 1.96 + 1.97 + rv = inStr->Read(buffer, streamLen, &read); 1.98 + NS_ENSURE_SUCCESS(rv, rv); 1.99 + 1.100 + // the dir listings are ascii text, null terminate this sucker. 1.101 + buffer[streamLen] = '\0'; 1.102 + 1.103 + PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("nsFTPDirListingConv::OnData(request = %x, ctxt = %x, inStr = %x, sourceOffset = %llu, count = %u)\n", request, ctxt, inStr, sourceOffset, count)); 1.104 + 1.105 + if (!mBuffer.IsEmpty()) { 1.106 + // we have data left over from a previous OnDataAvailable() call. 1.107 + // combine the buffers so we don't lose any data. 1.108 + mBuffer.Append(buffer); 1.109 + 1.110 + buffer = new char[mBuffer.Length()+1]; 1.111 + NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); 1.112 + 1.113 + strncpy(buffer, mBuffer.get(), mBuffer.Length()+1); 1.114 + mBuffer.Truncate(); 1.115 + } 1.116 + 1.117 +#ifndef DEBUG_dougt 1.118 + PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, buffer.get()) ); 1.119 +#else 1.120 + printf("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, buffer); 1.121 +#endif // DEBUG_dougt 1.122 + 1.123 + nsAutoCString indexFormat; 1.124 + if (!mSentHeading) { 1.125 + // build up the 300: line 1.126 + nsCOMPtr<nsIURI> uri; 1.127 + rv = channel->GetURI(getter_AddRefs(uri)); 1.128 + NS_ENSURE_SUCCESS(rv, rv); 1.129 + 1.130 + rv = GetHeaders(indexFormat, uri); 1.131 + NS_ENSURE_SUCCESS(rv, rv); 1.132 + 1.133 + mSentHeading = true; 1.134 + } 1.135 + 1.136 + char *line = buffer; 1.137 + line = DigestBufferLines(line, indexFormat); 1.138 + 1.139 +#ifndef DEBUG_dougt 1.140 + PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() sending the following %d bytes...\n\n%s\n\n", 1.141 + indexFormat.Length(), indexFormat.get()) ); 1.142 +#else 1.143 + char *unescData = ToNewCString(indexFormat); 1.144 + NS_ENSURE_TRUE(unescData, NS_ERROR_OUT_OF_MEMORY); 1.145 + 1.146 + nsUnescape(unescData); 1.147 + printf("::OnData() sending the following %d bytes...\n\n%s\n\n", indexFormat.Length(), unescData); 1.148 + nsMemory::Free(unescData); 1.149 +#endif // DEBUG_dougt 1.150 + 1.151 + // if there's any data left over, buffer it. 1.152 + if (line && *line) { 1.153 + mBuffer.Append(line); 1.154 + PR_LOG(gFTPDirListConvLog, PR_LOG_DEBUG, ("::OnData() buffering the following %d bytes...\n\n%s\n\n", 1.155 + strlen(line), line) ); 1.156 + } 1.157 + 1.158 + // send the converted data out. 1.159 + nsCOMPtr<nsIInputStream> inputData; 1.160 + 1.161 + rv = NS_NewCStringInputStream(getter_AddRefs(inputData), indexFormat); 1.162 + NS_ENSURE_SUCCESS(rv, rv); 1.163 + 1.164 + rv = mFinalListener->OnDataAvailable(request, ctxt, inputData, 0, indexFormat.Length()); 1.165 + 1.166 + return rv; 1.167 +} 1.168 + 1.169 + 1.170 +// nsIRequestObserver implementation 1.171 +NS_IMETHODIMP 1.172 +nsFTPDirListingConv::OnStartRequest(nsIRequest* request, nsISupports *ctxt) { 1.173 + // we don't care about start. move along... but start masqeurading 1.174 + // as the http-index channel now. 1.175 + return mFinalListener->OnStartRequest(request, ctxt); 1.176 +} 1.177 + 1.178 +NS_IMETHODIMP 1.179 +nsFTPDirListingConv::OnStopRequest(nsIRequest* request, nsISupports *ctxt, 1.180 + nsresult aStatus) { 1.181 + // we don't care about stop. move along... 1.182 + 1.183 + return mFinalListener->OnStopRequest(request, ctxt, aStatus); 1.184 +} 1.185 + 1.186 + 1.187 +// nsFTPDirListingConv methods 1.188 +nsFTPDirListingConv::nsFTPDirListingConv() { 1.189 + mFinalListener = nullptr; 1.190 + mSentHeading = false; 1.191 +} 1.192 + 1.193 +nsFTPDirListingConv::~nsFTPDirListingConv() { 1.194 + NS_IF_RELEASE(mFinalListener); 1.195 +} 1.196 + 1.197 +nsresult 1.198 +nsFTPDirListingConv::Init() { 1.199 +#if defined(PR_LOGGING) 1.200 + // 1.201 + // Initialize the global PRLogModule for FTP Protocol logging 1.202 + // if necessary... 1.203 + // 1.204 + if (nullptr == gFTPDirListConvLog) { 1.205 + gFTPDirListConvLog = PR_NewLogModule("nsFTPDirListingConv"); 1.206 + } 1.207 +#endif /* PR_LOGGING */ 1.208 + 1.209 + return NS_OK; 1.210 +} 1.211 + 1.212 +nsresult 1.213 +nsFTPDirListingConv::GetHeaders(nsACString& headers, 1.214 + nsIURI* uri) 1.215 +{ 1.216 + nsresult rv = NS_OK; 1.217 + // build up 300 line 1.218 + headers.AppendLiteral("300: "); 1.219 + 1.220 + // Bug 111117 - don't print the password 1.221 + nsAutoCString pw; 1.222 + nsAutoCString spec; 1.223 + uri->GetPassword(pw); 1.224 + if (!pw.IsEmpty()) { 1.225 + rv = uri->SetPassword(EmptyCString()); 1.226 + if (NS_FAILED(rv)) return rv; 1.227 + rv = uri->GetAsciiSpec(spec); 1.228 + if (NS_FAILED(rv)) return rv; 1.229 + headers.Append(spec); 1.230 + rv = uri->SetPassword(pw); 1.231 + if (NS_FAILED(rv)) return rv; 1.232 + } else { 1.233 + rv = uri->GetAsciiSpec(spec); 1.234 + if (NS_FAILED(rv)) return rv; 1.235 + 1.236 + headers.Append(spec); 1.237 + } 1.238 + headers.Append(char(nsCRT::LF)); 1.239 + // END 300: 1.240 + 1.241 + // build up the column heading; 200: 1.242 + headers.AppendLiteral("200: filename content-length last-modified file-type\n"); 1.243 + // END 200: 1.244 + return rv; 1.245 +} 1.246 + 1.247 +char * 1.248 +nsFTPDirListingConv::DigestBufferLines(char *aBuffer, nsCString &aString) { 1.249 + char *line = aBuffer; 1.250 + char *eol; 1.251 + bool cr = false; 1.252 + 1.253 + list_state state; 1.254 + 1.255 + // while we have new lines, parse 'em into application/http-index-format. 1.256 + while ( line && (eol = PL_strchr(line, nsCRT::LF)) ) { 1.257 + // yank any carriage returns too. 1.258 + if (eol > line && *(eol-1) == nsCRT::CR) { 1.259 + eol--; 1.260 + *eol = '\0'; 1.261 + cr = true; 1.262 + } else { 1.263 + *eol = '\0'; 1.264 + cr = false; 1.265 + } 1.266 + 1.267 + list_result result; 1.268 + 1.269 + int type = ParseFTPList(line, &state, &result ); 1.270 + 1.271 + // if it is other than a directory, file, or link -OR- if it is a 1.272 + // directory named . or .., skip over this line. 1.273 + if ((type != 'd' && type != 'f' && type != 'l') || 1.274 + (result.fe_type == 'd' && result.fe_fname[0] == '.' && 1.275 + (result.fe_fnlen == 1 || (result.fe_fnlen == 2 && result.fe_fname[1] == '.'))) ) 1.276 + { 1.277 + if (cr) 1.278 + line = eol+2; 1.279 + else 1.280 + line = eol+1; 1.281 + 1.282 + continue; 1.283 + } 1.284 + 1.285 + // blast the index entry into the indexFormat buffer as a 201: line. 1.286 + aString.AppendLiteral("201: "); 1.287 + // FILENAME 1.288 + 1.289 + // parsers for styles 'U' and 'W' handle sequence " -> " themself 1.290 + if (state.lstyle != 'U' && state.lstyle != 'W') { 1.291 + const char* offset = strstr(result.fe_fname, " -> "); 1.292 + if (offset) { 1.293 + result.fe_fnlen = offset - result.fe_fname; 1.294 + } 1.295 + } 1.296 + 1.297 + nsAutoCString buf; 1.298 + aString.Append('\"'); 1.299 + aString.Append(NS_EscapeURL(Substring(result.fe_fname, 1.300 + result.fe_fname+result.fe_fnlen), 1.301 + esc_Minimal|esc_OnlyASCII|esc_Forced,buf)); 1.302 + aString.AppendLiteral("\" "); 1.303 + 1.304 + // CONTENT LENGTH 1.305 + 1.306 + if (type != 'd') 1.307 + { 1.308 + for (int i = 0; i < int(sizeof(result.fe_size)); ++i) 1.309 + { 1.310 + if (result.fe_size[i] != '\0') 1.311 + aString.Append((const char*)&result.fe_size[i], 1); 1.312 + } 1.313 + 1.314 + aString.Append(' '); 1.315 + } 1.316 + else 1.317 + aString.AppendLiteral("0 "); 1.318 + 1.319 + 1.320 + // MODIFIED DATE 1.321 + char buffer[256] = ""; 1.322 + // Note: The below is the RFC822/1123 format, as required by 1.323 + // the application/http-index-format specs 1.324 + // viewers of such a format can then reformat this into the 1.325 + // current locale (or anything else they choose) 1.326 + PR_FormatTimeUSEnglish(buffer, sizeof(buffer), 1.327 + "%a, %d %b %Y %H:%M:%S", &result.fe_time ); 1.328 + 1.329 + char *escapedDate = nsEscape(buffer, url_Path); 1.330 + aString.Append(escapedDate); 1.331 + nsMemory::Free(escapedDate); 1.332 + aString.Append(' '); 1.333 + 1.334 + // ENTRY TYPE 1.335 + if (type == 'd') 1.336 + aString.AppendLiteral("DIRECTORY"); 1.337 + else if (type == 'l') 1.338 + aString.AppendLiteral("SYMBOLIC-LINK"); 1.339 + else 1.340 + aString.AppendLiteral("FILE"); 1.341 + 1.342 + aString.Append(' '); 1.343 + 1.344 + aString.Append(char(nsCRT::LF)); // complete this line 1.345 + // END 201: 1.346 + 1.347 + if (cr) 1.348 + line = eol+2; 1.349 + else 1.350 + line = eol+1; 1.351 + } // end while(eol) 1.352 + 1.353 + return line; 1.354 +} 1.355 + 1.356 +nsresult 1.357 +NS_NewFTPDirListingConv(nsFTPDirListingConv** aFTPDirListingConv) 1.358 +{ 1.359 + NS_PRECONDITION(aFTPDirListingConv != nullptr, "null ptr"); 1.360 + if (! aFTPDirListingConv) 1.361 + return NS_ERROR_NULL_POINTER; 1.362 + 1.363 + *aFTPDirListingConv = new nsFTPDirListingConv(); 1.364 + if (! *aFTPDirListingConv) 1.365 + return NS_ERROR_OUT_OF_MEMORY; 1.366 + 1.367 + NS_ADDREF(*aFTPDirListingConv); 1.368 + return (*aFTPDirListingConv)->Init(); 1.369 +}