Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / base / src / nsDirectoryIndexStream.cpp
blob25f380175c6d8332f385e35759b8f1fabf62616b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set sw=4 sts=4 et cin: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Bradley Baetz <bbaetz@cs.mcgill.ca>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
43 The converts a filesystem directory into an "HTTP index" stream per
44 Lou Montulli's original spec:
46 http://www.mozilla.org/projects/netlib/dirindexformat.html
50 #include "nsEscape.h"
51 #include "nsDirectoryIndexStream.h"
52 #include "nsXPIDLString.h"
53 #include "prio.h"
54 #include "prlog.h"
55 #include "prlong.h"
56 #ifdef PR_LOGGING
57 static PRLogModuleInfo* gLog;
58 #endif
60 #include "nsISimpleEnumerator.h"
61 #include "nsICollation.h"
62 #include "nsILocale.h"
63 #include "nsILocaleService.h"
64 #include "nsCollationCID.h"
65 #include "nsIPlatformCharset.h"
66 #include "nsReadableUtils.h"
67 #include "nsURLHelper.h"
68 #include "nsNetUtil.h"
69 #include "nsCRT.h"
70 #include "nsNativeCharsetUtils.h"
72 // NOTE: This runs on the _file transport_ thread.
73 // The problem is that now that we're actually doing something with the data,
74 // we want to do stuff like i18n sorting. However, none of the collation stuff
75 // is threadsafe.
76 // So THIS CODE IS ASCII ONLY!!!!!!!! This is no worse than the current
77 // behaviour, though. See bug 99382.
78 // When this is fixed, #define THREADSAFE_I18N to get this code working
80 //#define THREADSAFE_I18N
82 nsDirectoryIndexStream::nsDirectoryIndexStream()
83 : mOffset(0), mStatus(NS_OK), mPos(0)
85 #ifdef PR_LOGGING
86 if (! gLog)
87 gLog = PR_NewLogModule("nsDirectoryIndexStream");
88 #endif
90 PR_LOG(gLog, PR_LOG_DEBUG,
91 ("nsDirectoryIndexStream[%p]: created", this));
94 static int compare(nsIFile* aElement1, nsIFile* aElement2, void* aData)
96 if (!NS_IsNativeUTF8()) {
97 // don't check for errors, because we can't report them anyway
98 nsAutoString name1, name2;
99 aElement1->GetLeafName(name1);
100 aElement2->GetLeafName(name2);
102 // Note - we should do the collation to do sorting. Why don't we?
103 // Because that is _slow_. Using TestProtocols to list file:///dev/
104 // goes from 3 seconds to 22. (This may be why nsXULSortService is
105 // so slow as well).
106 // Does this have bad effects? Probably, but since nsXULTree appears
107 // to use the raw RDF literal value as the sort key (which ammounts to an
108 // strcmp), it won't be any worse, I think.
109 // This could be made faster, by creating the keys once,
110 // but CompareString could still be smarter - see bug 99383 - bbaetz
111 // NB - 99393 has been WONTFIXed. So if the I18N code is ever made
112 // threadsafe so that this matters, we'd have to pass through a
113 // struct { nsIFile*, PRUint8* } with the pre-calculated key.
114 return Compare(name1, name2);
117 nsCAutoString name1, name2;
118 aElement1->GetNativeLeafName(name1);
119 aElement2->GetNativeLeafName(name2);
121 return Compare(name1, name2);
124 nsresult
125 nsDirectoryIndexStream::Init(nsIFile* aDir)
127 nsresult rv;
128 PRBool isDir;
129 rv = aDir->IsDirectory(&isDir);
130 if (NS_FAILED(rv)) return rv;
131 NS_PRECONDITION(isDir, "not a directory");
132 if (!isDir)
133 return NS_ERROR_ILLEGAL_VALUE;
135 #ifdef PR_LOGGING
136 if (PR_LOG_TEST(gLog, PR_LOG_DEBUG)) {
137 nsCAutoString path;
138 aDir->GetNativePath(path);
139 PR_LOG(gLog, PR_LOG_DEBUG,
140 ("nsDirectoryIndexStream[%p]: initialized on %s",
141 this, path.get()));
143 #endif
145 // Sigh. We have to allocate on the heap because there are no
146 // assignment operators defined.
147 nsCOMPtr<nsISimpleEnumerator> iter;
148 rv = aDir->GetDirectoryEntries(getter_AddRefs(iter));
149 if (NS_FAILED(rv)) return rv;
151 // Now lets sort, because clients expect it that way
152 // XXX - should we do so here, or when the first item is requested?
153 // XXX - use insertion sort instead?
155 PRBool more;
156 nsCOMPtr<nsISupports> elem;
157 while (NS_SUCCEEDED(iter->HasMoreElements(&more)) && more) {
158 rv = iter->GetNext(getter_AddRefs(elem));
159 if (NS_SUCCEEDED(rv)) {
160 nsCOMPtr<nsIFile> file = do_QueryInterface(elem);
161 if (file)
162 mArray.AppendObject(file); // addrefs
166 #ifdef THREADSAFE_I18N
167 nsCOMPtr<nsILocaleService> ls = do_GetService(NS_LOCALESERVICE_CONTRACTID,
168 &rv);
169 if (NS_FAILED(rv)) return rv;
171 nsCOMPtr<nsILocale> locale;
172 rv = ls->GetApplicationLocale(getter_AddRefs(locale));
173 if (NS_FAILED(rv)) return rv;
175 nsCOMPtr<nsICollationFactory> cf = do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID,
176 &rv);
177 if (NS_FAILED(rv)) return rv;
179 nsCOMPtr<nsICollation> coll;
180 rv = cf->CreateCollation(locale, getter_AddRefs(coll));
181 if (NS_FAILED(rv)) return rv;
183 mArray.Sort(compare, coll);
184 #else
185 mArray.Sort(compare, nsnull);
186 #endif
188 mBuf.AppendLiteral("300: ");
189 nsCAutoString url;
190 rv = net_GetURLSpecFromFile(aDir, url);
191 if (NS_FAILED(rv)) return rv;
192 mBuf.Append(url);
193 mBuf.Append('\n');
195 mBuf.AppendLiteral("200: filename content-length last-modified file-type\n");
197 return NS_OK;
200 nsDirectoryIndexStream::~nsDirectoryIndexStream()
202 PR_LOG(gLog, PR_LOG_DEBUG,
203 ("nsDirectoryIndexStream[%p]: destroyed", this));
206 nsresult
207 nsDirectoryIndexStream::Create(nsIFile* aDir, nsIInputStream** aResult)
209 nsDirectoryIndexStream* result = new nsDirectoryIndexStream();
210 if (! result)
211 return NS_ERROR_OUT_OF_MEMORY;
213 nsresult rv;
214 rv = result->Init(aDir);
215 if (NS_FAILED(rv)) {
216 delete result;
217 return rv;
220 *aResult = result;
221 NS_ADDREF(*aResult);
222 return NS_OK;
225 NS_IMPL_THREADSAFE_ISUPPORTS1(nsDirectoryIndexStream, nsIInputStream)
227 // The below routines are proxied to the UI thread!
228 NS_IMETHODIMP
229 nsDirectoryIndexStream::Close()
231 mStatus = NS_BASE_STREAM_CLOSED;
232 return NS_OK;
235 NS_IMETHODIMP
236 nsDirectoryIndexStream::Available(PRUint32* aLength)
238 if (NS_FAILED(mStatus))
239 return mStatus;
241 // If there's data in our buffer, use that
242 if (mOffset < (PRInt32)mBuf.Length()) {
243 *aLength = mBuf.Length() - mOffset;
244 return NS_OK;
247 // Returning one byte is not ideal, but good enough
248 *aLength = (mPos < mArray.Count()) ? 1 : 0;
249 return NS_OK;
252 NS_IMETHODIMP
253 nsDirectoryIndexStream::Read(char* aBuf, PRUint32 aCount, PRUint32* aReadCount)
255 if (mStatus == NS_BASE_STREAM_CLOSED) {
256 *aReadCount = 0;
257 return NS_OK;
259 if (NS_FAILED(mStatus))
260 return mStatus;
262 PRUint32 nread = 0;
264 // If anything is enqueued (or left-over) in mBuf, then feed it to
265 // the reader first.
266 while (mOffset < (PRInt32)mBuf.Length() && aCount != 0) {
267 *(aBuf++) = char(mBuf.CharAt(mOffset++));
268 --aCount;
269 ++nread;
272 // Room left?
273 if (aCount > 0) {
274 mOffset = 0;
275 mBuf.Truncate();
277 // Okay, now we'll suck stuff off of our iterator into the mBuf...
278 while (PRUint32(mBuf.Length()) < aCount) {
279 PRBool more = mPos < mArray.Count();
280 if (!more) break;
282 // don't addref, for speed - an addref happened when it
283 // was placed in the array, so it's not going to go stale
284 nsIFile* current = mArray.ObjectAt(mPos);
285 ++mPos;
287 #ifdef PR_LOGGING
288 if (PR_LOG_TEST(gLog, PR_LOG_DEBUG)) {
289 nsCAutoString path;
290 current->GetNativePath(path);
291 PR_LOG(gLog, PR_LOG_DEBUG,
292 ("nsDirectoryIndexStream[%p]: iterated %s",
293 this, path.get()));
295 #endif
297 // rjc: don't return hidden files/directories!
298 // bbaetz: why not?
299 nsresult rv;
300 #ifndef XP_UNIX
301 PRBool hidden = PR_FALSE;
302 current->IsHidden(&hidden);
303 if (hidden) {
304 PR_LOG(gLog, PR_LOG_DEBUG,
305 ("nsDirectoryIndexStream[%p]: skipping hidden file/directory",
306 this));
307 continue;
309 #endif
311 PRInt64 fileSize = 0;
312 current->GetFileSize( &fileSize );
314 PRInt64 fileInfoModifyTime = 0;
315 current->GetLastModifiedTime( &fileInfoModifyTime );
316 fileInfoModifyTime *= PR_USEC_PER_MSEC;
318 mBuf.AppendLiteral("201: ");
320 // The "filename" field
321 char* escaped = nsnull;
322 if (!NS_IsNativeUTF8()) {
323 nsAutoString leafname;
324 rv = current->GetLeafName(leafname);
325 if (NS_FAILED(rv)) return rv;
326 if (!leafname.IsEmpty())
327 escaped = nsEscape(NS_ConvertUTF16toUTF8(leafname).get(), url_Path);
328 } else {
329 nsCAutoString leafname;
330 rv = current->GetNativeLeafName(leafname);
331 if (NS_FAILED(rv)) return rv;
332 if (!leafname.IsEmpty())
333 escaped = nsEscape(leafname.get(), url_Path);
335 if (escaped) {
336 mBuf += escaped;
337 mBuf.Append(' ');
338 nsMemory::Free(escaped);
341 // The "content-length" field
342 mBuf.AppendInt(fileSize, 10);
343 mBuf.Append(' ');
345 // The "last-modified" field
346 PRExplodedTime tm;
347 PR_ExplodeTime(fileInfoModifyTime, PR_GMTParameters, &tm);
349 char buf[64];
350 PR_FormatTimeUSEnglish(buf, sizeof(buf), "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm);
351 mBuf.Append(buf);
354 // The "file-type" field
355 PRBool isFile = PR_TRUE;
356 current->IsFile(&isFile);
357 if (isFile) {
358 mBuf.AppendLiteral("FILE ");
360 else {
361 PRBool isDir;
362 rv = current->IsDirectory(&isDir);
363 if (NS_FAILED(rv)) return rv;
364 if (isDir) {
365 mBuf.AppendLiteral("DIRECTORY ");
367 else {
368 PRBool isLink;
369 rv = current->IsSymlink(&isLink);
370 if (NS_FAILED(rv)) return rv;
371 if (isLink) {
372 mBuf.AppendLiteral("SYMBOLIC-LINK ");
377 mBuf.Append('\n');
380 // ...and once we've either run out of directory entries, or
381 // filled up the buffer, then we'll push it to the reader.
382 while (mOffset < (PRInt32)mBuf.Length() && aCount != 0) {
383 *(aBuf++) = char(mBuf.CharAt(mOffset++));
384 --aCount;
385 ++nread;
389 *aReadCount = nread;
390 return NS_OK;
393 NS_IMETHODIMP
394 nsDirectoryIndexStream::ReadSegments(nsWriteSegmentFun writer, void * closure, PRUint32 count, PRUint32 *_retval)
396 return NS_ERROR_NOT_IMPLEMENTED;
399 NS_IMETHODIMP
400 nsDirectoryIndexStream::IsNonBlocking(PRBool *aNonBlocking)
402 *aNonBlocking = PR_FALSE;
403 return NS_OK;