Bug 1845134 - Part 4: Update existing ui-icons to use the latest source from acorn...
[gecko.git] / netwerk / base / nsDirectoryIndexStream.cpp
blobb9b840172e21137dceb982c9bdfac55170b7c3f1
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/. */
7 /*
9 The converts a filesystem directory into an "HTTP index" stream per
10 Lou Montulli's original spec:
12 http://www.mozilla.org/projects/netlib/dirindexformat.html
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"
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.
30 using namespace mozilla;
31 static LazyLogModule gLog("nsDirectoryIndexStream");
33 nsDirectoryIndexStream::nsDirectoryIndexStream() {
34 MOZ_LOG(gLog, LogLevel::Debug, ("nsDirectoryIndexStream[%p]: created", this));
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);
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);
59 nsAutoCString name1, name2;
60 aElement1->GetNativeLeafName(name1);
61 aElement2->GetNativeLeafName(name2);
63 return Compare(name1, name2);
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;
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()));
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;
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?
90 nsCOMPtr<nsIFile> file;
91 while (NS_SUCCEEDED(iter->GetNextFile(getter_AddRefs(file))) && file) {
92 mArray.AppendObject(file); // addrefs
95 mArray.Sort(compare);
97 mBuf.AppendLiteral("200: filename content-length last-modified file-type\n");
99 return NS_OK;
102 nsDirectoryIndexStream::~nsDirectoryIndexStream() {
103 MOZ_LOG(gLog, LogLevel::Debug,
104 ("nsDirectoryIndexStream[%p]: destroyed", this));
107 nsresult nsDirectoryIndexStream::Create(nsIFile* aDir,
108 nsIInputStream** aResult) {
109 RefPtr<nsDirectoryIndexStream> result = new nsDirectoryIndexStream();
110 if (!result) return NS_ERROR_OUT_OF_MEMORY;
112 nsresult rv = result->Init(aDir);
113 if (NS_FAILED(rv)) {
114 return rv;
117 result.forget(aResult);
118 return NS_OK;
121 NS_IMPL_ISUPPORTS(nsDirectoryIndexStream, nsIInputStream)
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;
130 NS_IMETHODIMP
131 nsDirectoryIndexStream::Available(uint64_t* aLength) {
132 if (NS_FAILED(mStatus)) return mStatus;
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;
140 // Returning one byte is not ideal, but good enough
141 *aLength = (mPos < mArray.Count()) ? 1 : 0;
142 return NS_OK;
145 NS_IMETHODIMP
146 nsDirectoryIndexStream::StreamStatus() { return mStatus; }
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;
155 if (NS_FAILED(mStatus)) return mStatus;
157 uint32_t nread = 0;
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;
167 // Room left?
168 if (aCount > 0) {
169 mOffset = 0;
170 mBuf.Truncate();
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;
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;
182 if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
183 MOZ_LOG(gLog, LogLevel::Debug,
184 ("nsDirectoryIndexStream[%p]: iterated %s", this,
185 current->HumanReadablePath().get()));
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;
200 #endif
202 int64_t fileSize = 0;
203 current->GetFileSize(&fileSize);
205 PRTime fileInfoModifyTime = 0;
206 current->GetLastModifiedTime(&fileInfoModifyTime);
207 fileInfoModifyTime *= PR_USEC_PER_MSEC;
209 mBuf.AppendLiteral("201: ");
211 // The "filename" field
212 if (!NS_IsNativeUTF8()) {
213 nsAutoString leafname;
214 rv = current->GetLeafName(leafname);
215 if (NS_FAILED(rv)) return rv;
217 nsAutoCString escaped;
218 if (!leafname.IsEmpty() &&
219 NS_Escape(NS_ConvertUTF16toUTF8(leafname), escaped, url_Path)) {
220 mBuf.Append(escaped);
221 mBuf.Append(' ');
223 } else {
224 nsAutoCString leafname;
225 rv = current->GetNativeLeafName(leafname);
226 if (NS_FAILED(rv)) return rv;
228 nsAutoCString escaped;
229 if (!leafname.IsEmpty() && NS_Escape(leafname, escaped, url_Path)) {
230 mBuf.Append(escaped);
231 mBuf.Append(' ');
235 // The "content-length" field
236 mBuf.AppendInt(fileSize, 10);
237 mBuf.Append(' ');
239 // The "last-modified" field
240 PRExplodedTime tm;
241 PR_ExplodeTime(fileInfoModifyTime, PR_GMTParameters, &tm);
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);
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 ");
270 mBuf.Append('\n');
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;
282 *aReadCount = nread;
283 return NS_OK;
286 NS_IMETHODIMP
287 nsDirectoryIndexStream::ReadSegments(nsWriteSegmentFun writer, void* closure,
288 uint32_t count, uint32_t* _retval) {
289 return NS_ERROR_NOT_IMPLEMENTED;
292 NS_IMETHODIMP
293 nsDirectoryIndexStream::IsNonBlocking(bool* aNonBlocking) {
294 *aNonBlocking = false;
295 return NS_OK;