tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

MimeType.cpp (12543B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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/. */
      6 
      7 #include "MimeType.h"
      8 
      9 #include "nsNetUtil.h"
     10 #include "nsUnicharUtils.h"
     11 
     12 template <typename char_type>
     13 /* static */ RefPtr<TMimeType<char_type>> TMimeType<char_type>::Parse(
     14    const nsTSubstring<char_type>& aMimeType) {
     15  // See https://mimesniff.spec.whatwg.org/#parsing-a-mime-type
     16 
     17  // Steps 1-2
     18  const char_type* pos = aMimeType.BeginReading();
     19  const char_type* end = aMimeType.EndReading();
     20  while (pos < end && NS_IsHTTPWhitespace(*pos)) {
     21    ++pos;
     22  }
     23  if (pos == end) {
     24    return nullptr;
     25  }
     26  while (end > pos && NS_IsHTTPWhitespace(*(end - 1))) {
     27    --end;
     28  }
     29 
     30  // Steps 3-4
     31  const char_type* typeStart = pos;
     32  while (pos < end && *pos != '/') {
     33    if (!NS_IsHTTPTokenPoint(*pos)) {
     34      return nullptr;
     35    }
     36    ++pos;
     37  }
     38  const char_type* typeEnd = pos;
     39  if (typeStart == typeEnd) {
     40    return nullptr;
     41  }
     42 
     43  // Step 5
     44  if (pos == end) {
     45    return nullptr;
     46  }
     47 
     48  // Step 6
     49  ++pos;
     50 
     51  // Step 7-9
     52  const char_type* subtypeStart = pos;
     53  const char_type* subtypeEnd = nullptr;
     54  while (pos < end && *pos != ';') {
     55    if (!NS_IsHTTPTokenPoint(*pos)) {
     56      // If we hit a whitespace, check that the rest of
     57      // the subtype is whitespace, otherwise fail.
     58      if (NS_IsHTTPWhitespace(*pos)) {
     59        subtypeEnd = pos;
     60        ++pos;
     61        while (pos < end && *pos != ';') {
     62          if (!NS_IsHTTPWhitespace(*pos)) {
     63            return nullptr;
     64          }
     65          ++pos;
     66        }
     67        break;
     68      }
     69      return nullptr;
     70    }
     71    ++pos;
     72  }
     73  if (subtypeEnd == nullptr) {
     74    subtypeEnd = pos;
     75  }
     76  if (subtypeStart == subtypeEnd) {
     77    return nullptr;
     78  }
     79 
     80  // Step 10
     81  nsTString<char_type> type;
     82  nsTString<char_type> subtype;
     83  for (const char_type* c = typeStart; c < typeEnd; ++c) {
     84    type.Append(ToLowerCaseASCII(*c));
     85  }
     86  for (const char_type* c = subtypeStart; c < subtypeEnd; ++c) {
     87    subtype.Append(ToLowerCaseASCII(*c));
     88  }
     89  RefPtr<TMimeType<char_type>> mimeType =
     90      new TMimeType<char_type>(type, subtype);
     91 
     92  // Step 11
     93  while (pos < end) {
     94    // Step 11.1
     95    ++pos;
     96 
     97    // Step 11.2
     98    while (pos < end && NS_IsHTTPWhitespace(*pos)) {
     99      ++pos;
    100    }
    101 
    102    const char_type* namePos = pos;
    103 
    104    // Steps 11.3 and 11.4
    105    nsTString<char_type> paramName;
    106    bool paramNameHadInvalidChars = false;
    107    while (pos < end && *pos != ';' && *pos != '=') {
    108      if (!NS_IsHTTPTokenPoint(*pos)) {
    109        paramNameHadInvalidChars = true;
    110      }
    111      paramName.Append(ToLowerCaseASCII(*pos));
    112      ++pos;
    113    }
    114 
    115    // Might as well check for base64 now
    116    if (*pos != '=') {
    117      // trim leading and trailing spaces
    118      while (namePos < pos && NS_IsHTTPWhitespace(*namePos)) {
    119        ++namePos;
    120      }
    121      if (namePos < pos && ToLowerCaseASCII(*namePos) == 'b' &&
    122          ++namePos < pos && ToLowerCaseASCII(*namePos) == 'a' &&
    123          ++namePos < pos && ToLowerCaseASCII(*namePos) == 's' &&
    124          ++namePos < pos && ToLowerCaseASCII(*namePos) == 'e' &&
    125          ++namePos < pos && ToLowerCaseASCII(*namePos) == '6' &&
    126          ++namePos < pos && ToLowerCaseASCII(*namePos) == '4') {
    127        while (++namePos < pos && NS_IsHTTPWhitespace(*namePos)) {
    128        }
    129        mimeType->mIsBase64 = namePos == pos;
    130      }
    131    }
    132 
    133    // Step 11.5
    134    if (pos < end) {
    135      if (*pos == ';') {
    136        continue;
    137      }
    138      ++pos;
    139    }
    140 
    141    // Step 11.6
    142    if (pos == end) {
    143      break;
    144    }
    145 
    146    // Step 11.7
    147    ParameterValue paramValue;
    148    bool paramValueHadInvalidChars = false;
    149 
    150    // Step 11.8
    151    if (*pos == '"') {
    152      // Step 11.8.1
    153      ++pos;
    154 
    155      // Step 11.8.2
    156      while (true) {
    157        // Step 11.8.2.1
    158        while (pos < end && *pos != '"' && *pos != '\\') {
    159          if (!NS_IsHTTPQuotedStringTokenPoint(*pos)) {
    160            paramValueHadInvalidChars = true;
    161          }
    162          if (!NS_IsHTTPTokenPoint(*pos)) {
    163            paramValue.mRequiresQuoting = true;
    164          }
    165          paramValue.Append(*pos);
    166          ++pos;
    167        }
    168 
    169        // Step 11.8.2.2
    170        if (pos < end && *pos == '\\') {
    171          // Step 11.8.2.2.1
    172          ++pos;
    173 
    174          // Step 11.8.2.2.2
    175          if (pos < end) {
    176            if (!NS_IsHTTPQuotedStringTokenPoint(*pos)) {
    177              paramValueHadInvalidChars = true;
    178            }
    179            if (!NS_IsHTTPTokenPoint(*pos)) {
    180              paramValue.mRequiresQuoting = true;
    181            }
    182            paramValue.Append(*pos);
    183            ++pos;
    184            continue;
    185          }
    186 
    187          // Step 11.8.2.2.3
    188          paramValue.Append('\\');
    189          paramValue.mRequiresQuoting = true;
    190        }
    191 
    192        // Step 11.8.2.3
    193        break;
    194      }
    195 
    196      // Step 11.8.3
    197      while (pos < end && *pos != ';') {
    198        ++pos;
    199      }
    200 
    201      // Step 11.9
    202    } else {
    203      // Step 11.9.1
    204      const char_type* paramValueStart = pos;
    205      while (pos < end && *pos != ';') {
    206        ++pos;
    207      }
    208 
    209      // Step 11.9.2
    210      const char_type* paramValueLastChar = pos - 1;
    211      while (paramValueLastChar >= paramValueStart &&
    212             NS_IsHTTPWhitespace(*paramValueLastChar)) {
    213        --paramValueLastChar;
    214      }
    215 
    216      // Step 11.9.3
    217      if (paramValueStart > paramValueLastChar) {
    218        continue;
    219      }
    220 
    221      for (const char_type* c = paramValueStart; c <= paramValueLastChar; ++c) {
    222        if (!NS_IsHTTPQuotedStringTokenPoint(*c)) {
    223          paramValueHadInvalidChars = true;
    224        }
    225        if (!NS_IsHTTPTokenPoint(*c)) {
    226          paramValue.mRequiresQuoting = true;
    227        }
    228        paramValue.Append(*c);
    229      }
    230    }
    231 
    232    // Step 11.10
    233    if (!paramName.IsEmpty() && !paramNameHadInvalidChars &&
    234        !paramValueHadInvalidChars) {
    235      // XXX Is the assigned value used anywhere?
    236      paramValue = mimeType->mParameters.LookupOrInsertWith(paramName, [&] {
    237        mimeType->mParameterNames.AppendElement(paramName);
    238        return paramValue;
    239      });
    240    }
    241  }
    242 
    243  // Step 12
    244  return mimeType;
    245 }
    246 
    247 template <typename char_type>
    248 /* static */ nsTArray<nsTDependentSubstring<char_type>>
    249 TMimeType<char_type>::SplitMimetype(const nsTSubstring<char_type>& aMimeType) {
    250  nsTArray<nsTDependentSubstring<char_type>> mimeTypeParts;
    251  bool inQuotes = false;
    252  size_t start = 0;
    253 
    254  for (size_t i = 0; i < aMimeType.Length(); i++) {
    255    char_type c = aMimeType[i];
    256 
    257    if (c == '\"' && (i == 0 || aMimeType[i - 1] != '\\')) {
    258      inQuotes = !inQuotes;
    259    } else if (c == ',' && !inQuotes) {
    260      mimeTypeParts.AppendElement(Substring(aMimeType, start, i - start));
    261      start = i + 1;
    262    }
    263  }
    264  if (start < aMimeType.Length()) {
    265    mimeTypeParts.AppendElement(Substring(aMimeType, start));
    266  }
    267  return mimeTypeParts;
    268 }
    269 
    270 template <typename char_type>
    271 /* static */ bool TMimeType<char_type>::Parse(
    272    const nsTSubstring<char_type>& aMimeType,
    273    nsTSubstring<char_type>& aOutEssence,
    274    nsTSubstring<char_type>& aOutCharset) {
    275  static char_type kCHARSET[] = {'c', 'h', 'a', 'r', 's', 'e', 't'};
    276  static nsTDependentSubstring<char_type> kCharset(kCHARSET, 7);
    277 
    278  RefPtr<TMimeType<char_type>> parsed;
    279  nsTAutoString<char_type> prevContentType;
    280  nsTAutoString<char_type> prevCharset;
    281 
    282  prevContentType.Assign(aOutEssence);
    283  prevCharset.Assign(aOutCharset);
    284 
    285  nsTArray<nsTDependentSubstring<char_type>> mimeTypeParts =
    286      SplitMimetype(aMimeType);
    287 
    288  for (auto& mimeTypeString : mimeTypeParts) {
    289    if (mimeTypeString.EqualsLiteral("error")) {
    290      continue;
    291    }
    292 
    293    parsed = Parse(mimeTypeString);
    294 
    295    if (!parsed) {
    296      aOutEssence.Truncate();
    297      aOutCharset.Truncate();
    298      return false;
    299    }
    300 
    301    parsed->GetEssence(aOutEssence);
    302 
    303    if (aOutEssence.EqualsLiteral("*/*")) {
    304      aOutEssence.Assign(prevContentType);
    305      continue;
    306    }
    307 
    308    bool eq = !prevContentType.IsEmpty() && aOutEssence.Equals(prevContentType);
    309 
    310    if (!eq) {
    311      prevContentType.Assign(aOutEssence);
    312    }
    313 
    314    bool typeHasCharset = false;
    315    if (parsed->GetParameterValue(kCharset, aOutCharset, false, false)) {
    316      typeHasCharset = true;
    317    } else if (eq) {
    318      aOutCharset.Assign(prevCharset);
    319    }
    320 
    321    if ((!eq && !prevCharset.IsEmpty()) || typeHasCharset) {
    322      prevCharset.Assign(aOutCharset);
    323    }
    324  }
    325 
    326  return true;
    327 }
    328 
    329 template <typename char_type>
    330 void TMimeType<char_type>::Serialize(nsTSubstring<char_type>& aOutput) const {
    331  aOutput.Assign(mType);
    332  aOutput.Append('/');
    333  aOutput.Append(mSubtype);
    334  for (uint32_t i = 0; i < mParameterNames.Length(); i++) {
    335    auto name = mParameterNames[i];
    336    aOutput.Append(';');
    337    aOutput.Append(name);
    338    aOutput.Append('=');
    339    GetParameterValue(name, aOutput, true);
    340  }
    341 }
    342 
    343 template <typename char_type>
    344 void TMimeType<char_type>::GetEssence(nsTSubstring<char_type>& aOutput) const {
    345  aOutput.Assign(mType);
    346  aOutput.Append('/');
    347  GetSubtype(aOutput);
    348 }
    349 
    350 template <typename char_type>
    351 void TMimeType<char_type>::GetSubtype(nsTSubstring<char_type>& aOutput) const {
    352  aOutput.Append(mSubtype);
    353 }
    354 
    355 template <typename char_type>
    356 bool TMimeType<char_type>::HasParameter(
    357    const nsTSubstring<char_type>& aName) const {
    358  return mParameters.Get(aName, nullptr);
    359 }
    360 
    361 template <typename char_type>
    362 bool TMimeType<char_type>::GetParameterValue(
    363    const nsTSubstring<char_type>& aName, nsTSubstring<char_type>& aOutput,
    364    bool aAppend, bool aWithQuotes) const {
    365  if (!aAppend) {
    366    aOutput.Truncate();
    367  }
    368 
    369  ParameterValue value;
    370  if (!mParameters.Get(aName, &value)) {
    371    return false;
    372  }
    373 
    374  if (aWithQuotes && (value.mRequiresQuoting || value.IsEmpty())) {
    375    aOutput.Append('"');
    376    const char_type* vcur = value.BeginReading();
    377    const char_type* vend = value.EndReading();
    378    while (vcur < vend) {
    379      if (*vcur == '"' || *vcur == '\\') {
    380        aOutput.Append('\\');
    381      }
    382      aOutput.Append(*vcur);
    383      vcur++;
    384    }
    385    aOutput.Append('"');
    386  } else {
    387    aOutput.Append(value);
    388  }
    389 
    390  return true;
    391 }
    392 
    393 template <typename char_type>
    394 void TMimeType<char_type>::SetParameterValue(
    395    const nsTSubstring<char_type>& aName,
    396    const nsTSubstring<char_type>& aValue) {
    397  mParameters.WithEntryHandle(aName, [&](auto&& entry) {
    398    if (!entry) {
    399      mParameterNames.AppendElement(aName);
    400    }
    401    ParameterValue value;
    402    value.Append(aValue);
    403    entry.InsertOrUpdate(std::move(value));
    404  });
    405 }
    406 
    407 template RefPtr<TMimeType<char16_t>> TMimeType<char16_t>::Parse(
    408    const nsTSubstring<char16_t>& aMimeType);
    409 template RefPtr<TMimeType<char>> TMimeType<char>::Parse(
    410    const nsTSubstring<char>& aMimeType);
    411 template bool TMimeType<char16_t>::Parse(
    412    const nsTSubstring<char16_t>& aMimeType,
    413    nsTSubstring<char16_t>& aOutEssence, nsTSubstring<char16_t>& aOutCharset);
    414 template bool TMimeType<char>::Parse(const nsTSubstring<char>& aMimeType,
    415                                     nsTSubstring<char>& aOutEssence,
    416                                     nsTSubstring<char>& aOutCharset);
    417 template nsTArray<nsTDependentSubstring<char>> TMimeType<char>::SplitMimetype(
    418    const nsTSubstring<char>& aMimeType);
    419 template void TMimeType<char16_t>::Serialize(
    420    nsTSubstring<char16_t>& aOutput) const;
    421 template void TMimeType<char>::Serialize(nsTSubstring<char>& aOutput) const;
    422 template void TMimeType<char16_t>::GetEssence(
    423    nsTSubstring<char16_t>& aOutput) const;
    424 template void TMimeType<char>::GetEssence(nsTSubstring<char>& aOutput) const;
    425 template void TMimeType<char16_t>::GetSubtype(
    426    nsTSubstring<char16_t>& aOutput) const;
    427 template void TMimeType<char>::GetSubtype(nsTSubstring<char>& aOutput) const;
    428 template bool TMimeType<char16_t>::HasParameter(
    429    const nsTSubstring<char16_t>& aName) const;
    430 template bool TMimeType<char>::HasParameter(
    431    const nsTSubstring<char>& aName) const;
    432 template bool TMimeType<char16_t>::GetParameterValue(
    433    const nsTSubstring<char16_t>& aName, nsTSubstring<char16_t>& aOutput,
    434    bool aAppend, bool aWithQuotes) const;
    435 template bool TMimeType<char>::GetParameterValue(
    436    const nsTSubstring<char>& aName, nsTSubstring<char>& aOutput, bool aAppend,
    437    bool aWithQuotes) const;
    438 template void TMimeType<char16_t>::SetParameterValue(
    439    const nsTSubstring<char16_t>& aName, const nsTSubstring<char16_t>& aValue);
    440 template void TMimeType<char>::SetParameterValue(
    441    const nsTSubstring<char>& aName, const nsTSubstring<char>& aValue);