netwerk/base/src/nsDirectoryIndexStream.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim:set sw=4 sts=4 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/. */
     8 /*
    10   The converts a filesystem directory into an "HTTP index" stream per
    11   Lou Montulli's original spec:
    13   http://www.mozilla.org/projects/netlib/dirindexformat.html
    15  */
    17 #include "nsEscape.h"
    18 #include "nsDirectoryIndexStream.h"
    19 #include "prlog.h"
    20 #include "prtime.h"
    21 #ifdef PR_LOGGING
    22 static PRLogModuleInfo* gLog;
    23 #endif
    25 #include "nsISimpleEnumerator.h"
    26 #ifdef THREADSAFE_I18N
    27 #include "nsCollationCID.h"
    28 #include "nsICollation.h"
    29 #include "nsILocale.h"
    30 #include "nsILocaleService.h"
    31 #endif
    32 #include "nsIFile.h"
    33 #include "nsURLHelper.h"
    34 #include "nsNativeCharsetUtils.h"
    36 // NOTE: This runs on the _file transport_ thread.
    37 // The problem is that now that we're actually doing something with the data,
    38 // we want to do stuff like i18n sorting. However, none of the collation stuff
    39 // is threadsafe.
    40 // So THIS CODE IS ASCII ONLY!!!!!!!! This is no worse than the current
    41 // behaviour, though. See bug 99382.
    42 // When this is fixed, #define THREADSAFE_I18N to get this code working
    44 //#define THREADSAFE_I18N
    46 nsDirectoryIndexStream::nsDirectoryIndexStream()
    47     : mOffset(0), mStatus(NS_OK), mPos(0)
    48 {
    49 #ifdef PR_LOGGING
    50     if (! gLog)
    51         gLog = PR_NewLogModule("nsDirectoryIndexStream");
    52 #endif
    54     PR_LOG(gLog, PR_LOG_DEBUG,
    55            ("nsDirectoryIndexStream[%p]: created", this));
    56 }
    58 static int compare(nsIFile* aElement1, nsIFile* aElement2, void* aData)
    59 {
    60     if (!NS_IsNativeUTF8()) {
    61         // don't check for errors, because we can't report them anyway
    62         nsAutoString name1, name2;
    63         aElement1->GetLeafName(name1);
    64         aElement2->GetLeafName(name2);
    66         // Note - we should do the collation to do sorting. Why don't we?
    67         // Because that is _slow_. Using TestProtocols to list file:///dev/
    68         // goes from 3 seconds to 22. (This may be why nsXULSortService is
    69         // so slow as well).
    70         // Does this have bad effects? Probably, but since nsXULTree appears
    71         // to use the raw RDF literal value as the sort key (which ammounts to an
    72         // strcmp), it won't be any worse, I think.
    73         // This could be made faster, by creating the keys once,
    74         // but CompareString could still be smarter - see bug 99383 - bbaetz
    75         // NB - 99393 has been WONTFIXed. So if the I18N code is ever made
    76         // threadsafe so that this matters, we'd have to pass through a
    77         // struct { nsIFile*, uint8_t* } with the pre-calculated key.
    78         return Compare(name1, name2);
    79     }
    81     nsAutoCString name1, name2;
    82     aElement1->GetNativeLeafName(name1);
    83     aElement2->GetNativeLeafName(name2);
    85     return Compare(name1, name2);
    86 }
    88 nsresult
    89 nsDirectoryIndexStream::Init(nsIFile* aDir)
    90 {
    91     nsresult rv;
    92     bool isDir;
    93     rv = aDir->IsDirectory(&isDir);
    94     if (NS_FAILED(rv)) return rv;
    95     NS_PRECONDITION(isDir, "not a directory");
    96     if (!isDir)
    97         return NS_ERROR_ILLEGAL_VALUE;
    99 #ifdef PR_LOGGING
   100     if (PR_LOG_TEST(gLog, PR_LOG_DEBUG)) {
   101         nsAutoCString path;
   102         aDir->GetNativePath(path);
   103         PR_LOG(gLog, PR_LOG_DEBUG,
   104                ("nsDirectoryIndexStream[%p]: initialized on %s",
   105                 this, path.get()));
   106     }
   107 #endif
   109     // Sigh. We have to allocate on the heap because there are no
   110     // assignment operators defined.
   111     nsCOMPtr<nsISimpleEnumerator> iter;
   112     rv = aDir->GetDirectoryEntries(getter_AddRefs(iter));
   113     if (NS_FAILED(rv)) return rv;
   115     // Now lets sort, because clients expect it that way
   116     // XXX - should we do so here, or when the first item is requested?
   117     // XXX - use insertion sort instead?
   119     bool more;
   120     nsCOMPtr<nsISupports> elem;
   121     while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
   122         rv = iter->GetNext(getter_AddRefs(elem));
   123         if (NS_SUCCEEDED(rv)) {
   124             nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
   125             if (file)
   126                 mArray.AppendObject(file); // addrefs
   127         }
   128     }
   130 #ifdef THREADSAFE_I18N
   131     nsCOMPtr<nsILocaleService> ls = do_GetService(NS_LOCALESERVICE_CONTRACTID,
   132                                                   &rv);
   133     if (NS_FAILED(rv)) return rv;
   135     nsCOMPtr<nsILocale> locale;
   136     rv = ls->GetApplicationLocale(getter_AddRefs(locale));
   137     if (NS_FAILED(rv)) return rv;
   139     nsCOMPtr<nsICollationFactory> cf = do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID,
   140                                                          &rv);
   141     if (NS_FAILED(rv)) return rv;
   143     nsCOMPtr<nsICollation> coll;
   144     rv = cf->CreateCollation(locale, getter_AddRefs(coll));
   145     if (NS_FAILED(rv)) return rv;
   147     mArray.Sort(compare, coll);
   148 #else
   149     mArray.Sort(compare, nullptr);
   150 #endif
   152     mBuf.AppendLiteral("300: ");
   153     nsAutoCString url;
   154     rv = net_GetURLSpecFromFile(aDir, url);
   155     if (NS_FAILED(rv)) return rv;
   156     mBuf.Append(url);
   157     mBuf.Append('\n');
   159     mBuf.AppendLiteral("200: filename content-length last-modified file-type\n");
   161     return NS_OK;
   162 }
   164 nsDirectoryIndexStream::~nsDirectoryIndexStream()
   165 {
   166     PR_LOG(gLog, PR_LOG_DEBUG,
   167            ("nsDirectoryIndexStream[%p]: destroyed", this));
   168 }
   170 nsresult
   171 nsDirectoryIndexStream::Create(nsIFile* aDir, nsIInputStream** aResult)
   172 {
   173     nsDirectoryIndexStream* result = new nsDirectoryIndexStream();
   174     if (! result)
   175         return NS_ERROR_OUT_OF_MEMORY;
   177     nsresult rv;
   178     rv = result->Init(aDir);
   179     if (NS_FAILED(rv)) {
   180         delete result;
   181         return rv;
   182     }
   184     *aResult = result;
   185     NS_ADDREF(*aResult);
   186     return NS_OK;
   187 }
   189 NS_IMPL_ISUPPORTS(nsDirectoryIndexStream, nsIInputStream)
   191 // The below routines are proxied to the UI thread!
   192 NS_IMETHODIMP
   193 nsDirectoryIndexStream::Close()
   194 {
   195     mStatus = NS_BASE_STREAM_CLOSED;
   196     return NS_OK;
   197 }
   199 NS_IMETHODIMP
   200 nsDirectoryIndexStream::Available(uint64_t* aLength)
   201 {
   202     if (NS_FAILED(mStatus))
   203         return mStatus;
   205     // If there's data in our buffer, use that
   206     if (mOffset < (int32_t)mBuf.Length()) {
   207         *aLength = mBuf.Length() - mOffset;
   208         return NS_OK;
   209     }
   211     // Returning one byte is not ideal, but good enough
   212     *aLength = (mPos < mArray.Count()) ? 1 : 0;
   213     return NS_OK;
   214 }
   216 NS_IMETHODIMP
   217 nsDirectoryIndexStream::Read(char* aBuf, uint32_t aCount, uint32_t* aReadCount)
   218 {
   219     if (mStatus == NS_BASE_STREAM_CLOSED) {
   220         *aReadCount = 0;
   221         return NS_OK;
   222     }
   223     if (NS_FAILED(mStatus))
   224         return mStatus;
   226     uint32_t nread = 0;
   228     // If anything is enqueued (or left-over) in mBuf, then feed it to
   229     // the reader first.
   230     while (mOffset < (int32_t)mBuf.Length() && aCount != 0) {
   231         *(aBuf++) = char(mBuf.CharAt(mOffset++));
   232         --aCount;
   233         ++nread;
   234     }
   236     // Room left?
   237     if (aCount > 0) {
   238         mOffset = 0;
   239         mBuf.Truncate();
   241         // Okay, now we'll suck stuff off of our iterator into the mBuf...
   242         while (uint32_t(mBuf.Length()) < aCount) {
   243             bool more = mPos < mArray.Count();
   244             if (!more) break;
   246             // don't addref, for speed - an addref happened when it
   247             // was placed in the array, so it's not going to go stale
   248             nsIFile* current = mArray.ObjectAt(mPos);
   249             ++mPos;
   251 #ifdef PR_LOGGING
   252             if (PR_LOG_TEST(gLog, PR_LOG_DEBUG)) {
   253                 nsAutoCString path;
   254                 current->GetNativePath(path);
   255                 PR_LOG(gLog, PR_LOG_DEBUG,
   256                        ("nsDirectoryIndexStream[%p]: iterated %s",
   257                         this, path.get()));
   258             }
   259 #endif
   261             // rjc: don't return hidden files/directories!
   262             // bbaetz: why not?
   263             nsresult rv;
   264 #ifndef XP_UNIX
   265             bool hidden = false;
   266             current->IsHidden(&hidden);
   267             if (hidden) {
   268                 PR_LOG(gLog, PR_LOG_DEBUG,
   269                        ("nsDirectoryIndexStream[%p]: skipping hidden file/directory",
   270                         this));
   271                 continue;
   272             }
   273 #endif
   275             int64_t fileSize = 0;
   276             current->GetFileSize( &fileSize );
   278             PRTime fileInfoModifyTime = 0;
   279             current->GetLastModifiedTime( &fileInfoModifyTime );
   280             fileInfoModifyTime *= PR_USEC_PER_MSEC;
   282             mBuf.AppendLiteral("201: ");
   284             // The "filename" field
   285             char* escaped = nullptr;
   286             if (!NS_IsNativeUTF8()) {
   287                 nsAutoString leafname;
   288                 rv = current->GetLeafName(leafname);
   289                 if (NS_FAILED(rv)) return rv;
   290                 if (!leafname.IsEmpty())
   291                     escaped = nsEscape(NS_ConvertUTF16toUTF8(leafname).get(), url_Path);
   292             } else {
   293                 nsAutoCString leafname;
   294                 rv = current->GetNativeLeafName(leafname);
   295                 if (NS_FAILED(rv)) return rv;
   296                 if (!leafname.IsEmpty())
   297                     escaped = nsEscape(leafname.get(), url_Path);
   298             }
   299             if (escaped) {
   300                 mBuf += escaped;
   301                 mBuf.Append(' ');
   302                 nsMemory::Free(escaped);
   303             }
   305             // The "content-length" field
   306             mBuf.AppendInt(fileSize, 10);
   307             mBuf.Append(' ');
   309             // The "last-modified" field
   310             PRExplodedTime tm;
   311             PR_ExplodeTime(fileInfoModifyTime, PR_GMTParameters, &tm);
   312             {
   313                 char buf[64];
   314                 PR_FormatTimeUSEnglish(buf, sizeof(buf), "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm);
   315                 mBuf.Append(buf);
   316             }
   318             // The "file-type" field
   319             bool isFile = true;
   320             current->IsFile(&isFile);
   321             if (isFile) {
   322                 mBuf.AppendLiteral("FILE ");
   323             }
   324             else {
   325                 bool isDir;
   326                 rv = current->IsDirectory(&isDir);
   327                 if (NS_FAILED(rv)) return rv; 
   328                 if (isDir) {
   329                     mBuf.AppendLiteral("DIRECTORY ");
   330                 }
   331                 else {
   332                     bool isLink;
   333                     rv = current->IsSymlink(&isLink);
   334                     if (NS_FAILED(rv)) return rv; 
   335                     if (isLink) {
   336                         mBuf.AppendLiteral("SYMBOLIC-LINK ");
   337                     }
   338                 }
   339             }
   341             mBuf.Append('\n');
   342         }
   344         // ...and once we've either run out of directory entries, or
   345         // filled up the buffer, then we'll push it to the reader.
   346         while (mOffset < (int32_t)mBuf.Length() && aCount != 0) {
   347             *(aBuf++) = char(mBuf.CharAt(mOffset++));
   348             --aCount;
   349             ++nread;
   350         }
   351     }
   353     *aReadCount = nread;
   354     return NS_OK;
   355 }
   357 NS_IMETHODIMP
   358 nsDirectoryIndexStream::ReadSegments(nsWriteSegmentFun writer, void * closure, uint32_t count, uint32_t *_retval)
   359 {
   360     return NS_ERROR_NOT_IMPLEMENTED;
   361 }
   363 NS_IMETHODIMP
   364 nsDirectoryIndexStream::IsNonBlocking(bool *aNonBlocking)
   365 {
   366     *aNonBlocking = false;
   367     return NS_OK;
   368 }

mercurial