netwerk/streamconv/converters/nsDirIndexParser.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: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /* This parsing code originally lived in xpfe/components/directory/ - bbaetz */
michael@0 7
michael@0 8 #include "mozilla/ArrayUtils.h"
michael@0 9
michael@0 10 #include "prprf.h"
michael@0 11
michael@0 12 #include "nsDirIndexParser.h"
michael@0 13 #include "nsEscape.h"
michael@0 14 #include "nsIInputStream.h"
michael@0 15 #include "nsCRT.h"
michael@0 16 #include "mozilla/dom/FallbackEncoding.h"
michael@0 17 #include "nsITextToSubURI.h"
michael@0 18 #include "nsIDirIndex.h"
michael@0 19 #include "nsServiceManagerUtils.h"
michael@0 20
michael@0 21 using namespace mozilla;
michael@0 22
michael@0 23 NS_IMPL_ISUPPORTS(nsDirIndexParser,
michael@0 24 nsIRequestObserver,
michael@0 25 nsIStreamListener,
michael@0 26 nsIDirIndexParser)
michael@0 27
michael@0 28 nsDirIndexParser::nsDirIndexParser() {
michael@0 29 }
michael@0 30
michael@0 31 nsresult
michael@0 32 nsDirIndexParser::Init() {
michael@0 33 mLineStart = 0;
michael@0 34 mHasDescription = false;
michael@0 35 mFormat = nullptr;
michael@0 36 mozilla::dom::FallbackEncoding::FromLocale(mEncoding);
michael@0 37
michael@0 38 nsresult rv;
michael@0 39 // XXX not threadsafe
michael@0 40 if (gRefCntParser++ == 0)
michael@0 41 rv = CallGetService(NS_ITEXTTOSUBURI_CONTRACTID, &gTextToSubURI);
michael@0 42 else
michael@0 43 rv = NS_OK;
michael@0 44
michael@0 45 return rv;
michael@0 46 }
michael@0 47
michael@0 48 nsDirIndexParser::~nsDirIndexParser() {
michael@0 49 delete[] mFormat;
michael@0 50 // XXX not threadsafe
michael@0 51 if (--gRefCntParser == 0) {
michael@0 52 NS_IF_RELEASE(gTextToSubURI);
michael@0 53 }
michael@0 54 }
michael@0 55
michael@0 56 NS_IMETHODIMP
michael@0 57 nsDirIndexParser::SetListener(nsIDirIndexListener* aListener) {
michael@0 58 mListener = aListener;
michael@0 59 return NS_OK;
michael@0 60 }
michael@0 61
michael@0 62 NS_IMETHODIMP
michael@0 63 nsDirIndexParser::GetListener(nsIDirIndexListener** aListener) {
michael@0 64 NS_IF_ADDREF(*aListener = mListener.get());
michael@0 65 return NS_OK;
michael@0 66 }
michael@0 67
michael@0 68 NS_IMETHODIMP
michael@0 69 nsDirIndexParser::GetComment(char** aComment) {
michael@0 70 *aComment = ToNewCString(mComment);
michael@0 71
michael@0 72 if (!*aComment)
michael@0 73 return NS_ERROR_OUT_OF_MEMORY;
michael@0 74
michael@0 75 return NS_OK;
michael@0 76 }
michael@0 77
michael@0 78 NS_IMETHODIMP
michael@0 79 nsDirIndexParser::SetEncoding(const char* aEncoding) {
michael@0 80 mEncoding.Assign(aEncoding);
michael@0 81 return NS_OK;
michael@0 82 }
michael@0 83
michael@0 84 NS_IMETHODIMP
michael@0 85 nsDirIndexParser::GetEncoding(char** aEncoding) {
michael@0 86 *aEncoding = ToNewCString(mEncoding);
michael@0 87
michael@0 88 if (!*aEncoding)
michael@0 89 return NS_ERROR_OUT_OF_MEMORY;
michael@0 90
michael@0 91 return NS_OK;
michael@0 92 }
michael@0 93
michael@0 94 NS_IMETHODIMP
michael@0 95 nsDirIndexParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt) {
michael@0 96 return NS_OK;
michael@0 97 }
michael@0 98
michael@0 99 NS_IMETHODIMP
michael@0 100 nsDirIndexParser::OnStopRequest(nsIRequest *aRequest, nsISupports *aCtxt,
michael@0 101 nsresult aStatusCode) {
michael@0 102 // Finish up
michael@0 103 if (mBuf.Length() > (uint32_t) mLineStart) {
michael@0 104 ProcessData(aRequest, aCtxt);
michael@0 105 }
michael@0 106
michael@0 107 return NS_OK;
michael@0 108 }
michael@0 109
michael@0 110 nsDirIndexParser::Field
michael@0 111 nsDirIndexParser::gFieldTable[] = {
michael@0 112 { "Filename", FIELD_FILENAME },
michael@0 113 { "Description", FIELD_DESCRIPTION },
michael@0 114 { "Content-Length", FIELD_CONTENTLENGTH },
michael@0 115 { "Last-Modified", FIELD_LASTMODIFIED },
michael@0 116 { "Content-Type", FIELD_CONTENTTYPE },
michael@0 117 { "File-Type", FIELD_FILETYPE },
michael@0 118 { nullptr, FIELD_UNKNOWN }
michael@0 119 };
michael@0 120
michael@0 121 nsrefcnt nsDirIndexParser::gRefCntParser = 0;
michael@0 122 nsITextToSubURI *nsDirIndexParser::gTextToSubURI;
michael@0 123
michael@0 124 nsresult
michael@0 125 nsDirIndexParser::ParseFormat(const char* aFormatStr) {
michael@0 126 // Parse a "200" format line, and remember the fields and their
michael@0 127 // ordering in mFormat. Multiple 200 lines stomp on each other.
michael@0 128
michael@0 129 // Lets find out how many elements we have.
michael@0 130 // easier to do this then realloc
michael@0 131 const char* pos = aFormatStr;
michael@0 132 unsigned int num = 0;
michael@0 133 do {
michael@0 134 while (*pos && nsCRT::IsAsciiSpace(char16_t(*pos)))
michael@0 135 ++pos;
michael@0 136
michael@0 137 ++num;
michael@0 138 // There are a maximum of six allowed header fields (doubled plus
michael@0 139 // terminator, just in case) -- Bug 443299
michael@0 140 if (num > (2 * ArrayLength(gFieldTable)))
michael@0 141 return NS_ERROR_UNEXPECTED;
michael@0 142
michael@0 143 if (! *pos)
michael@0 144 break;
michael@0 145
michael@0 146 while (*pos && !nsCRT::IsAsciiSpace(char16_t(*pos)))
michael@0 147 ++pos;
michael@0 148
michael@0 149 } while (*pos);
michael@0 150
michael@0 151 delete[] mFormat;
michael@0 152 mFormat = new int[num+1];
michael@0 153 // Prevent nullptr Deref - Bug 443299
michael@0 154 if (mFormat == nullptr)
michael@0 155 return NS_ERROR_OUT_OF_MEMORY;
michael@0 156 mFormat[num] = -1;
michael@0 157
michael@0 158 int formatNum=0;
michael@0 159 do {
michael@0 160 while (*aFormatStr && nsCRT::IsAsciiSpace(char16_t(*aFormatStr)))
michael@0 161 ++aFormatStr;
michael@0 162
michael@0 163 if (! *aFormatStr)
michael@0 164 break;
michael@0 165
michael@0 166 nsAutoCString name;
michael@0 167 int32_t len = 0;
michael@0 168 while (aFormatStr[len] && !nsCRT::IsAsciiSpace(char16_t(aFormatStr[len])))
michael@0 169 ++len;
michael@0 170 name.SetCapacity(len + 1);
michael@0 171 name.Append(aFormatStr, len);
michael@0 172 aFormatStr += len;
michael@0 173
michael@0 174 // Okay, we're gonna monkey with the nsStr. Bold!
michael@0 175 name.SetLength(nsUnescapeCount(name.BeginWriting()));
michael@0 176
michael@0 177 // All tokens are case-insensitive - http://www.mozilla.org/projects/netlib/dirindexformat.html
michael@0 178 if (name.LowerCaseEqualsLiteral("description"))
michael@0 179 mHasDescription = true;
michael@0 180
michael@0 181 for (Field* i = gFieldTable; i->mName; ++i) {
michael@0 182 if (name.EqualsIgnoreCase(i->mName)) {
michael@0 183 mFormat[formatNum] = i->mType;
michael@0 184 ++formatNum;
michael@0 185 break;
michael@0 186 }
michael@0 187 }
michael@0 188
michael@0 189 } while (*aFormatStr);
michael@0 190
michael@0 191 return NS_OK;
michael@0 192 }
michael@0 193
michael@0 194 nsresult
michael@0 195 nsDirIndexParser::ParseData(nsIDirIndex *aIdx, char* aDataStr) {
michael@0 196 // Parse a "201" data line, using the field ordering specified in
michael@0 197 // mFormat.
michael@0 198
michael@0 199 if (!mFormat) {
michael@0 200 // Ignore if we haven't seen a format yet.
michael@0 201 return NS_OK;
michael@0 202 }
michael@0 203
michael@0 204 nsresult rv = NS_OK;
michael@0 205
michael@0 206 nsAutoCString filename;
michael@0 207
michael@0 208 for (int32_t i = 0; mFormat[i] != -1; ++i) {
michael@0 209 // If we've exhausted the data before we run out of fields, just
michael@0 210 // bail.
michael@0 211 if (! *aDataStr)
michael@0 212 break;
michael@0 213
michael@0 214 while (*aDataStr && nsCRT::IsAsciiSpace(*aDataStr))
michael@0 215 ++aDataStr;
michael@0 216
michael@0 217 char *value = aDataStr;
michael@0 218
michael@0 219 if (*aDataStr == '"' || *aDataStr == '\'') {
michael@0 220 // it's a quoted string. snarf everything up to the next quote character
michael@0 221 const char quotechar = *(aDataStr++);
michael@0 222 ++value;
michael@0 223 while (*aDataStr && *aDataStr != quotechar)
michael@0 224 ++aDataStr;
michael@0 225 *aDataStr++ = '\0';
michael@0 226
michael@0 227 if (! aDataStr) {
michael@0 228 NS_WARNING("quoted value not terminated");
michael@0 229 }
michael@0 230 } else {
michael@0 231 // it's unquoted. snarf until we see whitespace.
michael@0 232 value = aDataStr;
michael@0 233 while (*aDataStr && (!nsCRT::IsAsciiSpace(*aDataStr)))
michael@0 234 ++aDataStr;
michael@0 235 *aDataStr++ = '\0';
michael@0 236 }
michael@0 237
michael@0 238 fieldType t = fieldType(mFormat[i]);
michael@0 239 switch (t) {
michael@0 240 case FIELD_FILENAME: {
michael@0 241 // don't unescape at this point, so that UnEscapeAndConvert() can
michael@0 242 filename = value;
michael@0 243
michael@0 244 bool success = false;
michael@0 245
michael@0 246 nsAutoString entryuri;
michael@0 247
michael@0 248 if (gTextToSubURI) {
michael@0 249 char16_t *result = nullptr;
michael@0 250 if (NS_SUCCEEDED(rv = gTextToSubURI->UnEscapeAndConvert(mEncoding.get(), filename.get(),
michael@0 251 &result)) && (result)) {
michael@0 252 if (*result) {
michael@0 253 aIdx->SetLocation(filename.get());
michael@0 254 if (!mHasDescription)
michael@0 255 aIdx->SetDescription(result);
michael@0 256 success = true;
michael@0 257 }
michael@0 258 NS_Free(result);
michael@0 259 } else {
michael@0 260 NS_WARNING("UnEscapeAndConvert error");
michael@0 261 }
michael@0 262 }
michael@0 263
michael@0 264 if (!success) {
michael@0 265 // if unsuccessfully at charset conversion, then
michael@0 266 // just fallback to unescape'ing in-place
michael@0 267 // XXX - this shouldn't be using UTF8, should it?
michael@0 268 // when can we fail to get the service, anyway? - bbaetz
michael@0 269 aIdx->SetLocation(filename.get());
michael@0 270 if (!mHasDescription) {
michael@0 271 aIdx->SetDescription(NS_ConvertUTF8toUTF16(value).get());
michael@0 272 }
michael@0 273 }
michael@0 274 }
michael@0 275 break;
michael@0 276 case FIELD_DESCRIPTION:
michael@0 277 nsUnescape(value);
michael@0 278 aIdx->SetDescription(NS_ConvertUTF8toUTF16(value).get());
michael@0 279 break;
michael@0 280 case FIELD_CONTENTLENGTH:
michael@0 281 {
michael@0 282 int64_t len;
michael@0 283 int32_t status = PR_sscanf(value, "%lld", &len);
michael@0 284 if (status == 1)
michael@0 285 aIdx->SetSize(len);
michael@0 286 else
michael@0 287 aIdx->SetSize(UINT64_MAX); // UINT64_MAX means unknown
michael@0 288 }
michael@0 289 break;
michael@0 290 case FIELD_LASTMODIFIED:
michael@0 291 {
michael@0 292 PRTime tm;
michael@0 293 nsUnescape(value);
michael@0 294 if (PR_ParseTimeString(value, false, &tm) == PR_SUCCESS) {
michael@0 295 aIdx->SetLastModified(tm);
michael@0 296 }
michael@0 297 }
michael@0 298 break;
michael@0 299 case FIELD_CONTENTTYPE:
michael@0 300 aIdx->SetContentType(value);
michael@0 301 break;
michael@0 302 case FIELD_FILETYPE:
michael@0 303 // unescape in-place
michael@0 304 nsUnescape(value);
michael@0 305 if (!nsCRT::strcasecmp(value, "directory")) {
michael@0 306 aIdx->SetType(nsIDirIndex::TYPE_DIRECTORY);
michael@0 307 } else if (!nsCRT::strcasecmp(value, "file")) {
michael@0 308 aIdx->SetType(nsIDirIndex::TYPE_FILE);
michael@0 309 } else if (!nsCRT::strcasecmp(value, "symbolic-link")) {
michael@0 310 aIdx->SetType(nsIDirIndex::TYPE_SYMLINK);
michael@0 311 } else {
michael@0 312 aIdx->SetType(nsIDirIndex::TYPE_UNKNOWN);
michael@0 313 }
michael@0 314 break;
michael@0 315 case FIELD_UNKNOWN:
michael@0 316 // ignore
michael@0 317 break;
michael@0 318 }
michael@0 319 }
michael@0 320
michael@0 321 return NS_OK;
michael@0 322 }
michael@0 323
michael@0 324 NS_IMETHODIMP
michael@0 325 nsDirIndexParser::OnDataAvailable(nsIRequest *aRequest, nsISupports *aCtxt,
michael@0 326 nsIInputStream *aStream,
michael@0 327 uint64_t aSourceOffset,
michael@0 328 uint32_t aCount) {
michael@0 329 if (aCount < 1)
michael@0 330 return NS_OK;
michael@0 331
michael@0 332 int32_t len = mBuf.Length();
michael@0 333
michael@0 334 // Ensure that our mBuf has capacity to hold the data we're about to
michael@0 335 // read.
michael@0 336 if (!mBuf.SetLength(len + aCount, fallible_t()))
michael@0 337 return NS_ERROR_OUT_OF_MEMORY;
michael@0 338
michael@0 339 // Now read the data into our buffer.
michael@0 340 nsresult rv;
michael@0 341 uint32_t count;
michael@0 342 rv = aStream->Read(mBuf.BeginWriting() + len, aCount, &count);
michael@0 343 if (NS_FAILED(rv)) return rv;
michael@0 344
michael@0 345 // Set the string's length according to the amount of data we've read.
michael@0 346 // Note: we know this to work on nsCString. This isn't guaranteed to
michael@0 347 // work on other strings.
michael@0 348 mBuf.SetLength(len + count);
michael@0 349
michael@0 350 return ProcessData(aRequest, aCtxt);
michael@0 351 }
michael@0 352
michael@0 353 nsresult
michael@0 354 nsDirIndexParser::ProcessData(nsIRequest *aRequest, nsISupports *aCtxt) {
michael@0 355 if (!mListener)
michael@0 356 return NS_ERROR_FAILURE;
michael@0 357
michael@0 358 int32_t numItems = 0;
michael@0 359
michael@0 360 while(true) {
michael@0 361 ++numItems;
michael@0 362
michael@0 363 int32_t eol = mBuf.FindCharInSet("\n\r", mLineStart);
michael@0 364 if (eol < 0) break;
michael@0 365 mBuf.SetCharAt(char16_t('\0'), eol);
michael@0 366
michael@0 367 const char *line = mBuf.get() + mLineStart;
michael@0 368
michael@0 369 int32_t lineLen = eol - mLineStart;
michael@0 370 mLineStart = eol + 1;
michael@0 371
michael@0 372 if (lineLen >= 4) {
michael@0 373 nsresult rv;
michael@0 374 const char *buf = line;
michael@0 375
michael@0 376 if (buf[0] == '1') {
michael@0 377 if (buf[1] == '0') {
michael@0 378 if (buf[2] == '0' && buf[3] == ':') {
michael@0 379 // 100. Human-readable comment line. Ignore
michael@0 380 } else if (buf[2] == '1' && buf[3] == ':') {
michael@0 381 // 101. Human-readable information line.
michael@0 382 mComment.Append(buf + 4);
michael@0 383
michael@0 384 char *value = ((char *)buf) + 4;
michael@0 385 nsUnescape(value);
michael@0 386 mListener->OnInformationAvailable(aRequest, aCtxt, NS_ConvertUTF8toUTF16(value));
michael@0 387
michael@0 388 } else if (buf[2] == '2' && buf[3] == ':') {
michael@0 389 // 102. Human-readable information line, HTML.
michael@0 390 mComment.Append(buf + 4);
michael@0 391 }
michael@0 392 }
michael@0 393 } else if (buf[0] == '2') {
michael@0 394 if (buf[1] == '0') {
michael@0 395 if (buf[2] == '0' && buf[3] == ':') {
michael@0 396 // 200. Define field names
michael@0 397 rv = ParseFormat(buf + 4);
michael@0 398 if (NS_FAILED(rv)) {
michael@0 399 return rv;
michael@0 400 }
michael@0 401 } else if (buf[2] == '1' && buf[3] == ':') {
michael@0 402 // 201. Field data
michael@0 403 nsCOMPtr<nsIDirIndex> idx = do_CreateInstance("@mozilla.org/dirIndex;1",&rv);
michael@0 404 if (NS_FAILED(rv))
michael@0 405 return rv;
michael@0 406
michael@0 407 rv = ParseData(idx, ((char *)buf) + 4);
michael@0 408 if (NS_FAILED(rv)) {
michael@0 409 return rv;
michael@0 410 }
michael@0 411
michael@0 412 mListener->OnIndexAvailable(aRequest, aCtxt, idx);
michael@0 413 }
michael@0 414 }
michael@0 415 } else if (buf[0] == '3') {
michael@0 416 if (buf[1] == '0') {
michael@0 417 if (buf[2] == '0' && buf[3] == ':') {
michael@0 418 // 300. Self-referring URL
michael@0 419 } else if (buf[2] == '1' && buf[3] == ':') {
michael@0 420 // 301. OUR EXTENSION - encoding
michael@0 421 int i = 4;
michael@0 422 while (buf[i] && nsCRT::IsAsciiSpace(buf[i]))
michael@0 423 ++i;
michael@0 424
michael@0 425 if (buf[i])
michael@0 426 SetEncoding(buf+i);
michael@0 427 }
michael@0 428 }
michael@0 429 }
michael@0 430 }
michael@0 431 }
michael@0 432
michael@0 433 return NS_OK;
michael@0 434 }

mercurial