2019-07-05 20:05:57 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
2018-08-13 23:12:10 +03:00
|
|
|
#include "MimeType.h"
|
2023-08-03 02:41:24 +03:00
|
|
|
#include "nsNetUtil.h"
|
2018-08-13 23:12:10 +03:00
|
|
|
#include "nsUnicharUtils.h"
|
|
|
|
|
2018-09-07 08:41:29 +03:00
|
|
|
template <typename char_type>
|
|
|
|
/* static */ mozilla::UniquePtr<TMimeType<char_type>>
|
|
|
|
TMimeType<char_type>::Parse(const nsTSubstring<char_type>& aMimeType) {
|
2018-08-13 23:12:10 +03:00
|
|
|
// See https://mimesniff.spec.whatwg.org/#parsing-a-mime-type
|
|
|
|
|
|
|
|
// Steps 1-2
|
2018-09-07 08:41:29 +03:00
|
|
|
const char_type* pos = aMimeType.BeginReading();
|
|
|
|
const char_type* end = aMimeType.EndReading();
|
2023-08-03 02:41:24 +03:00
|
|
|
while (pos < end && NS_IsHTTPWhitespace(*pos)) {
|
2018-08-13 23:12:10 +03:00
|
|
|
++pos;
|
|
|
|
}
|
|
|
|
if (pos == end) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2023-08-03 02:41:24 +03:00
|
|
|
while (end > pos && NS_IsHTTPWhitespace(*(end - 1))) {
|
2018-08-13 23:12:10 +03:00
|
|
|
--end;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Steps 3-4
|
2018-09-07 08:41:29 +03:00
|
|
|
const char_type* typeStart = pos;
|
2018-08-13 23:12:10 +03:00
|
|
|
while (pos < end && *pos != '/') {
|
2023-08-03 02:41:24 +03:00
|
|
|
if (!NS_IsHTTPTokenPoint(*pos)) {
|
2018-08-13 23:12:10 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
++pos;
|
|
|
|
}
|
2018-09-07 08:41:29 +03:00
|
|
|
const char_type* typeEnd = pos;
|
2018-08-13 23:12:10 +03:00
|
|
|
if (typeStart == typeEnd) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 5
|
|
|
|
if (pos == end) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 6
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
// Step 7-9
|
2018-09-07 08:41:29 +03:00
|
|
|
const char_type* subtypeStart = pos;
|
|
|
|
const char_type* subtypeEnd = nullptr;
|
2018-08-13 23:12:10 +03:00
|
|
|
while (pos < end && *pos != ';') {
|
2023-08-03 02:41:24 +03:00
|
|
|
if (!NS_IsHTTPTokenPoint(*pos)) {
|
2018-08-13 23:12:10 +03:00
|
|
|
// If we hit a whitespace, check that the rest of
|
|
|
|
// the subtype is whitespace, otherwise fail.
|
2023-08-03 02:41:24 +03:00
|
|
|
if (NS_IsHTTPWhitespace(*pos)) {
|
2018-08-13 23:12:10 +03:00
|
|
|
subtypeEnd = pos;
|
|
|
|
++pos;
|
|
|
|
while (pos < end && *pos != ';') {
|
2023-08-03 02:41:24 +03:00
|
|
|
if (!NS_IsHTTPWhitespace(*pos)) {
|
2018-08-13 23:12:10 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
++pos;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2018-09-26 00:05:12 +03:00
|
|
|
return nullptr;
|
2018-08-13 23:12:10 +03:00
|
|
|
}
|
|
|
|
++pos;
|
|
|
|
}
|
|
|
|
if (subtypeEnd == nullptr) {
|
|
|
|
subtypeEnd = pos;
|
|
|
|
}
|
|
|
|
if (subtypeStart == subtypeEnd) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 10
|
2018-09-07 08:41:29 +03:00
|
|
|
nsTString<char_type> type;
|
|
|
|
nsTString<char_type> subtype;
|
|
|
|
for (const char_type* c = typeStart; c < typeEnd; ++c) {
|
2018-08-13 23:12:10 +03:00
|
|
|
type.Append(ToLowerCaseASCII(*c));
|
|
|
|
}
|
2018-09-07 08:41:29 +03:00
|
|
|
for (const char_type* c = subtypeStart; c < subtypeEnd; ++c) {
|
2018-08-13 23:12:10 +03:00
|
|
|
subtype.Append(ToLowerCaseASCII(*c));
|
|
|
|
}
|
2018-09-07 08:41:29 +03:00
|
|
|
mozilla::UniquePtr<TMimeType<char_type>> mimeType(
|
|
|
|
mozilla::MakeUnique<TMimeType<char_type>>(type, subtype));
|
2018-08-13 23:12:10 +03:00
|
|
|
|
|
|
|
// Step 11
|
|
|
|
while (pos < end) {
|
|
|
|
// Step 11.1
|
|
|
|
++pos;
|
|
|
|
|
|
|
|
// Step 11.2
|
2023-08-03 02:41:24 +03:00
|
|
|
while (pos < end && NS_IsHTTPWhitespace(*pos)) {
|
2018-08-13 23:12:10 +03:00
|
|
|
++pos;
|
|
|
|
}
|
|
|
|
|
2023-08-03 02:41:24 +03:00
|
|
|
const char_type* namePos = pos;
|
|
|
|
|
2018-08-13 23:12:10 +03:00
|
|
|
// Steps 11.3 and 11.4
|
2018-09-07 08:41:29 +03:00
|
|
|
nsTString<char_type> paramName;
|
2018-08-13 23:12:10 +03:00
|
|
|
bool paramNameHadInvalidChars = false;
|
|
|
|
while (pos < end && *pos != ';' && *pos != '=') {
|
2023-08-03 02:41:24 +03:00
|
|
|
if (!NS_IsHTTPTokenPoint(*pos)) {
|
2018-08-13 23:12:10 +03:00
|
|
|
paramNameHadInvalidChars = true;
|
|
|
|
}
|
|
|
|
paramName.Append(ToLowerCaseASCII(*pos));
|
|
|
|
++pos;
|
|
|
|
}
|
|
|
|
|
2023-08-03 02:41:24 +03:00
|
|
|
// Might as well check for base64 now
|
|
|
|
if (*pos != '=') {
|
|
|
|
// trim leading and trailing spaces
|
|
|
|
while (namePos < pos && NS_IsHTTPWhitespace(*namePos)) {
|
|
|
|
++namePos;
|
|
|
|
}
|
|
|
|
if (namePos < pos && ToLowerCaseASCII(*namePos) == 'b' &&
|
|
|
|
++namePos < pos && ToLowerCaseASCII(*namePos) == 'a' &&
|
|
|
|
++namePos < pos && ToLowerCaseASCII(*namePos) == 's' &&
|
|
|
|
++namePos < pos && ToLowerCaseASCII(*namePos) == 'e' &&
|
|
|
|
++namePos < pos && ToLowerCaseASCII(*namePos) == '6' &&
|
|
|
|
++namePos < pos && ToLowerCaseASCII(*namePos) == '4') {
|
|
|
|
while (++namePos < pos && NS_IsHTTPWhitespace(*namePos)) {
|
|
|
|
}
|
|
|
|
mimeType->mIsBase64 = namePos == pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-13 23:12:10 +03:00
|
|
|
// Step 11.5
|
|
|
|
if (pos < end) {
|
|
|
|
if (*pos == ';') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
++pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 11.6
|
2018-09-26 00:05:12 +03:00
|
|
|
if (pos == end) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 11.7
|
2018-08-13 23:12:10 +03:00
|
|
|
ParameterValue paramValue;
|
|
|
|
bool paramValueHadInvalidChars = false;
|
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 11.8
|
|
|
|
if (*pos == '"') {
|
|
|
|
// Step 11.8.1
|
|
|
|
++pos;
|
2018-08-13 23:12:10 +03:00
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 11.8.2
|
|
|
|
while (true) {
|
|
|
|
// Step 11.8.2.1
|
|
|
|
while (pos < end && *pos != '"' && *pos != '\\') {
|
2023-08-03 02:41:24 +03:00
|
|
|
if (!NS_IsHTTPQuotedStringTokenPoint(*pos)) {
|
2018-09-26 00:05:12 +03:00
|
|
|
paramValueHadInvalidChars = true;
|
|
|
|
}
|
2023-08-03 02:41:24 +03:00
|
|
|
if (!NS_IsHTTPTokenPoint(*pos)) {
|
2018-09-26 00:05:12 +03:00
|
|
|
paramValue.mRequiresQuoting = true;
|
|
|
|
}
|
|
|
|
paramValue.Append(*pos);
|
|
|
|
++pos;
|
|
|
|
}
|
2018-08-13 23:12:10 +03:00
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 11.8.2.2
|
|
|
|
if (pos < end && *pos == '\\') {
|
|
|
|
// Step 11.8.2.2.1
|
|
|
|
++pos;
|
2018-08-13 23:12:10 +03:00
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 11.8.2.2.2
|
|
|
|
if (pos < end) {
|
2023-08-03 02:41:24 +03:00
|
|
|
if (!NS_IsHTTPQuotedStringTokenPoint(*pos)) {
|
2018-08-13 23:12:10 +03:00
|
|
|
paramValueHadInvalidChars = true;
|
|
|
|
}
|
2023-08-03 02:41:24 +03:00
|
|
|
if (!NS_IsHTTPTokenPoint(*pos)) {
|
2018-08-13 23:12:10 +03:00
|
|
|
paramValue.mRequiresQuoting = true;
|
|
|
|
}
|
|
|
|
paramValue.Append(*pos);
|
|
|
|
++pos;
|
2018-09-26 00:05:12 +03:00
|
|
|
continue;
|
2018-08-13 23:12:10 +03:00
|
|
|
}
|
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 11.8.2.2.3
|
|
|
|
paramValue.Append('\\');
|
|
|
|
paramValue.mRequiresQuoting = true;
|
|
|
|
}
|
2018-08-13 23:12:10 +03:00
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 11.8.2.3
|
|
|
|
break;
|
|
|
|
}
|
2018-08-13 23:12:10 +03:00
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 11.8.3
|
|
|
|
while (pos < end && *pos != ';') {
|
|
|
|
++pos;
|
|
|
|
}
|
2018-08-13 23:12:10 +03:00
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 11.9
|
|
|
|
} else {
|
|
|
|
// Step 11.9.1
|
|
|
|
const char_type* paramValueStart = pos;
|
|
|
|
while (pos < end && *pos != ';') {
|
|
|
|
++pos;
|
|
|
|
}
|
2018-08-13 23:12:10 +03:00
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 11.9.2
|
|
|
|
const char_type* paramValueLastChar = pos - 1;
|
|
|
|
while (paramValueLastChar >= paramValueStart &&
|
2023-08-03 02:41:24 +03:00
|
|
|
NS_IsHTTPWhitespace(*paramValueLastChar)) {
|
2018-09-26 00:05:12 +03:00
|
|
|
--paramValueLastChar;
|
|
|
|
}
|
2018-08-13 23:12:10 +03:00
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 11.9.3
|
|
|
|
if (paramValueStart > paramValueLastChar) {
|
|
|
|
continue;
|
|
|
|
}
|
2018-08-13 23:12:10 +03:00
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
for (const char_type* c = paramValueStart; c <= paramValueLastChar; ++c) {
|
2023-08-03 02:41:24 +03:00
|
|
|
if (!NS_IsHTTPQuotedStringTokenPoint(*c)) {
|
2018-09-26 00:05:12 +03:00
|
|
|
paramValueHadInvalidChars = true;
|
2018-08-13 23:12:10 +03:00
|
|
|
}
|
2023-08-03 02:41:24 +03:00
|
|
|
if (!NS_IsHTTPTokenPoint(*c)) {
|
2018-09-26 00:05:12 +03:00
|
|
|
paramValue.mRequiresQuoting = true;
|
2018-08-13 23:12:10 +03:00
|
|
|
}
|
2018-09-26 00:05:12 +03:00
|
|
|
paramValue.Append(*c);
|
2018-08-13 23:12:10 +03:00
|
|
|
}
|
2018-09-26 00:05:12 +03:00
|
|
|
}
|
2018-08-13 23:12:10 +03:00
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 11.10
|
|
|
|
if (!paramName.IsEmpty() && !paramNameHadInvalidChars &&
|
2021-02-26 14:36:40 +03:00
|
|
|
!paramValueHadInvalidChars) {
|
|
|
|
// XXX Is the assigned value used anywhere?
|
|
|
|
paramValue = mimeType->mParameters.LookupOrInsertWith(paramName, [&] {
|
|
|
|
mimeType->mParameterNames.AppendElement(paramName);
|
|
|
|
return paramValue;
|
|
|
|
});
|
2018-08-13 23:12:10 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
// Step 12
|
2018-08-13 23:12:10 +03:00
|
|
|
return mimeType;
|
|
|
|
}
|
|
|
|
|
2018-09-07 08:41:29 +03:00
|
|
|
template <typename char_type>
|
|
|
|
void TMimeType<char_type>::Serialize(nsTSubstring<char_type>& aOutput) const {
|
2018-08-13 23:12:10 +03:00
|
|
|
aOutput.Assign(mType);
|
|
|
|
aOutput.AppendLiteral("/");
|
|
|
|
aOutput.Append(mSubtype);
|
|
|
|
for (uint32_t i = 0; i < mParameterNames.Length(); i++) {
|
|
|
|
auto name = mParameterNames[i];
|
|
|
|
aOutput.AppendLiteral(";");
|
|
|
|
aOutput.Append(name);
|
|
|
|
aOutput.AppendLiteral("=");
|
2018-09-07 08:44:49 +03:00
|
|
|
GetParameterValue(name, aOutput, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename char_type>
|
|
|
|
void TMimeType<char_type>::GetFullType(nsTSubstring<char_type>& aOutput) const {
|
|
|
|
aOutput.Assign(mType);
|
|
|
|
aOutput.AppendLiteral("/");
|
|
|
|
aOutput.Append(mSubtype);
|
|
|
|
}
|
|
|
|
|
2018-09-20 04:04:51 +03:00
|
|
|
template <typename char_type>
|
|
|
|
bool TMimeType<char_type>::HasParameter(
|
|
|
|
const nsTSubstring<char_type>& aName) const {
|
|
|
|
return mParameters.Get(aName, nullptr);
|
|
|
|
}
|
|
|
|
|
2018-09-07 08:44:49 +03:00
|
|
|
template <typename char_type>
|
|
|
|
bool TMimeType<char_type>::GetParameterValue(
|
|
|
|
const nsTSubstring<char_type>& aName, nsTSubstring<char_type>& aOutput,
|
|
|
|
bool aAppend) const {
|
|
|
|
if (!aAppend) {
|
|
|
|
aOutput.Truncate();
|
|
|
|
}
|
|
|
|
|
|
|
|
ParameterValue value;
|
|
|
|
if (!mParameters.Get(aName, &value)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-09-26 00:05:12 +03:00
|
|
|
if (value.mRequiresQuoting || value.IsEmpty()) {
|
2018-09-07 08:44:49 +03:00
|
|
|
aOutput.AppendLiteral("\"");
|
|
|
|
const char_type* vcur = value.BeginReading();
|
|
|
|
const char_type* vend = value.EndReading();
|
|
|
|
while (vcur < vend) {
|
|
|
|
if (*vcur == '"' || *vcur == '\\') {
|
|
|
|
aOutput.AppendLiteral("\\");
|
2018-08-13 23:12:10 +03:00
|
|
|
}
|
2018-09-07 08:44:49 +03:00
|
|
|
aOutput.Append(*vcur);
|
|
|
|
vcur++;
|
2018-08-13 23:12:10 +03:00
|
|
|
}
|
2018-09-07 08:44:49 +03:00
|
|
|
aOutput.AppendLiteral("\"");
|
|
|
|
} else {
|
|
|
|
aOutput.Append(value);
|
2018-08-13 23:12:10 +03:00
|
|
|
}
|
2018-09-07 08:44:49 +03:00
|
|
|
|
|
|
|
return true;
|
2018-08-13 23:12:10 +03:00
|
|
|
}
|
2018-09-07 08:41:29 +03:00
|
|
|
|
2018-09-20 04:04:51 +03:00
|
|
|
template <typename char_type>
|
|
|
|
void TMimeType<char_type>::SetParameterValue(
|
|
|
|
const nsTSubstring<char_type>& aName,
|
|
|
|
const nsTSubstring<char_type>& aValue) {
|
2021-02-26 14:36:40 +03:00
|
|
|
mParameters.WithEntryHandle(aName, [&](auto&& entry) {
|
|
|
|
if (!entry) {
|
|
|
|
mParameterNames.AppendElement(aName);
|
|
|
|
}
|
|
|
|
ParameterValue value;
|
|
|
|
value.Append(aValue);
|
|
|
|
entry.InsertOrUpdate(std::move(value));
|
|
|
|
});
|
2018-09-20 04:04:51 +03:00
|
|
|
}
|
|
|
|
|
2018-09-07 08:41:29 +03:00
|
|
|
template mozilla::UniquePtr<TMimeType<char16_t>> TMimeType<char16_t>::Parse(
|
|
|
|
const nsTSubstring<char16_t>& aMimeType);
|
|
|
|
template mozilla::UniquePtr<TMimeType<char>> TMimeType<char>::Parse(
|
|
|
|
const nsTSubstring<char>& aMimeType);
|
|
|
|
template void TMimeType<char16_t>::Serialize(
|
|
|
|
nsTSubstring<char16_t>& aOutput) const;
|
|
|
|
template void TMimeType<char>::Serialize(nsTSubstring<char>& aOutput) const;
|
2018-09-07 08:44:49 +03:00
|
|
|
template void TMimeType<char16_t>::GetFullType(
|
|
|
|
nsTSubstring<char16_t>& aOutput) const;
|
|
|
|
template void TMimeType<char>::GetFullType(nsTSubstring<char>& aOutput) const;
|
2018-09-20 04:04:51 +03:00
|
|
|
template bool TMimeType<char16_t>::HasParameter(
|
|
|
|
const nsTSubstring<char16_t>& aName) const;
|
|
|
|
template bool TMimeType<char>::HasParameter(
|
|
|
|
const nsTSubstring<char>& aName) const;
|
2018-09-07 08:44:49 +03:00
|
|
|
template bool TMimeType<char16_t>::GetParameterValue(
|
|
|
|
const nsTSubstring<char16_t>& aName, nsTSubstring<char16_t>& aOutput,
|
|
|
|
bool aAppend) const;
|
|
|
|
template bool TMimeType<char>::GetParameterValue(
|
|
|
|
const nsTSubstring<char>& aName, nsTSubstring<char>& aOutput,
|
|
|
|
bool aAppend) const;
|
2018-09-20 04:04:51 +03:00
|
|
|
template void TMimeType<char16_t>::SetParameterValue(
|
|
|
|
const nsTSubstring<char16_t>& aName, const nsTSubstring<char16_t>& aValue);
|
|
|
|
template void TMimeType<char>::SetParameterValue(
|
|
|
|
const nsTSubstring<char>& aName, const nsTSubstring<char>& aValue);
|