tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsDirectoryIndexStream.cpp (8843B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set sw=2 sts=2 et cin: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 
      9  The converts a filesystem directory into an "HTTP index" stream per
     10  Lou Montulli's original spec:
     11 
     12  http://www.mozilla.org/projects/netlib/dirindexformat.html
     13 
     14 */
     15 
     16 #include "nsEscape.h"
     17 #include "nsDirectoryIndexStream.h"
     18 #include "mozilla/Logging.h"
     19 #include "prtime.h"
     20 #include "nsIFile.h"
     21 #include "nsNativeCharsetUtils.h"
     22 
     23 // NOTE: This runs on the _file transport_ thread.
     24 // The problem is that now that we're actually doing something with the data,
     25 // we want to do stuff like i18n sorting. However, none of the collation stuff
     26 // is threadsafe.
     27 // So THIS CODE IS ASCII ONLY!!!!!!!! This is no worse than the current
     28 // behaviour, though. See bug 99382.
     29 
     30 using namespace mozilla;
     31 static LazyLogModule gLog("nsDirectoryIndexStream");
     32 
     33 nsDirectoryIndexStream::nsDirectoryIndexStream() {
     34  MOZ_LOG(gLog, LogLevel::Debug, ("nsDirectoryIndexStream[%p]: created", this));
     35 }
     36 
     37 static int compare(nsIFile* aElement1, nsIFile* aElement2) {
     38  if (!NS_IsNativeUTF8()) {
     39    // don't check for errors, because we can't report them anyway
     40    nsAutoString name1, name2;
     41    aElement1->GetLeafName(name1);
     42    aElement2->GetLeafName(name2);
     43 
     44    // Note - we should do the collation to do sorting. Why don't we?
     45    // Because that is _slow_. Using TestProtocols to list file:///dev/
     46    // goes from 3 seconds to 22. (This may be why nsXULSortService is
     47    // so slow as well).
     48    // Does this have bad effects? Probably, but since nsXULTree appears
     49    // to use the raw RDF literal value as the sort key (which ammounts to an
     50    // strcmp), it won't be any worse, I think.
     51    // This could be made faster, by creating the keys once,
     52    // but CompareString could still be smarter - see bug 99383 - bbaetz
     53    // NB - 99393 has been WONTFIXed. So if the I18N code is ever made
     54    // threadsafe so that this matters, we'd have to pass through a
     55    // struct { nsIFile*, uint8_t* } with the pre-calculated key.
     56    return Compare(name1, name2);
     57  }
     58 
     59  nsAutoCString name1, name2;
     60  aElement1->GetNativeLeafName(name1);
     61  aElement2->GetNativeLeafName(name2);
     62 
     63  return Compare(name1, name2);
     64 }
     65 
     66 nsresult nsDirectoryIndexStream::Init(nsIFile* aDir) {
     67  nsresult rv;
     68  bool isDir;
     69  rv = aDir->IsDirectory(&isDir);
     70  if (NS_FAILED(rv)) return rv;
     71  MOZ_ASSERT(isDir, "not a directory");
     72  if (!isDir) return NS_ERROR_ILLEGAL_VALUE;
     73 
     74  if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
     75    MOZ_LOG(gLog, LogLevel::Debug,
     76            ("nsDirectoryIndexStream[%p]: initialized on %s", this,
     77             aDir->HumanReadablePath().get()));
     78  }
     79 
     80  // Sigh. We have to allocate on the heap because there are no
     81  // assignment operators defined.
     82  nsCOMPtr<nsIDirectoryEnumerator> iter;
     83  rv = aDir->GetDirectoryEntries(getter_AddRefs(iter));
     84  if (NS_FAILED(rv)) return rv;
     85 
     86  // Now lets sort, because clients expect it that way
     87  // XXX - should we do so here, or when the first item is requested?
     88  // XXX - use insertion sort instead?
     89 
     90  nsCOMPtr<nsIFile> file;
     91  while (NS_SUCCEEDED(iter->GetNextFile(getter_AddRefs(file))) && file) {
     92    mArray.AppendObject(file);  // addrefs
     93  }
     94 
     95  mArray.Sort(compare);
     96 
     97  mBuf.AppendLiteral("200: filename content-length last-modified file-type\n");
     98 
     99  return NS_OK;
    100 }
    101 
    102 nsDirectoryIndexStream::~nsDirectoryIndexStream() {
    103  MOZ_LOG(gLog, LogLevel::Debug,
    104          ("nsDirectoryIndexStream[%p]: destroyed", this));
    105 }
    106 
    107 nsresult nsDirectoryIndexStream::Create(nsIFile* aDir,
    108                                        nsIInputStream** aResult) {
    109  RefPtr<nsDirectoryIndexStream> result = new nsDirectoryIndexStream();
    110  if (!result) return NS_ERROR_OUT_OF_MEMORY;
    111 
    112  nsresult rv = result->Init(aDir);
    113  if (NS_FAILED(rv)) {
    114    return rv;
    115  }
    116 
    117  result.forget(aResult);
    118  return NS_OK;
    119 }
    120 
    121 NS_IMPL_ISUPPORTS(nsDirectoryIndexStream, nsIInputStream)
    122 
    123 // The below routines are proxied to the UI thread!
    124 NS_IMETHODIMP
    125 nsDirectoryIndexStream::Close() {
    126  mStatus = NS_BASE_STREAM_CLOSED;
    127  return NS_OK;
    128 }
    129 
    130 NS_IMETHODIMP
    131 nsDirectoryIndexStream::Available(uint64_t* aLength) {
    132  if (NS_FAILED(mStatus)) return mStatus;
    133 
    134  // If there's data in our buffer, use that
    135  if (mOffset < (int32_t)mBuf.Length()) {
    136    *aLength = mBuf.Length() - mOffset;
    137    return NS_OK;
    138  }
    139 
    140  // Returning one byte is not ideal, but good enough
    141  *aLength = (mPos < mArray.Count()) ? 1 : 0;
    142  return NS_OK;
    143 }
    144 
    145 NS_IMETHODIMP
    146 nsDirectoryIndexStream::StreamStatus() { return mStatus; }
    147 
    148 NS_IMETHODIMP
    149 nsDirectoryIndexStream::Read(char* aBuf, uint32_t aCount,
    150                             uint32_t* aReadCount) {
    151  if (mStatus == NS_BASE_STREAM_CLOSED) {
    152    *aReadCount = 0;
    153    return NS_OK;
    154  }
    155  if (NS_FAILED(mStatus)) return mStatus;
    156 
    157  uint32_t nread = 0;
    158 
    159  // If anything is enqueued (or left-over) in mBuf, then feed it to
    160  // the reader first.
    161  while (mOffset < (int32_t)mBuf.Length() && aCount != 0) {
    162    *(aBuf++) = char(mBuf.CharAt(mOffset++));
    163    --aCount;
    164    ++nread;
    165  }
    166 
    167  // Room left?
    168  if (aCount > 0) {
    169    mOffset = 0;
    170    mBuf.Truncate();
    171 
    172    // Okay, now we'll suck stuff off of our iterator into the mBuf...
    173    while (uint32_t(mBuf.Length()) < aCount) {
    174      bool more = mPos < mArray.Count();
    175      if (!more) break;
    176 
    177      // don't addref, for speed - an addref happened when it
    178      // was placed in the array, so it's not going to go stale
    179      nsIFile* current = mArray.ObjectAt(mPos);
    180      ++mPos;
    181 
    182      if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
    183        MOZ_LOG(gLog, LogLevel::Debug,
    184                ("nsDirectoryIndexStream[%p]: iterated %s", this,
    185                 current->HumanReadablePath().get()));
    186      }
    187 
    188      // rjc: don't return hidden files/directories!
    189      // bbaetz: why not?
    190      nsresult rv;
    191 #ifndef XP_UNIX
    192      bool hidden = false;
    193      current->IsHidden(&hidden);
    194      if (hidden) {
    195        MOZ_LOG(gLog, LogLevel::Debug,
    196                ("nsDirectoryIndexStream[%p]: skipping hidden file/directory",
    197                 this));
    198        continue;
    199      }
    200 #endif
    201 
    202      int64_t fileSize = 0;
    203      current->GetFileSize(&fileSize);
    204 
    205      PRTime fileInfoModifyTime = 0;
    206      current->GetLastModifiedTime(&fileInfoModifyTime);
    207      fileInfoModifyTime *= PR_USEC_PER_MSEC;
    208 
    209      mBuf.AppendLiteral("201: ");
    210 
    211      // The "filename" field
    212      if (!NS_IsNativeUTF8()) {
    213        nsAutoString leafname;
    214        rv = current->GetLeafName(leafname);
    215        if (NS_FAILED(rv)) return rv;
    216 
    217        nsAutoCString escaped;
    218        if (!leafname.IsEmpty() &&
    219            NS_Escape(NS_ConvertUTF16toUTF8(leafname), escaped, url_Path)) {
    220          mBuf.Append(escaped);
    221          mBuf.Append(' ');
    222        }
    223      } else {
    224        nsAutoCString leafname;
    225        rv = current->GetNativeLeafName(leafname);
    226        if (NS_FAILED(rv)) return rv;
    227 
    228        nsAutoCString escaped;
    229        if (!leafname.IsEmpty() && NS_Escape(leafname, escaped, url_Path)) {
    230          mBuf.Append(escaped);
    231          mBuf.Append(' ');
    232        }
    233      }
    234 
    235      // The "content-length" field
    236      mBuf.AppendInt(fileSize, 10);
    237      mBuf.Append(' ');
    238 
    239      // The "last-modified" field
    240      PRExplodedTime tm;
    241      PR_ExplodeTime(fileInfoModifyTime, PR_GMTParameters, &tm);
    242      {
    243        char buf[64];
    244        PR_FormatTimeUSEnglish(
    245            buf, sizeof(buf), "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm);
    246        mBuf.Append(buf);
    247      }
    248 
    249      // The "file-type" field
    250      bool isFile = true;
    251      current->IsFile(&isFile);
    252      if (isFile) {
    253        mBuf.AppendLiteral("FILE ");
    254      } else {
    255        bool isDir;
    256        rv = current->IsDirectory(&isDir);
    257        if (NS_FAILED(rv)) return rv;
    258        if (isDir) {
    259          mBuf.AppendLiteral("DIRECTORY ");
    260        } else {
    261          bool isLink;
    262          rv = current->IsSymlink(&isLink);
    263          if (NS_FAILED(rv)) return rv;
    264          if (isLink) {
    265            mBuf.AppendLiteral("SYMBOLIC-LINK ");
    266          }
    267        }
    268      }
    269 
    270      mBuf.Append('\n');
    271    }
    272 
    273    // ...and once we've either run out of directory entries, or
    274    // filled up the buffer, then we'll push it to the reader.
    275    while (mOffset < (int32_t)mBuf.Length() && aCount != 0) {
    276      *(aBuf++) = char(mBuf.CharAt(mOffset++));
    277      --aCount;
    278      ++nread;
    279    }
    280  }
    281 
    282  *aReadCount = nread;
    283  return NS_OK;
    284 }
    285 
    286 NS_IMETHODIMP
    287 nsDirectoryIndexStream::ReadSegments(nsWriteSegmentFun writer, void* closure,
    288                                     uint32_t count, uint32_t* _retval) {
    289  return NS_ERROR_NOT_IMPLEMENTED;
    290 }
    291 
    292 NS_IMETHODIMP
    293 nsDirectoryIndexStream::IsNonBlocking(bool* aNonBlocking) {
    294  *aNonBlocking = false;
    295  return NS_OK;
    296 }