1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /* This parsing code originally lived in xpfe/components/directory/ - bbaetz */
8 #include "nsDirIndexParser.h"
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/ClearOnShutdown.h"
12 #include "mozilla/Encoding.h"
13 #include "mozilla/StaticPtr.h"
16 #include "nsDirIndex.h"
18 #include "nsIDirIndex.h"
19 #include "nsIInputStream.h"
21 using namespace mozilla
;
23 NS_IMPL_ISUPPORTS(nsDirIndexParser
, nsIRequestObserver
, nsIStreamListener
,
26 void nsDirIndexParser::Init() {
32 nsDirIndexParser::SetListener(nsIDirIndexListener
* aListener
) {
33 mListener
= aListener
;
38 nsDirIndexParser::GetListener(nsIDirIndexListener
** aListener
) {
39 *aListener
= do_AddRef(mListener
).take();
44 nsDirIndexParser::OnStartRequest(nsIRequest
* aRequest
) { return NS_OK
; }
47 nsDirIndexParser::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatusCode
) {
49 if (mBuf
.Length() > (uint32_t)mLineStart
) {
50 ProcessData(aRequest
);
56 nsDirIndexParser::Field
nsDirIndexParser::gFieldTable
[] = {
57 {"Filename", FIELD_FILENAME
},
58 {"Content-Length", FIELD_CONTENTLENGTH
},
59 {"Last-Modified", FIELD_LASTMODIFIED
},
60 {"File-Type", FIELD_FILETYPE
},
61 {nullptr, FIELD_UNKNOWN
}};
63 void nsDirIndexParser::ParseFormat(const char* aFormatStr
) {
64 // Parse a "200" format line, and remember the fields and their
65 // ordering in mFormat. Multiple 200 lines stomp on each other.
66 unsigned int formatNum
= 0;
70 while (*aFormatStr
&& nsCRT::IsAsciiSpace(char16_t(*aFormatStr
))) {
74 if (!*aFormatStr
) break;
78 while (aFormatStr
[len
] && !nsCRT::IsAsciiSpace(char16_t(aFormatStr
[len
]))) {
81 name
.Append(aFormatStr
, len
);
84 // Okay, we're gonna monkey with the nsStr. Bold!
85 name
.SetLength(nsUnescapeCount(name
.BeginWriting()));
87 for (Field
* i
= gFieldTable
; i
->mName
; ++i
) {
88 if (name
.EqualsIgnoreCase(i
->mName
)) {
89 mFormat
[formatNum
] = i
->mType
;
90 mFormat
[++formatNum
] = -1;
95 } while (*aFormatStr
&& (formatNum
< (ArrayLength(mFormat
) - 1)));
98 void nsDirIndexParser::ParseData(nsIDirIndex
* aIdx
, char* aDataStr
,
100 // Parse a "201" data line, using the field ordering specified in
103 if (mFormat
[0] == -1) {
104 // Ignore if we haven't seen a format yet.
108 nsAutoCString filename
;
109 int32_t lineLen
= aLineLen
;
111 for (int32_t i
= 0; mFormat
[i
] != -1; ++i
) {
112 // If we've exhausted the data before we run out of fields, just bail.
113 if (!*aDataStr
|| (lineLen
< 1)) {
117 while ((lineLen
> 0) && nsCRT::IsAsciiSpace(*aDataStr
)) {
123 // invalid format, bail
127 char* value
= aDataStr
;
128 if (*aDataStr
== '"' || *aDataStr
== '\'') {
129 // it's a quoted string. snarf everything up to the next quote character
130 const char quotechar
= *(aDataStr
++);
133 while ((lineLen
> 0) && *aDataStr
!= quotechar
) {
143 // invalid format, bail
147 // it's unquoted. snarf until we see whitespace.
149 while ((lineLen
> 0) && (!nsCRT::IsAsciiSpace(*aDataStr
))) {
157 // even if we ran out of line length here, there's still a trailing zero
161 fieldType t
= fieldType(mFormat
[i
]);
163 case FIELD_FILENAME
: {
164 // don't unescape at this point, so that UnEscapeAndConvert() can
166 aIdx
->SetLocation(filename
);
168 case FIELD_CONTENTLENGTH
: {
170 int32_t status
= PR_sscanf(value
, "%lld", &len
);
174 aIdx
->SetSize(UINT64_MAX
); // UINT64_MAX means unknown
177 case FIELD_LASTMODIFIED
: {
180 if (PR_ParseTimeString(value
, false, &tm
) == PR_SUCCESS
) {
181 aIdx
->SetLastModified(tm
);
187 if (!nsCRT::strcasecmp(value
, "directory")) {
188 aIdx
->SetType(nsIDirIndex::TYPE_DIRECTORY
);
189 } else if (!nsCRT::strcasecmp(value
, "file")) {
190 aIdx
->SetType(nsIDirIndex::TYPE_FILE
);
191 } else if (!nsCRT::strcasecmp(value
, "symbolic-link")) {
192 aIdx
->SetType(nsIDirIndex::TYPE_SYMLINK
);
194 aIdx
->SetType(nsIDirIndex::TYPE_UNKNOWN
);
205 nsDirIndexParser::OnDataAvailable(nsIRequest
* aRequest
, nsIInputStream
* aStream
,
206 uint64_t aSourceOffset
, uint32_t aCount
) {
207 if (aCount
< 1) return NS_OK
;
209 uint32_t len
= mBuf
.Length();
211 // Ensure that our mBuf has capacity to hold the data we're about to
213 // Before adjusting the capacity, guard against any potential overflow
214 // resulting from the addition of aCount with len. See Bug 1823551.
215 NS_ENSURE_TRUE((UINT32_MAX
- aCount
) >= len
, NS_ERROR_FAILURE
);
216 if (!mBuf
.SetLength(len
+ aCount
, fallible
)) return NS_ERROR_OUT_OF_MEMORY
;
218 // Now read the data into our buffer.
221 rv
= aStream
->Read(mBuf
.BeginWriting() + len
, aCount
, &count
);
222 if (NS_FAILED(rv
)) return rv
;
224 // Set the string's length according to the amount of data we've read.
225 // Note: we know this to work on nsCString. This isn't guaranteed to
226 // work on other strings.
227 mBuf
.SetLength(len
+ count
);
229 return ProcessData(aRequest
);
232 nsresult
nsDirIndexParser::ProcessData(nsIRequest
* aRequest
) {
233 if (!mListener
) return NS_ERROR_FAILURE
;
236 int32_t eol
= mBuf
.FindCharInSet("\n\r", mLineStart
);
238 mBuf
.SetCharAt(char16_t('\0'), eol
);
240 const char* line
= mBuf
.get() + mLineStart
;
242 int32_t lineLen
= eol
- mLineStart
;
243 mLineStart
= eol
+ 1;
246 const char* buf
= line
;
250 if (buf
[2] == '0' && buf
[3] == ':') {
251 // 200. Define field names
252 ParseFormat(buf
+ 4);
253 } else if (buf
[2] == '1' && buf
[3] == ':') {
255 nsCOMPtr
<nsIDirIndex
> idx
= new nsDirIndex();
257 ParseData(idx
, ((char*)buf
) + 4, lineLen
- 4);
258 mListener
->OnIndexAvailable(aRequest
, idx
);