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);