1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/base/src/nsDirectoryIndexStream.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,368 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim:set sw=4 sts=4 et cin: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 + 1.11 +/* 1.12 + 1.13 + The converts a filesystem directory into an "HTTP index" stream per 1.14 + Lou Montulli's original spec: 1.15 + 1.16 + http://www.mozilla.org/projects/netlib/dirindexformat.html 1.17 + 1.18 + */ 1.19 + 1.20 +#include "nsEscape.h" 1.21 +#include "nsDirectoryIndexStream.h" 1.22 +#include "prlog.h" 1.23 +#include "prtime.h" 1.24 +#ifdef PR_LOGGING 1.25 +static PRLogModuleInfo* gLog; 1.26 +#endif 1.27 + 1.28 +#include "nsISimpleEnumerator.h" 1.29 +#ifdef THREADSAFE_I18N 1.30 +#include "nsCollationCID.h" 1.31 +#include "nsICollation.h" 1.32 +#include "nsILocale.h" 1.33 +#include "nsILocaleService.h" 1.34 +#endif 1.35 +#include "nsIFile.h" 1.36 +#include "nsURLHelper.h" 1.37 +#include "nsNativeCharsetUtils.h" 1.38 + 1.39 +// NOTE: This runs on the _file transport_ thread. 1.40 +// The problem is that now that we're actually doing something with the data, 1.41 +// we want to do stuff like i18n sorting. However, none of the collation stuff 1.42 +// is threadsafe. 1.43 +// So THIS CODE IS ASCII ONLY!!!!!!!! This is no worse than the current 1.44 +// behaviour, though. See bug 99382. 1.45 +// When this is fixed, #define THREADSAFE_I18N to get this code working 1.46 + 1.47 +//#define THREADSAFE_I18N 1.48 + 1.49 +nsDirectoryIndexStream::nsDirectoryIndexStream() 1.50 + : mOffset(0), mStatus(NS_OK), mPos(0) 1.51 +{ 1.52 +#ifdef PR_LOGGING 1.53 + if (! gLog) 1.54 + gLog = PR_NewLogModule("nsDirectoryIndexStream"); 1.55 +#endif 1.56 + 1.57 + PR_LOG(gLog, PR_LOG_DEBUG, 1.58 + ("nsDirectoryIndexStream[%p]: created", this)); 1.59 +} 1.60 + 1.61 +static int compare(nsIFile* aElement1, nsIFile* aElement2, void* aData) 1.62 +{ 1.63 + if (!NS_IsNativeUTF8()) { 1.64 + // don't check for errors, because we can't report them anyway 1.65 + nsAutoString name1, name2; 1.66 + aElement1->GetLeafName(name1); 1.67 + aElement2->GetLeafName(name2); 1.68 + 1.69 + // Note - we should do the collation to do sorting. Why don't we? 1.70 + // Because that is _slow_. Using TestProtocols to list file:///dev/ 1.71 + // goes from 3 seconds to 22. (This may be why nsXULSortService is 1.72 + // so slow as well). 1.73 + // Does this have bad effects? Probably, but since nsXULTree appears 1.74 + // to use the raw RDF literal value as the sort key (which ammounts to an 1.75 + // strcmp), it won't be any worse, I think. 1.76 + // This could be made faster, by creating the keys once, 1.77 + // but CompareString could still be smarter - see bug 99383 - bbaetz 1.78 + // NB - 99393 has been WONTFIXed. So if the I18N code is ever made 1.79 + // threadsafe so that this matters, we'd have to pass through a 1.80 + // struct { nsIFile*, uint8_t* } with the pre-calculated key. 1.81 + return Compare(name1, name2); 1.82 + } 1.83 + 1.84 + nsAutoCString name1, name2; 1.85 + aElement1->GetNativeLeafName(name1); 1.86 + aElement2->GetNativeLeafName(name2); 1.87 + 1.88 + return Compare(name1, name2); 1.89 +} 1.90 + 1.91 +nsresult 1.92 +nsDirectoryIndexStream::Init(nsIFile* aDir) 1.93 +{ 1.94 + nsresult rv; 1.95 + bool isDir; 1.96 + rv = aDir->IsDirectory(&isDir); 1.97 + if (NS_FAILED(rv)) return rv; 1.98 + NS_PRECONDITION(isDir, "not a directory"); 1.99 + if (!isDir) 1.100 + return NS_ERROR_ILLEGAL_VALUE; 1.101 + 1.102 +#ifdef PR_LOGGING 1.103 + if (PR_LOG_TEST(gLog, PR_LOG_DEBUG)) { 1.104 + nsAutoCString path; 1.105 + aDir->GetNativePath(path); 1.106 + PR_LOG(gLog, PR_LOG_DEBUG, 1.107 + ("nsDirectoryIndexStream[%p]: initialized on %s", 1.108 + this, path.get())); 1.109 + } 1.110 +#endif 1.111 + 1.112 + // Sigh. We have to allocate on the heap because there are no 1.113 + // assignment operators defined. 1.114 + nsCOMPtr<nsISimpleEnumerator> iter; 1.115 + rv = aDir->GetDirectoryEntries(getter_AddRefs(iter)); 1.116 + if (NS_FAILED(rv)) return rv; 1.117 + 1.118 + // Now lets sort, because clients expect it that way 1.119 + // XXX - should we do so here, or when the first item is requested? 1.120 + // XXX - use insertion sort instead? 1.121 + 1.122 + bool more; 1.123 + nsCOMPtr<nsISupports> elem; 1.124 + while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) { 1.125 + rv = iter->GetNext(getter_AddRefs(elem)); 1.126 + if (NS_SUCCEEDED(rv)) { 1.127 + nsCOMPtr<nsIFile> file = do_QueryInterface(elem); 1.128 + if (file) 1.129 + mArray.AppendObject(file); // addrefs 1.130 + } 1.131 + } 1.132 + 1.133 +#ifdef THREADSAFE_I18N 1.134 + nsCOMPtr<nsILocaleService> ls = do_GetService(NS_LOCALESERVICE_CONTRACTID, 1.135 + &rv); 1.136 + if (NS_FAILED(rv)) return rv; 1.137 + 1.138 + nsCOMPtr<nsILocale> locale; 1.139 + rv = ls->GetApplicationLocale(getter_AddRefs(locale)); 1.140 + if (NS_FAILED(rv)) return rv; 1.141 + 1.142 + nsCOMPtr<nsICollationFactory> cf = do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, 1.143 + &rv); 1.144 + if (NS_FAILED(rv)) return rv; 1.145 + 1.146 + nsCOMPtr<nsICollation> coll; 1.147 + rv = cf->CreateCollation(locale, getter_AddRefs(coll)); 1.148 + if (NS_FAILED(rv)) return rv; 1.149 + 1.150 + mArray.Sort(compare, coll); 1.151 +#else 1.152 + mArray.Sort(compare, nullptr); 1.153 +#endif 1.154 + 1.155 + mBuf.AppendLiteral("300: "); 1.156 + nsAutoCString url; 1.157 + rv = net_GetURLSpecFromFile(aDir, url); 1.158 + if (NS_FAILED(rv)) return rv; 1.159 + mBuf.Append(url); 1.160 + mBuf.Append('\n'); 1.161 + 1.162 + mBuf.AppendLiteral("200: filename content-length last-modified file-type\n"); 1.163 + 1.164 + return NS_OK; 1.165 +} 1.166 + 1.167 +nsDirectoryIndexStream::~nsDirectoryIndexStream() 1.168 +{ 1.169 + PR_LOG(gLog, PR_LOG_DEBUG, 1.170 + ("nsDirectoryIndexStream[%p]: destroyed", this)); 1.171 +} 1.172 + 1.173 +nsresult 1.174 +nsDirectoryIndexStream::Create(nsIFile* aDir, nsIInputStream** aResult) 1.175 +{ 1.176 + nsDirectoryIndexStream* result = new nsDirectoryIndexStream(); 1.177 + if (! result) 1.178 + return NS_ERROR_OUT_OF_MEMORY; 1.179 + 1.180 + nsresult rv; 1.181 + rv = result->Init(aDir); 1.182 + if (NS_FAILED(rv)) { 1.183 + delete result; 1.184 + return rv; 1.185 + } 1.186 + 1.187 + *aResult = result; 1.188 + NS_ADDREF(*aResult); 1.189 + return NS_OK; 1.190 +} 1.191 + 1.192 +NS_IMPL_ISUPPORTS(nsDirectoryIndexStream, nsIInputStream) 1.193 + 1.194 +// The below routines are proxied to the UI thread! 1.195 +NS_IMETHODIMP 1.196 +nsDirectoryIndexStream::Close() 1.197 +{ 1.198 + mStatus = NS_BASE_STREAM_CLOSED; 1.199 + return NS_OK; 1.200 +} 1.201 + 1.202 +NS_IMETHODIMP 1.203 +nsDirectoryIndexStream::Available(uint64_t* aLength) 1.204 +{ 1.205 + if (NS_FAILED(mStatus)) 1.206 + return mStatus; 1.207 + 1.208 + // If there's data in our buffer, use that 1.209 + if (mOffset < (int32_t)mBuf.Length()) { 1.210 + *aLength = mBuf.Length() - mOffset; 1.211 + return NS_OK; 1.212 + } 1.213 + 1.214 + // Returning one byte is not ideal, but good enough 1.215 + *aLength = (mPos < mArray.Count()) ? 1 : 0; 1.216 + return NS_OK; 1.217 +} 1.218 + 1.219 +NS_IMETHODIMP 1.220 +nsDirectoryIndexStream::Read(char* aBuf, uint32_t aCount, uint32_t* aReadCount) 1.221 +{ 1.222 + if (mStatus == NS_BASE_STREAM_CLOSED) { 1.223 + *aReadCount = 0; 1.224 + return NS_OK; 1.225 + } 1.226 + if (NS_FAILED(mStatus)) 1.227 + return mStatus; 1.228 + 1.229 + uint32_t nread = 0; 1.230 + 1.231 + // If anything is enqueued (or left-over) in mBuf, then feed it to 1.232 + // the reader first. 1.233 + while (mOffset < (int32_t)mBuf.Length() && aCount != 0) { 1.234 + *(aBuf++) = char(mBuf.CharAt(mOffset++)); 1.235 + --aCount; 1.236 + ++nread; 1.237 + } 1.238 + 1.239 + // Room left? 1.240 + if (aCount > 0) { 1.241 + mOffset = 0; 1.242 + mBuf.Truncate(); 1.243 + 1.244 + // Okay, now we'll suck stuff off of our iterator into the mBuf... 1.245 + while (uint32_t(mBuf.Length()) < aCount) { 1.246 + bool more = mPos < mArray.Count(); 1.247 + if (!more) break; 1.248 + 1.249 + // don't addref, for speed - an addref happened when it 1.250 + // was placed in the array, so it's not going to go stale 1.251 + nsIFile* current = mArray.ObjectAt(mPos); 1.252 + ++mPos; 1.253 + 1.254 +#ifdef PR_LOGGING 1.255 + if (PR_LOG_TEST(gLog, PR_LOG_DEBUG)) { 1.256 + nsAutoCString path; 1.257 + current->GetNativePath(path); 1.258 + PR_LOG(gLog, PR_LOG_DEBUG, 1.259 + ("nsDirectoryIndexStream[%p]: iterated %s", 1.260 + this, path.get())); 1.261 + } 1.262 +#endif 1.263 + 1.264 + // rjc: don't return hidden files/directories! 1.265 + // bbaetz: why not? 1.266 + nsresult rv; 1.267 +#ifndef XP_UNIX 1.268 + bool hidden = false; 1.269 + current->IsHidden(&hidden); 1.270 + if (hidden) { 1.271 + PR_LOG(gLog, PR_LOG_DEBUG, 1.272 + ("nsDirectoryIndexStream[%p]: skipping hidden file/directory", 1.273 + this)); 1.274 + continue; 1.275 + } 1.276 +#endif 1.277 + 1.278 + int64_t fileSize = 0; 1.279 + current->GetFileSize( &fileSize ); 1.280 + 1.281 + PRTime fileInfoModifyTime = 0; 1.282 + current->GetLastModifiedTime( &fileInfoModifyTime ); 1.283 + fileInfoModifyTime *= PR_USEC_PER_MSEC; 1.284 + 1.285 + mBuf.AppendLiteral("201: "); 1.286 + 1.287 + // The "filename" field 1.288 + char* escaped = nullptr; 1.289 + if (!NS_IsNativeUTF8()) { 1.290 + nsAutoString leafname; 1.291 + rv = current->GetLeafName(leafname); 1.292 + if (NS_FAILED(rv)) return rv; 1.293 + if (!leafname.IsEmpty()) 1.294 + escaped = nsEscape(NS_ConvertUTF16toUTF8(leafname).get(), url_Path); 1.295 + } else { 1.296 + nsAutoCString leafname; 1.297 + rv = current->GetNativeLeafName(leafname); 1.298 + if (NS_FAILED(rv)) return rv; 1.299 + if (!leafname.IsEmpty()) 1.300 + escaped = nsEscape(leafname.get(), url_Path); 1.301 + } 1.302 + if (escaped) { 1.303 + mBuf += escaped; 1.304 + mBuf.Append(' '); 1.305 + nsMemory::Free(escaped); 1.306 + } 1.307 + 1.308 + // The "content-length" field 1.309 + mBuf.AppendInt(fileSize, 10); 1.310 + mBuf.Append(' '); 1.311 + 1.312 + // The "last-modified" field 1.313 + PRExplodedTime tm; 1.314 + PR_ExplodeTime(fileInfoModifyTime, PR_GMTParameters, &tm); 1.315 + { 1.316 + char buf[64]; 1.317 + PR_FormatTimeUSEnglish(buf, sizeof(buf), "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm); 1.318 + mBuf.Append(buf); 1.319 + } 1.320 + 1.321 + // The "file-type" field 1.322 + bool isFile = true; 1.323 + current->IsFile(&isFile); 1.324 + if (isFile) { 1.325 + mBuf.AppendLiteral("FILE "); 1.326 + } 1.327 + else { 1.328 + bool isDir; 1.329 + rv = current->IsDirectory(&isDir); 1.330 + if (NS_FAILED(rv)) return rv; 1.331 + if (isDir) { 1.332 + mBuf.AppendLiteral("DIRECTORY "); 1.333 + } 1.334 + else { 1.335 + bool isLink; 1.336 + rv = current->IsSymlink(&isLink); 1.337 + if (NS_FAILED(rv)) return rv; 1.338 + if (isLink) { 1.339 + mBuf.AppendLiteral("SYMBOLIC-LINK "); 1.340 + } 1.341 + } 1.342 + } 1.343 + 1.344 + mBuf.Append('\n'); 1.345 + } 1.346 + 1.347 + // ...and once we've either run out of directory entries, or 1.348 + // filled up the buffer, then we'll push it to the reader. 1.349 + while (mOffset < (int32_t)mBuf.Length() && aCount != 0) { 1.350 + *(aBuf++) = char(mBuf.CharAt(mOffset++)); 1.351 + --aCount; 1.352 + ++nread; 1.353 + } 1.354 + } 1.355 + 1.356 + *aReadCount = nread; 1.357 + return NS_OK; 1.358 +} 1.359 + 1.360 +NS_IMETHODIMP 1.361 +nsDirectoryIndexStream::ReadSegments(nsWriteSegmentFun writer, void * closure, uint32_t count, uint32_t *_retval) 1.362 +{ 1.363 + return NS_ERROR_NOT_IMPLEMENTED; 1.364 +} 1.365 + 1.366 +NS_IMETHODIMP 1.367 +nsDirectoryIndexStream::IsNonBlocking(bool *aNonBlocking) 1.368 +{ 1.369 + *aNonBlocking = false; 1.370 + return NS_OK; 1.371 +}