|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <iterator> | 
|  | #include <map> | 
|  | #include <string> | 
|  | #include <unordered_set> | 
|  |  | 
|  | #include "base/base64.h" | 
|  | #include "base/lazy_instance.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/rand_util.h" | 
|  | #include "base/stl_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/mime_util.h" | 
|  | #include "net/base/platform_mime_util.h" | 
|  | #include "net/http/http_util.h" | 
|  |  | 
|  | using std::string; | 
|  |  | 
|  | namespace net { | 
|  |  | 
|  | // Singleton utility class for mime types. | 
|  | class MimeUtil : public PlatformMimeUtil { | 
|  | public: | 
|  | bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext, | 
|  | std::string* mime_type) const; | 
|  |  | 
|  | bool GetMimeTypeFromFile(const base::FilePath& file_path, | 
|  | std::string* mime_type) const; | 
|  |  | 
|  | bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext, | 
|  | std::string* mime_type) const; | 
|  |  | 
|  | bool MatchesMimeType(const std::string &mime_type_pattern, | 
|  | const std::string &mime_type) const; | 
|  |  | 
|  | bool ParseMimeTypeWithoutParameter(const std::string& type_string, | 
|  | std::string* top_level_type, | 
|  | std::string* subtype) const; | 
|  |  | 
|  | bool IsValidTopLevelMimeType(const std::string& type_string) const; | 
|  |  | 
|  | private: | 
|  | friend struct base::DefaultLazyInstanceTraits<MimeUtil>; | 
|  |  | 
|  | MimeUtil(); | 
|  |  | 
|  | bool GetMimeTypeFromExtensionHelper(const base::FilePath::StringType& ext, | 
|  | bool include_platform_types, | 
|  | std::string* mime_type) const; | 
|  | };  // class MimeUtil | 
|  |  | 
|  | // This variable is Leaky because we need to access it from WorkerPool threads. | 
|  | static base::LazyInstance<MimeUtil>::Leaky g_mime_util = | 
|  | LAZY_INSTANCE_INITIALIZER; | 
|  |  | 
|  | static const MimeInfo kPrimaryMappings[] = { | 
|  | {"text/html", "html,htm,shtml,shtm"}, | 
|  | {"text/css", "css"}, | 
|  | {"text/xml", "xml"}, | 
|  | {"image/gif", "gif"}, | 
|  | {"image/jpeg", "jpeg,jpg"}, | 
|  | {"image/webp", "webp"}, | 
|  | {"image/png", "png"}, | 
|  | {"video/mp4", "mp4,m4v"}, | 
|  | {"audio/x-m4a", "m4a"}, | 
|  | {"audio/mp3", "mp3"}, | 
|  | {"video/ogg", "ogv,ogm"}, | 
|  | {"audio/ogg", "ogg,oga,opus"}, | 
|  | {"video/webm", "webm"}, | 
|  | {"audio/webm", "webm"}, | 
|  | {"audio/wav", "wav"}, | 
|  | {"audio/flac", "flac"}, | 
|  | {"application/xhtml+xml", "xhtml,xht,xhtm"}, | 
|  | {"application/x-chrome-extension", "crx"}, | 
|  | {"multipart/related", "mhtml,mht"}}; | 
|  |  | 
|  | static const MimeInfo kSecondaryMappings[] = { | 
|  | {"application/octet-stream", "exe,com,bin"}, | 
|  | {"application/gzip", "gz,tgz"}, | 
|  | {"application/x-gzip", "gz,tgz"}, | 
|  | {"application/pdf", "pdf"}, | 
|  | {"application/postscript", "ps,eps,ai"}, | 
|  | {"application/javascript", "js"}, | 
|  | {"application/font-woff", "woff"}, | 
|  | {"image/bmp", "bmp"}, | 
|  | {"image/x-icon", "ico"}, | 
|  | {"image/vnd.microsoft.icon", "ico"}, | 
|  | {"image/jpeg", "jfif,pjpeg,pjp"}, | 
|  | {"image/tiff", "tiff,tif"}, | 
|  | {"image/x-xbitmap", "xbm"}, | 
|  | {"image/svg+xml", "svg,svgz"}, | 
|  | {"image/x-png", "png"}, | 
|  | {"message/rfc822", "eml"}, | 
|  | {"text/plain", "txt,text"}, | 
|  | {"text/html", "ehtml"}, | 
|  | {"application/rss+xml", "rss"}, | 
|  | {"application/rdf+xml", "rdf"}, | 
|  | {"text/xml", "xsl,xbl,xslt"}, | 
|  | {"application/vnd.mozilla.xul+xml", "xul"}, | 
|  | {"application/x-shockwave-flash", "swf,swl"}, | 
|  | {"application/pkcs7-mime", "p7m,p7c,p7z"}, | 
|  | {"application/pkcs7-signature", "p7s"}, | 
|  | {"application/x-mpegurl", "m3u8"}, | 
|  | }; | 
|  |  | 
|  | const char* FindMimeType(const MimeInfo* mappings, | 
|  | size_t mappings_len, | 
|  | const std::string& ext) { | 
|  | for (size_t i = 0; i < mappings_len; ++i) { | 
|  | const char* extensions = mappings[i].extensions; | 
|  | for (;;) { | 
|  | size_t end_pos = strcspn(extensions, ","); | 
|  | // The length check is required to prevent the StringPiece below from | 
|  | // including uninitialized memory if ext is longer than extensions. | 
|  | if (end_pos == ext.size() && | 
|  | base::EqualsCaseInsensitiveASCII( | 
|  | base::StringPiece(extensions, ext.size()), ext)) | 
|  | return mappings[i].mime_type; | 
|  | extensions += end_pos; | 
|  | if (!*extensions) | 
|  | break; | 
|  | extensions += 1;  // skip over comma | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | bool MimeUtil::GetMimeTypeFromExtension(const base::FilePath::StringType& ext, | 
|  | string* result) const { | 
|  | return GetMimeTypeFromExtensionHelper(ext, true, result); | 
|  | } | 
|  |  | 
|  | bool MimeUtil::GetWellKnownMimeTypeFromExtension( | 
|  | const base::FilePath::StringType& ext, | 
|  | string* result) const { | 
|  | return GetMimeTypeFromExtensionHelper(ext, false, result); | 
|  | } | 
|  |  | 
|  | bool MimeUtil::GetMimeTypeFromFile(const base::FilePath& file_path, | 
|  | string* result) const { | 
|  | base::FilePath::StringType file_name_str = file_path.Extension(); | 
|  | if (file_name_str.empty()) | 
|  | return false; | 
|  | return GetMimeTypeFromExtension(file_name_str.substr(1), result); | 
|  | } | 
|  |  | 
|  | bool MimeUtil::GetMimeTypeFromExtensionHelper( | 
|  | const base::FilePath::StringType& ext, | 
|  | bool include_platform_types, | 
|  | string* result) const { | 
|  | // Avoids crash when unable to handle a long file path. See crbug.com/48733. | 
|  | const unsigned kMaxFilePathSize = 65536; | 
|  | if (ext.length() > kMaxFilePathSize) | 
|  | return false; | 
|  |  | 
|  | // Reject a string which contains null character. | 
|  | base::FilePath::StringType::size_type nul_pos = | 
|  | ext.find(FILE_PATH_LITERAL('\0')); | 
|  | if (nul_pos != base::FilePath::StringType::npos) | 
|  | return false; | 
|  |  | 
|  | // We implement the same algorithm as Mozilla for mapping a file extension to | 
|  | // a mime type.  That is, we first check a hard-coded list (that cannot be | 
|  | // overridden), and then if not found there, we defer to the system registry. | 
|  | // Finally, we scan a secondary hard-coded list to catch types that we can | 
|  | // deduce but that we also want to allow the OS to override. | 
|  |  | 
|  | base::FilePath path_ext(ext); | 
|  | const string ext_narrow_str = path_ext.AsUTF8Unsafe(); | 
|  | const char* mime_type = FindMimeType( | 
|  | kPrimaryMappings, arraysize(kPrimaryMappings), ext_narrow_str); | 
|  | if (mime_type) { | 
|  | *result = mime_type; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (include_platform_types && GetPlatformMimeTypeFromExtension(ext, result)) | 
|  | return true; | 
|  |  | 
|  | mime_type = FindMimeType(kSecondaryMappings, arraysize(kSecondaryMappings), | 
|  | ext_narrow_str); | 
|  | if (mime_type) { | 
|  | *result = mime_type; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | MimeUtil::MimeUtil() { | 
|  | } | 
|  |  | 
|  | // 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(const std::string& mime_type_pattern, | 
|  | const std::string& mime_type) { | 
|  | typedef std::map<std::string, std::string> StringPairMap; | 
|  |  | 
|  | const std::string::size_type semicolon = mime_type_pattern.find(';'); | 
|  | const std::string::size_type test_semicolon = mime_type.find(';'); | 
|  | if (semicolon != std::string::npos) { | 
|  | if (test_semicolon == std::string::npos) | 
|  | return false; | 
|  |  | 
|  | base::StringPairs pattern_parameters; | 
|  | base::SplitStringIntoKeyValuePairs(mime_type_pattern.substr(semicolon + 1), | 
|  | '=', ';', &pattern_parameters); | 
|  | base::StringPairs test_parameters; | 
|  | base::SplitStringIntoKeyValuePairs(mime_type.substr(test_semicolon + 1), | 
|  | '=', ';', &test_parameters); | 
|  |  | 
|  | // Put the parameters to maps with the keys converted to lower case. | 
|  | StringPairMap pattern_parameter_map; | 
|  | for (const auto& pair : pattern_parameters) { | 
|  | pattern_parameter_map[base::ToLowerASCII(pair.first)] = pair.second; | 
|  | } | 
|  |  | 
|  | StringPairMap test_parameter_map; | 
|  | for (const auto& pair : test_parameters) { | 
|  | test_parameter_map[base::ToLowerASCII(pair.first)] = pair.second; | 
|  | } | 
|  |  | 
|  | if (pattern_parameter_map.size() > test_parameter_map.size()) | 
|  | return false; | 
|  |  | 
|  | for (const auto& parameter_pair : pattern_parameter_map) { | 
|  | const auto& test_parameter_pair_it = | 
|  | test_parameter_map.find(parameter_pair.first); | 
|  | if (test_parameter_pair_it == test_parameter_map.end()) | 
|  | return false; | 
|  | if (parameter_pair.second != test_parameter_pair_it->second) | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // 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(const std::string& mime_type_pattern, | 
|  | const std::string& mime_type) const { | 
|  | if (mime_type_pattern.empty()) | 
|  | return false; | 
|  |  | 
|  | std::string::size_type semicolon = mime_type_pattern.find(';'); | 
|  | const std::string base_pattern(mime_type_pattern.substr(0, semicolon)); | 
|  | semicolon = mime_type.find(';'); | 
|  | const std::string base_type(mime_type.substr(0, semicolon)); | 
|  |  | 
|  | if (base_pattern == "*" || base_pattern == "*/*") | 
|  | return MatchesMimeTypeParameters(mime_type_pattern, mime_type); | 
|  |  | 
|  | const std::string::size_type star = base_pattern.find('*'); | 
|  | if (star == std::string::npos) { | 
|  | if (base::EqualsCaseInsensitiveASCII(base_pattern, base_type)) | 
|  | return MatchesMimeTypeParameters(mime_type_pattern, mime_type); | 
|  | else | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Test length to prevent overlap between |left| and |right|. | 
|  | if (base_type.length() < base_pattern.length() - 1) | 
|  | return false; | 
|  |  | 
|  | base::StringPiece base_pattern_piece(base_pattern); | 
|  | base::StringPiece left(base_pattern_piece.substr(0, star)); | 
|  | base::StringPiece right(base_pattern_piece.substr(star + 1)); | 
|  |  | 
|  | if (!base::StartsWith(base_type, left, base::CompareCase::INSENSITIVE_ASCII)) | 
|  | return false; | 
|  |  | 
|  | if (!right.empty() && | 
|  | !base::EndsWith(base_type, right, base::CompareCase::INSENSITIVE_ASCII)) | 
|  | return false; | 
|  |  | 
|  | return MatchesMimeTypeParameters(mime_type_pattern, mime_type); | 
|  | } | 
|  |  | 
|  | // See http://www.iana.org/assignments/media-types/media-types.xhtml | 
|  | static const char* const legal_top_level_types[] = { | 
|  | "application", | 
|  | "audio", | 
|  | "example", | 
|  | "image", | 
|  | "message", | 
|  | "model", | 
|  | "multipart", | 
|  | "text", | 
|  | "video", | 
|  | }; | 
|  |  | 
|  | bool MimeUtil::ParseMimeTypeWithoutParameter( | 
|  | const std::string& type_string, | 
|  | std::string* top_level_type, | 
|  | std::string* subtype) const { | 
|  | std::vector<std::string> components = base::SplitString( | 
|  | type_string, "/", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | 
|  | if (components.size() != 2 || | 
|  | !HttpUtil::IsToken(components[0]) || | 
|  | !HttpUtil::IsToken(components[1])) | 
|  | return false; | 
|  |  | 
|  | if (top_level_type) | 
|  | *top_level_type = components[0]; | 
|  | if (subtype) | 
|  | *subtype = components[1]; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool MimeUtil::IsValidTopLevelMimeType(const std::string& type_string) const { | 
|  | std::string lower_type = base::ToLowerASCII(type_string); | 
|  | for (size_t i = 0; i < arraysize(legal_top_level_types); ++i) { | 
|  | if (lower_type.compare(legal_top_level_types[i]) == 0) | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return type_string.size() > 2 && | 
|  | base::StartsWith(type_string, "x-", | 
|  | base::CompareCase::INSENSITIVE_ASCII); | 
|  | } | 
|  |  | 
|  | //---------------------------------------------------------------------------- | 
|  | // Wrappers for the singleton | 
|  | //---------------------------------------------------------------------------- | 
|  |  | 
|  | bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext, | 
|  | std::string* mime_type) { | 
|  | return g_mime_util.Get().GetMimeTypeFromExtension(ext, mime_type); | 
|  | } | 
|  |  | 
|  | bool GetMimeTypeFromFile(const base::FilePath& file_path, | 
|  | std::string* mime_type) { | 
|  | return g_mime_util.Get().GetMimeTypeFromFile(file_path, mime_type); | 
|  | } | 
|  |  | 
|  | bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext, | 
|  | std::string* mime_type) { | 
|  | return g_mime_util.Get().GetWellKnownMimeTypeFromExtension(ext, mime_type); | 
|  | } | 
|  |  | 
|  | bool GetPreferredExtensionForMimeType(const std::string& mime_type, | 
|  | base::FilePath::StringType* extension) { | 
|  | return g_mime_util.Get().GetPreferredExtensionForMimeType(mime_type, | 
|  | extension); | 
|  | } | 
|  |  | 
|  | bool MatchesMimeType(const std::string& mime_type_pattern, | 
|  | const std::string& mime_type) { | 
|  | return g_mime_util.Get().MatchesMimeType(mime_type_pattern, mime_type); | 
|  | } | 
|  |  | 
|  | bool ParseMimeTypeWithoutParameter(const std::string& type_string, | 
|  | std::string* top_level_type, | 
|  | std::string* subtype) { | 
|  | return g_mime_util.Get().ParseMimeTypeWithoutParameter( | 
|  | type_string, top_level_type, subtype); | 
|  | } | 
|  |  | 
|  | bool IsValidTopLevelMimeType(const std::string& type_string) { | 
|  | return g_mime_util.Get().IsValidTopLevelMimeType(type_string); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // From http://www.w3schools.com/media/media_mimeref.asp and | 
|  | // http://plugindoc.mozdev.org/winmime.php | 
|  | static const char* const kStandardImageTypes[] = { | 
|  | "image/bmp", | 
|  | "image/cis-cod", | 
|  | "image/gif", | 
|  | "image/ief", | 
|  | "image/jpeg", | 
|  | "image/webp", | 
|  | "image/pict", | 
|  | "image/pipeg", | 
|  | "image/png", | 
|  | "image/svg+xml", | 
|  | "image/tiff", | 
|  | "image/vnd.microsoft.icon", | 
|  | "image/x-cmu-raster", | 
|  | "image/x-cmx", | 
|  | "image/x-icon", | 
|  | "image/x-portable-anymap", | 
|  | "image/x-portable-bitmap", | 
|  | "image/x-portable-graymap", | 
|  | "image/x-portable-pixmap", | 
|  | "image/x-rgb", | 
|  | "image/x-xbitmap", | 
|  | "image/x-xpixmap", | 
|  | "image/x-xwindowdump" | 
|  | }; | 
|  | static const char* const kStandardAudioTypes[] = { | 
|  | "audio/aac", | 
|  | "audio/aiff", | 
|  | "audio/amr", | 
|  | "audio/basic", | 
|  | "audio/flac", | 
|  | "audio/midi", | 
|  | "audio/mp3", | 
|  | "audio/mp4", | 
|  | "audio/mpeg", | 
|  | "audio/mpeg3", | 
|  | "audio/ogg", | 
|  | "audio/vorbis", | 
|  | "audio/wav", | 
|  | "audio/webm", | 
|  | "audio/x-m4a", | 
|  | "audio/x-ms-wma", | 
|  | "audio/vnd.rn-realaudio", | 
|  | "audio/vnd.wave" | 
|  | }; | 
|  | static const char* const kStandardVideoTypes[] = { | 
|  | "video/avi", | 
|  | "video/divx", | 
|  | "video/flc", | 
|  | "video/mp4", | 
|  | "video/mpeg", | 
|  | "video/ogg", | 
|  | "video/quicktime", | 
|  | "video/sd-video", | 
|  | "video/webm", | 
|  | "video/x-dv", | 
|  | "video/x-m4v", | 
|  | "video/x-mpeg", | 
|  | "video/x-ms-asf", | 
|  | "video/x-ms-wmv" | 
|  | }; | 
|  |  | 
|  | struct StandardType { | 
|  | const char* const leading_mime_type; | 
|  | const char* const* standard_types; | 
|  | size_t standard_types_len; | 
|  | }; | 
|  | static const StandardType kStandardTypes[] = { | 
|  | { "image/", kStandardImageTypes, arraysize(kStandardImageTypes) }, | 
|  | { "audio/", kStandardAudioTypes, arraysize(kStandardAudioTypes) }, | 
|  | { "video/", kStandardVideoTypes, arraysize(kStandardVideoTypes) }, | 
|  | { NULL, NULL, 0 } | 
|  | }; | 
|  |  | 
|  | void GetExtensionsFromHardCodedMappings( | 
|  | const MimeInfo* mappings, | 
|  | size_t mappings_len, | 
|  | const std::string& leading_mime_type, | 
|  | std::unordered_set<base::FilePath::StringType>* extensions) { | 
|  | for (size_t i = 0; i < mappings_len; ++i) { | 
|  | if (base::StartsWith(mappings[i].mime_type, leading_mime_type, | 
|  | base::CompareCase::INSENSITIVE_ASCII)) { | 
|  | for (const base::StringPiece& this_extension : base::SplitStringPiece( | 
|  | mappings[i].extensions, ",", base::TRIM_WHITESPACE, | 
|  | base::SPLIT_WANT_ALL)) { | 
|  | #if defined(OS_WIN) | 
|  | extensions->insert(base::UTF8ToUTF16(this_extension)); | 
|  | #else | 
|  | extensions->insert(this_extension.as_string()); | 
|  | #endif | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void GetExtensionsHelper( | 
|  | const char* const* standard_types, | 
|  | size_t standard_types_len, | 
|  | const std::string& leading_mime_type, | 
|  | std::unordered_set<base::FilePath::StringType>* extensions) { | 
|  | for (size_t i = 0; i < standard_types_len; ++i) { | 
|  | g_mime_util.Get().GetPlatformExtensionsForMimeType(standard_types[i], | 
|  | extensions); | 
|  | } | 
|  |  | 
|  | // Also look up the extensions from hard-coded mappings in case that some | 
|  | // supported extensions are not registered in the system registry, like ogg. | 
|  | GetExtensionsFromHardCodedMappings(kPrimaryMappings, | 
|  | arraysize(kPrimaryMappings), | 
|  | leading_mime_type, extensions); | 
|  |  | 
|  | GetExtensionsFromHardCodedMappings(kSecondaryMappings, | 
|  | arraysize(kSecondaryMappings), | 
|  | leading_mime_type, 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) { | 
|  | size_t old_target_size = target->size(); | 
|  | target->resize(old_target_size + source->size()); | 
|  | size_t i = 0; | 
|  | for (typename std::unordered_set<T>::iterator iter = source->begin(); | 
|  | iter != source->end(); ++iter, ++i) | 
|  | (*target)[old_target_size + i] = *iter; | 
|  | } | 
|  |  | 
|  | // 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: (),./:=+ | 
|  | const char kMimeBoundaryCharacters[] = | 
|  | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; | 
|  |  | 
|  | // Size of mime multipart boundary. | 
|  | const size_t kMimeBoundarySize = 69; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | void GetExtensionsForMimeType( | 
|  | const std::string& unsafe_mime_type, | 
|  | std::vector<base::FilePath::StringType>* extensions) { | 
|  | if (unsafe_mime_type == "*/*" || unsafe_mime_type == "*") | 
|  | return; | 
|  |  | 
|  | const std::string mime_type = base::ToLowerASCII(unsafe_mime_type); | 
|  | std::unordered_set<base::FilePath::StringType> unique_extensions; | 
|  |  | 
|  | if (base::EndsWith(mime_type, "/*", base::CompareCase::INSENSITIVE_ASCII)) { | 
|  | std::string leading_mime_type = mime_type.substr(0, mime_type.length() - 1); | 
|  |  | 
|  | // Find the matching StandardType from within kStandardTypes, or fall | 
|  | // through to the last (default) StandardType. | 
|  | const StandardType* type = NULL; | 
|  | for (size_t i = 0; i < arraysize(kStandardTypes); ++i) { | 
|  | type = &(kStandardTypes[i]); | 
|  | if (type->leading_mime_type && | 
|  | leading_mime_type == type->leading_mime_type) | 
|  | break; | 
|  | } | 
|  | DCHECK(type); | 
|  | GetExtensionsHelper(type->standard_types, | 
|  | type->standard_types_len, | 
|  | leading_mime_type, | 
|  | &unique_extensions); | 
|  | } else { | 
|  | g_mime_util.Get().GetPlatformExtensionsForMimeType(mime_type, | 
|  | &unique_extensions); | 
|  |  | 
|  | // Also look up the extensions from hard-coded mappings in case that some | 
|  | // supported extensions are not registered in the system registry, like ogg. | 
|  | GetExtensionsFromHardCodedMappings(kPrimaryMappings, | 
|  | arraysize(kPrimaryMappings), mime_type, | 
|  | &unique_extensions); | 
|  |  | 
|  | GetExtensionsFromHardCodedMappings(kSecondaryMappings, | 
|  | arraysize(kSecondaryMappings), mime_type, | 
|  | &unique_extensions); | 
|  | } | 
|  |  | 
|  | UnorderedSetToVector(&unique_extensions, extensions); | 
|  | } | 
|  |  | 
|  | NET_EXPORT std::string GenerateMimeMultipartBoundary() { | 
|  | // Based on RFC 1341, section "7.2.1 Multipart: The common syntax": | 
|  | //   Because encapsulation boundaries must not appear in the body parts being | 
|  | //   encapsulated, a user agent must exercise care to choose a unique | 
|  | //   boundary. The boundary in the example above could have been the result of | 
|  | //   an algorithm designed to produce boundaries with a very low probability | 
|  | //   of already existing in the data to be encapsulated without having to | 
|  | //   prescan the data. | 
|  | //   [...] | 
|  | //   the boundary parameter [...] consists of 1 to 70 characters from a set of | 
|  | //   characters known to be very robust through email gateways, and NOT ending | 
|  | //   with white space. | 
|  | //   [...] | 
|  | //   boundary := 0*69<bchars> bcharsnospace | 
|  | //   bchars := bcharsnospace / " " | 
|  | //   bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / "+" / | 
|  | //            "_" / "," / "-" / "." / "/" / ":" / "=" / "?" | 
|  |  | 
|  | std::string result; | 
|  | result.reserve(kMimeBoundarySize); | 
|  | result.append("----MultipartBoundary--"); | 
|  | while (result.size() < (kMimeBoundarySize - 4)) { | 
|  | // Subtract 2 from the array size to 1) exclude '\0', and 2) turn the size | 
|  | // into the last index. | 
|  | const int last_char_index = sizeof(kMimeBoundaryCharacters) - 2; | 
|  | char c = kMimeBoundaryCharacters[base::RandInt(0, last_char_index)]; | 
|  | result.push_back(c); | 
|  | } | 
|  | result.append("----"); | 
|  |  | 
|  | // Not a strict requirement - documentation only. | 
|  | DCHECK_EQ(kMimeBoundarySize, result.size()); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | DCHECK(post_data); | 
|  | // First line is the boundary. | 
|  | post_data->append("--" + mime_boundary + "\r\n"); | 
|  | // Next line is the Content-disposition. | 
|  | post_data->append("Content-Disposition: form-data; name=\"" + | 
|  | value_name + "\"\r\n"); | 
|  | if (!content_type.empty()) { | 
|  | // If Content-type is specified, the next line is that. | 
|  | post_data->append("Content-Type: " + content_type + "\r\n"); | 
|  | } | 
|  | // Leave an empty line and append the value. | 
|  | post_data->append("\r\n" + value + "\r\n"); | 
|  | } | 
|  |  | 
|  | void AddMultipartFinalDelimiterForUpload(const std::string& mime_boundary, | 
|  | std::string* post_data) { | 
|  | DCHECK(post_data); | 
|  | post_data->append("--" + mime_boundary + "--\r\n"); | 
|  | } | 
|  |  | 
|  | }  // namespace net |