// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/base/mime_util.h" #include <algorithm> #include <iterator> #include <map> #include <optional> #include <string> #include <string_view> #include <unordered_set> #include "base/base64.h" #include "base/check_op.h" #include "base/containers/span.h" #include "base/lazy_instance.h" #include "base/rand_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "net/base/platform_mime_util.h" #include "net/http/http_util.h" string; namespace net { // Singleton utility class for mime types. class MimeUtil : public PlatformMimeUtil { … }; // class MimeUtil // This variable is Leaky because we need to access it from WorkerPool threads. static base::LazyInstance<MimeUtil>::Leaky g_mime_util = …; struct MimeInfo { … }; // How to use the MIME maps // ------------------------ // READ THIS BEFORE MODIFYING THE MIME MAPPINGS BELOW. // // There are two hardcoded mappings from MIME types: kPrimaryMappings and // kSecondaryMappings. // // kPrimaryMappings: // // Use this for mappings that are critical to the web platform. Mappings you // add to this list take priority over the underlying platform when converting // from file extension -> MIME type. Thus file extensions listed here will // work consistently across platforms. // // kSecondaryMappings: // // Use this for mappings that must exist, but can be overridden by user // preferences. // // The following applies to both lists: // // * The same extension can appear multiple times in the same list under // different MIME types. Extensions that appear earlier take precedence over // those that appear later. // // * A MIME type must not appear more than once in a single list. It is valid // for the same MIME type to appear in kPrimaryMappings and // kSecondaryMappings. // // The MIME maps are used for three types of lookups: // // 1) MIME type -> file extension. Implemented as // GetPreferredExtensionForMimeType(). // // Sources are consulted in the following order: // // a) As a special case application/octet-stream is mapped to nothing. Web // sites are supposed to use this MIME type to indicate that the content // is opaque and shouldn't be parsed as any specific type of content. It // doesn't make sense to map this to anything. // // b) The underlying platform. If the operating system has a mapping from // the MIME type to a file extension, then that takes priority. The // platform is assumed to represent the user's preference. // // c) kPrimaryMappings. Order doesn't matter since there should only be at // most one entry per MIME type. // // d) kSecondaryMappings. Again, order doesn't matter. // // 2) File extension -> MIME type. Implemented in GetMimeTypeFromExtension(). // // Sources are considered in the following order: // // a) kPrimaryMappings. Order matters here since file extensions can appear // multiple times on these lists. The first mapping in order of // appearance in the list wins. // // b) Underlying platform. // // c) kSecondaryMappings. Again, the order matters. // // 3) File extension -> Well known MIME type. Implemented as // GetWellKnownMimeTypeFromExtension(). // // This is similar to 2), with the exception that b) is skipped. I.e. Only // considers the hardcoded mappings in kPrimaryMappings and // kSecondaryMappings. // See comments above for details on how this list is used. static const MimeInfo kPrimaryMappings[] = …; // See comments above for details on how this list is used. static const MimeInfo kSecondaryMappings[] = …; // Finds mime type of |ext| from |mappings|. template <size_t num_mappings> static std::optional<std::string_view> FindMimeType( const MimeInfo (&mappings)[num_mappings], const std::string& ext) { … } static base::FilePath::StringType StringToFilePathStringType( std::string_view string_piece) { … } // Helper used in MimeUtil::GetPreferredExtensionForMimeType() to search // preferred extension in MimeInfo arrays. template <size_t num_mappings> static bool FindPreferredExtension(const MimeInfo (&mappings)[num_mappings], const std::string& mime_type, base::FilePath::StringType* result) { … } bool MimeUtil::GetMimeTypeFromExtension(const base::FilePath::StringType& ext, string* result) const { … } bool MimeUtil::GetWellKnownMimeTypeFromExtension( const base::FilePath::StringType& ext, string* result) const { … } bool MimeUtil::GetPreferredExtensionForMimeType( const std::string& mime_type, base::FilePath::StringType* extension) const { … } bool MimeUtil::GetMimeTypeFromFile(const base::FilePath& file_path, string* result) const { … } bool MimeUtil::GetMimeTypeFromExtensionHelper( const base::FilePath::StringType& ext, bool include_platform_types, string* result) const { … } MimeUtil::MimeUtil() = default; // Tests for MIME parameter equality. Each parameter in the |mime_type_pattern| // must be matched by a parameter in the |mime_type|. If there are no // parameters in the pattern, the match is a success. // // According rfc2045 keys of parameters are case-insensitive, while values may // or may not be case-sensitive, but they are usually case-sensitive. So, this // function matches values in *case-sensitive* manner, however note that this // may produce some false negatives. bool MatchesMimeTypeParameters(std::string_view mime_type_pattern, std::string_view mime_type) { … } // This comparison handles absolute maching and also basic // wildcards. The plugin mime types could be: // application/x-foo // application/* // application/*+xml // * // Also tests mime parameters -- all parameters in the pattern must be present // in the tested type for a match to succeed. bool MimeUtil::MatchesMimeType(std::string_view mime_type_pattern, std::string_view mime_type) const { … } bool ParseMimeType(const std::string& type_str, std::string* mime_type, base::StringPairs* params) { … } bool MimeUtil::ParseMimeTypeWithoutParameter(std::string_view type_string, std::string* top_level_type, std::string* subtype) const { … } // See https://www.iana.org/assignments/media-types/media-types.xhtml static const char* const kLegalTopLevelTypes[] = …; bool MimeUtil::IsValidTopLevelMimeType(const std::string& type_string) const { … } //---------------------------------------------------------------------------- // Wrappers for the singleton //---------------------------------------------------------------------------- bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext, std::string* mime_type) { … } bool GetMimeTypeFromFile(const base::FilePath& file_path, std::string* mime_type) { … } bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext, std::string* mime_type) { … } bool GetPreferredExtensionForMimeType(const std::string& mime_type, base::FilePath::StringType* extension) { … } bool MatchesMimeType(std::string_view mime_type_pattern, std::string_view mime_type) { … } bool ParseMimeTypeWithoutParameter(std::string_view type_string, std::string* top_level_type, std::string* subtype) { … } bool IsValidTopLevelMimeType(const std::string& type_string) { … } namespace { // From http://www.w3schools.com/media/media_mimeref.asp and // http://plugindoc.mozdev.org/winmime.php static const char* const kStandardImageTypes[] = …; static const char* const kStandardAudioTypes[] = …; // https://tools.ietf.org/html/rfc8081 static const char* const kStandardFontTypes[] = …; static const char* const kStandardVideoTypes[] = …; struct StandardType { … }; static const StandardType kStandardTypes[] = …; // GetExtensionsFromHardCodedMappings() adds file extensions (without a leading // dot) to the set |extensions|, for all MIME types matching |mime_type|. // // The meaning of |mime_type| depends on the value of |prefix_match|: // // * If |prefix_match = false| then |mime_type| is an exact (case-insensitive) // string such as "text/plain". // // * If |prefix_match = true| then |mime_type| is treated as the prefix for a // (case-insensitive) string. For instance "Text/" would match "text/plain". void GetExtensionsFromHardCodedMappings( base::span<const MimeInfo> mappings, const std::string& mime_type, bool prefix_match, std::unordered_set<base::FilePath::StringType>* extensions) { … } void GetExtensionsHelper( base::span<const char* const> standard_types, const std::string& leading_mime_type, std::unordered_set<base::FilePath::StringType>* extensions) { … } // Note that the elements in the source set will be appended to the target // vector. template <class T> void UnorderedSetToVector(std::unordered_set<T>* source, std::vector<T>* target) { … } // Characters to be used for mime multipart boundary. // // TODO(rsleevi): crbug.com/575779: Follow the spec or fix the spec. // The RFC 2046 spec says the alphanumeric characters plus the // following characters are legal for boundaries: '()+_,-./:=? // However the following characters, though legal, cause some sites // to fail: (),./:=+ constexpr std::string_view kMimeBoundaryCharacters( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); // Size of mime multipart boundary. const size_t kMimeBoundarySize = …; } // namespace void GetExtensionsForMimeType( const std::string& unsafe_mime_type, std::vector<base::FilePath::StringType>* extensions) { … } NET_EXPORT std::string GenerateMimeMultipartBoundary() { … } void AddMultipartValueForUpload(const std::string& value_name, const std::string& value, const std::string& mime_boundary, const std::string& content_type, std::string* post_data) { … } void AddMultipartValueForUploadWithFileName(const std::string& value_name, const std::string& file_name, const std::string& value, const std::string& mime_boundary, const std::string& content_type, std::string* post_data) { … } void AddMultipartFinalDelimiterForUpload(const std::string& mime_boundary, std::string* post_data) { … } // TODO(toyoshim): We may prefer to implement a strict RFC2616 media-type // (https://tools.ietf.org/html/rfc2616#section-3.7) parser. std::optional<std::string> ExtractMimeTypeFromMediaType( const std::string& type_string, bool accept_comma_separated) { … } } // namespace net