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.

     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 }

mercurial