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.

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

mercurial