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.

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

mercurial