diff options
Diffstat (limited to '')
-rw-r--r-- | netwerk/streamconv/converters/nsFTPDirListingConv.cpp | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/netwerk/streamconv/converters/nsFTPDirListingConv.cpp b/netwerk/streamconv/converters/nsFTPDirListingConv.cpp new file mode 100644 index 0000000000..d155481f55 --- /dev/null +++ b/netwerk/streamconv/converters/nsFTPDirListingConv.cpp @@ -0,0 +1,342 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsFTPDirListingConv.h" +#include "nsMemory.h" +#include "plstr.h" +#include "mozilla/Logging.h" +#include "nsCOMPtr.h" +#include "nsEscape.h" +#include "nsStringStream.h" +#include "nsIStreamListener.h" +#include "nsCRT.h" +#include "nsIChannel.h" +#include "nsIURI.h" +#include "nsIURIMutator.h" + +#include "ParseFTPList.h" +#include <algorithm> + +#include "mozilla/UniquePtrExtensions.h" +#include "mozilla/Unused.h" + +// +// Log module for FTP dir listing stream converter logging... +// +// To enable logging (see prlog.h for full details): +// +// set MOZ_LOG=nsFTPDirListConv:5 +// set MOZ_LOG_FILE=network.log +// +// This enables LogLevel::Debug level information and places all output in +// the file network.log. +// +static mozilla::LazyLogModule gFTPDirListConvLog("nsFTPDirListingConv"); +using namespace mozilla; + +// nsISupports implementation +NS_IMPL_ISUPPORTS(nsFTPDirListingConv, nsIStreamConverter, nsIStreamListener, + nsIRequestObserver) + +// nsIStreamConverter implementation +NS_IMETHODIMP +nsFTPDirListingConv::Convert(nsIInputStream* aFromStream, const char* aFromType, + const char* aToType, nsISupports* aCtxt, + nsIInputStream** _retval) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +// Stream converter service calls this to initialize the actual stream converter +// (us). +NS_IMETHODIMP +nsFTPDirListingConv::AsyncConvertData(const char* aFromType, + const char* aToType, + nsIStreamListener* aListener, + nsISupports* aCtxt) { + NS_ASSERTION(aListener && aFromType && aToType, + "null pointer passed into FTP dir listing converter"); + + // hook up our final listener. this guy gets the various On*() calls we want + // to throw at him. + mFinalListener = aListener; + NS_ADDREF(mFinalListener); + + MOZ_LOG(gFTPDirListConvLog, LogLevel::Debug, + ("nsFTPDirListingConv::AsyncConvertData() converting FROM raw, TO " + "application/http-index-format\n")); + + return NS_OK; +} + +NS_IMETHODIMP +nsFTPDirListingConv::GetConvertedType(const nsACString& aFromType, + nsIChannel* aChannel, + nsACString& aToType) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +// nsIStreamListener implementation +NS_IMETHODIMP +nsFTPDirListingConv::OnDataAvailable(nsIRequest* request, nsIInputStream* inStr, + uint64_t sourceOffset, uint32_t count) { + NS_ASSERTION(request, "FTP dir listing stream converter needs a request"); + + nsresult rv; + + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t read, streamLen; + + uint64_t streamLen64; + rv = inStr->Available(&streamLen64); + NS_ENSURE_SUCCESS(rv, rv); + streamLen = (uint32_t)std::min(streamLen64, uint64_t(UINT32_MAX - 1)); + + auto buffer = MakeUniqueFallible<char[]>(streamLen + 1); + NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); + + rv = inStr->Read(buffer.get(), streamLen, &read); + NS_ENSURE_SUCCESS(rv, rv); + + // the dir listings are ascii text, null terminate this sucker. + buffer[streamLen] = '\0'; + + MOZ_LOG(gFTPDirListConvLog, LogLevel::Debug, + ("nsFTPDirListingConv::OnData(request = %p, inStr = %p, " + "sourceOffset = %" PRIu64 ", count = %u)\n", + request, inStr, sourceOffset, count)); + + if (!mBuffer.IsEmpty()) { + // we have data left over from a previous OnDataAvailable() call. + // combine the buffers so we don't lose any data. + mBuffer.Append(buffer.get()); + + buffer = MakeUniqueFallible<char[]>(mBuffer.Length() + 1); + NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); + + strncpy(buffer.get(), mBuffer.get(), mBuffer.Length() + 1); + mBuffer.Truncate(); + } + + MOZ_LOG(gFTPDirListConvLog, LogLevel::Debug, + ("::OnData() received the following %d bytes...\n\n%s\n\n", streamLen, + buffer.get())); + + nsAutoCString indexFormat; + if (!mSentHeading) { + // build up the 300: line + nsCOMPtr<nsIURI> uri; + rv = channel->GetURI(getter_AddRefs(uri)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = GetHeaders(indexFormat, uri); + NS_ENSURE_SUCCESS(rv, rv); + + mSentHeading = true; + } + + char* line = buffer.get(); + line = DigestBufferLines(line, indexFormat); + + MOZ_LOG(gFTPDirListConvLog, LogLevel::Debug, + ("::OnData() sending the following %d bytes...\n\n%s\n\n", + indexFormat.Length(), indexFormat.get())); + + // if there's any data left over, buffer it. + if (line && *line) { + mBuffer.Append(line); + MOZ_LOG(gFTPDirListConvLog, LogLevel::Debug, + ("::OnData() buffering the following %zu bytes...\n\n%s\n\n", + strlen(line), line)); + } + + // send the converted data out. + nsCOMPtr<nsIInputStream> inputData; + + rv = NS_NewCStringInputStream(getter_AddRefs(inputData), indexFormat); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mFinalListener->OnDataAvailable(request, inputData, 0, + indexFormat.Length()); + + return rv; +} + +// nsIRequestObserver implementation +NS_IMETHODIMP +nsFTPDirListingConv::OnStartRequest(nsIRequest* request) { + // we don't care about start. move along... but start masqeurading + // as the http-index channel now. + return mFinalListener->OnStartRequest(request); +} + +NS_IMETHODIMP +nsFTPDirListingConv::OnStopRequest(nsIRequest* request, nsresult aStatus) { + // we don't care about stop. move along... + + return mFinalListener->OnStopRequest(request, aStatus); +} + +// nsFTPDirListingConv methods +nsFTPDirListingConv::nsFTPDirListingConv() { + mFinalListener = nullptr; + mSentHeading = false; +} + +nsFTPDirListingConv::~nsFTPDirListingConv() { NS_IF_RELEASE(mFinalListener); } + +nsresult nsFTPDirListingConv::GetHeaders(nsACString& headers, nsIURI* uri) { + nsresult rv = NS_OK; + // build up 300 line + headers.AppendLiteral("300: "); + + // Bug 111117 - don't print the password + nsAutoCString pw; + nsAutoCString spec; + uri->GetPassword(pw); + if (!pw.IsEmpty()) { + nsCOMPtr<nsIURI> noPassURI; + rv = NS_MutateURI(uri).SetPassword(""_ns).Finalize(noPassURI); + if (NS_FAILED(rv)) return rv; + rv = noPassURI->GetAsciiSpec(spec); + if (NS_FAILED(rv)) return rv; + headers.Append(spec); + } else { + rv = uri->GetAsciiSpec(spec); + if (NS_FAILED(rv)) return rv; + + headers.Append(spec); + } + headers.Append(char(nsCRT::LF)); + // END 300: + + // build up the column heading; 200: + headers.AppendLiteral( + "200: filename content-length last-modified file-type\n"); + // END 200: + return rv; +} + +char* nsFTPDirListingConv::DigestBufferLines(char* aBuffer, + nsCString& aString) { + char* line = aBuffer; + char* eol; + bool cr = false; + + list_state state; + + // while we have new lines, parse 'em into application/http-index-format. + while (line && (eol = PL_strchr(line, nsCRT::LF))) { + // yank any carriage returns too. + if (eol > line && *(eol - 1) == nsCRT::CR) { + eol--; + *eol = '\0'; + cr = true; + } else { + *eol = '\0'; + cr = false; + } + + list_result result; + + int type = ParseFTPList(line, &state, &result); + + // if it is other than a directory, file, or link -OR- if it is a + // directory named . or .., skip over this line. + if ((type != 'd' && type != 'f' && type != 'l') || + (result.fe_type == 'd' && result.fe_fname[0] == '.' && + (result.fe_fnlen == 1 || + (result.fe_fnlen == 2 && result.fe_fname[1] == '.')))) { + if (cr) + line = eol + 2; + else + line = eol + 1; + + continue; + } + + // blast the index entry into the indexFormat buffer as a 201: line. + aString.AppendLiteral("201: "); + // FILENAME + + // parsers for styles 'U' and 'W' handle sequence " -> " themself + if (state.lstyle != 'U' && state.lstyle != 'W') { + const char* offset = strstr(result.fe_fname, " -> "); + if (offset) { + result.fe_fnlen = offset - result.fe_fname; + } + } + + nsAutoCString buf; + aString.Append('\"'); + aString.Append(NS_EscapeURL( + Substring(result.fe_fname, result.fe_fname + result.fe_fnlen), + esc_Minimal | esc_OnlyASCII | esc_Forced, buf)); + aString.AppendLiteral("\" "); + + // CONTENT LENGTH + + if (type != 'd') { + for (char& fe : result.fe_size) { + if (fe != '\0') aString.Append((const char*)&fe, 1); + } + + aString.Append(' '); + } else + aString.AppendLiteral("0 "); + + // MODIFIED DATE + char buffer[256] = ""; + + // ParseFTPList can return time structure with invalid values. + // PR_NormalizeTime will set all values into valid limits. + result.fe_time.tm_params.tp_gmt_offset = 0; + result.fe_time.tm_params.tp_dst_offset = 0; + PR_NormalizeTime(&result.fe_time, PR_GMTParameters); + + // Note: The below is the RFC822/1123 format, as required by + // the application/http-index-format specs + // viewers of such a format can then reformat this into the + // current locale (or anything else they choose) + PR_FormatTimeUSEnglish(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S GMT", + &result.fe_time); + + nsAutoCString escaped; + Unused << NS_WARN_IF( + !NS_Escape(nsDependentCString(buffer), escaped, url_Path)); + aString.Append(escaped); + aString.Append(' '); + + // ENTRY TYPE + if (type == 'd') + aString.AppendLiteral("DIRECTORY"); + else if (type == 'l') + aString.AppendLiteral("SYMBOLIC-LINK"); + else + aString.AppendLiteral("FILE"); + + aString.Append(' '); + + aString.Append(char(nsCRT::LF)); // complete this line + // END 201: + + if (cr) + line = eol + 2; + else + line = eol + 1; + } // end while(eol) + + return line; +} + +nsresult NS_NewFTPDirListingConv(nsFTPDirListingConv** aFTPDirListingConv) { + MOZ_ASSERT(aFTPDirListingConv != nullptr, "null ptr"); + if (!aFTPDirListingConv) return NS_ERROR_NULL_POINTER; + + RefPtr<nsFTPDirListingConv> conv = new nsFTPDirListingConv(); + conv.forget(aFTPDirListingConv); + return NS_OK; +} |