1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "net/base/mime_util.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include <algorithm>
8*6777b538SAndroid Build Coastguard Worker #include <iterator>
9*6777b538SAndroid Build Coastguard Worker #include <map>
10*6777b538SAndroid Build Coastguard Worker #include <string>
11*6777b538SAndroid Build Coastguard Worker #include <string_view>
12*6777b538SAndroid Build Coastguard Worker #include <unordered_set>
13*6777b538SAndroid Build Coastguard Worker
14*6777b538SAndroid Build Coastguard Worker #include "base/base64.h"
15*6777b538SAndroid Build Coastguard Worker #include "base/check_op.h"
16*6777b538SAndroid Build Coastguard Worker #include "base/containers/span.h"
17*6777b538SAndroid Build Coastguard Worker #include "base/lazy_instance.h"
18*6777b538SAndroid Build Coastguard Worker #include "base/rand_util.h"
19*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_number_conversions.h"
20*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_split.h"
21*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
22*6777b538SAndroid Build Coastguard Worker #include "base/strings/utf_string_conversions.h"
23*6777b538SAndroid Build Coastguard Worker #include "build/build_config.h"
24*6777b538SAndroid Build Coastguard Worker #include "net/base/platform_mime_util.h"
25*6777b538SAndroid Build Coastguard Worker #include "net/http/http_util.h"
26*6777b538SAndroid Build Coastguard Worker
27*6777b538SAndroid Build Coastguard Worker using std::string;
28*6777b538SAndroid Build Coastguard Worker
29*6777b538SAndroid Build Coastguard Worker namespace net {
30*6777b538SAndroid Build Coastguard Worker
31*6777b538SAndroid Build Coastguard Worker // Singleton utility class for mime types.
32*6777b538SAndroid Build Coastguard Worker class MimeUtil : public PlatformMimeUtil {
33*6777b538SAndroid Build Coastguard Worker public:
34*6777b538SAndroid Build Coastguard Worker bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext,
35*6777b538SAndroid Build Coastguard Worker std::string* mime_type) const;
36*6777b538SAndroid Build Coastguard Worker
37*6777b538SAndroid Build Coastguard Worker bool GetMimeTypeFromFile(const base::FilePath& file_path,
38*6777b538SAndroid Build Coastguard Worker std::string* mime_type) const;
39*6777b538SAndroid Build Coastguard Worker
40*6777b538SAndroid Build Coastguard Worker bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext,
41*6777b538SAndroid Build Coastguard Worker std::string* mime_type) const;
42*6777b538SAndroid Build Coastguard Worker
43*6777b538SAndroid Build Coastguard Worker bool GetPreferredExtensionForMimeType(
44*6777b538SAndroid Build Coastguard Worker const std::string& mime_type,
45*6777b538SAndroid Build Coastguard Worker base::FilePath::StringType* extension) const;
46*6777b538SAndroid Build Coastguard Worker
47*6777b538SAndroid Build Coastguard Worker bool MatchesMimeType(const std::string& mime_type_pattern,
48*6777b538SAndroid Build Coastguard Worker const std::string& mime_type) const;
49*6777b538SAndroid Build Coastguard Worker
50*6777b538SAndroid Build Coastguard Worker bool ParseMimeTypeWithoutParameter(std::string_view type_string,
51*6777b538SAndroid Build Coastguard Worker std::string* top_level_type,
52*6777b538SAndroid Build Coastguard Worker std::string* subtype) const;
53*6777b538SAndroid Build Coastguard Worker
54*6777b538SAndroid Build Coastguard Worker bool IsValidTopLevelMimeType(const std::string& type_string) const;
55*6777b538SAndroid Build Coastguard Worker
56*6777b538SAndroid Build Coastguard Worker private:
57*6777b538SAndroid Build Coastguard Worker friend struct base::LazyInstanceTraitsBase<MimeUtil>;
58*6777b538SAndroid Build Coastguard Worker
59*6777b538SAndroid Build Coastguard Worker MimeUtil();
60*6777b538SAndroid Build Coastguard Worker
61*6777b538SAndroid Build Coastguard Worker bool GetMimeTypeFromExtensionHelper(const base::FilePath::StringType& ext,
62*6777b538SAndroid Build Coastguard Worker bool include_platform_types,
63*6777b538SAndroid Build Coastguard Worker std::string* mime_type) const;
64*6777b538SAndroid Build Coastguard Worker }; // class MimeUtil
65*6777b538SAndroid Build Coastguard Worker
66*6777b538SAndroid Build Coastguard Worker // This variable is Leaky because we need to access it from WorkerPool threads.
67*6777b538SAndroid Build Coastguard Worker static base::LazyInstance<MimeUtil>::Leaky g_mime_util =
68*6777b538SAndroid Build Coastguard Worker LAZY_INSTANCE_INITIALIZER;
69*6777b538SAndroid Build Coastguard Worker
70*6777b538SAndroid Build Coastguard Worker struct MimeInfo {
71*6777b538SAndroid Build Coastguard Worker const char* const mime_type;
72*6777b538SAndroid Build Coastguard Worker
73*6777b538SAndroid Build Coastguard Worker // Comma-separated list of possible extensions for the type. The first
74*6777b538SAndroid Build Coastguard Worker // extension is considered preferred.
75*6777b538SAndroid Build Coastguard Worker const char* const extensions;
76*6777b538SAndroid Build Coastguard Worker };
77*6777b538SAndroid Build Coastguard Worker
78*6777b538SAndroid Build Coastguard Worker // How to use the MIME maps
79*6777b538SAndroid Build Coastguard Worker // ------------------------
80*6777b538SAndroid Build Coastguard Worker // READ THIS BEFORE MODIFYING THE MIME MAPPINGS BELOW.
81*6777b538SAndroid Build Coastguard Worker //
82*6777b538SAndroid Build Coastguard Worker // There are two hardcoded mappings from MIME types: kPrimaryMappings and
83*6777b538SAndroid Build Coastguard Worker // kSecondaryMappings.
84*6777b538SAndroid Build Coastguard Worker //
85*6777b538SAndroid Build Coastguard Worker // kPrimaryMappings:
86*6777b538SAndroid Build Coastguard Worker //
87*6777b538SAndroid Build Coastguard Worker // Use this for mappings that are critical to the web platform. Mappings you
88*6777b538SAndroid Build Coastguard Worker // add to this list take priority over the underlying platform when converting
89*6777b538SAndroid Build Coastguard Worker // from file extension -> MIME type. Thus file extensions listed here will
90*6777b538SAndroid Build Coastguard Worker // work consistently across platforms.
91*6777b538SAndroid Build Coastguard Worker //
92*6777b538SAndroid Build Coastguard Worker // kSecondaryMappings:
93*6777b538SAndroid Build Coastguard Worker //
94*6777b538SAndroid Build Coastguard Worker // Use this for mappings that must exist, but can be overridden by user
95*6777b538SAndroid Build Coastguard Worker // preferences.
96*6777b538SAndroid Build Coastguard Worker //
97*6777b538SAndroid Build Coastguard Worker // The following applies to both lists:
98*6777b538SAndroid Build Coastguard Worker //
99*6777b538SAndroid Build Coastguard Worker // * The same extension can appear multiple times in the same list under
100*6777b538SAndroid Build Coastguard Worker // different MIME types. Extensions that appear earlier take precedence over
101*6777b538SAndroid Build Coastguard Worker // those that appear later.
102*6777b538SAndroid Build Coastguard Worker //
103*6777b538SAndroid Build Coastguard Worker // * A MIME type must not appear more than once in a single list. It is valid
104*6777b538SAndroid Build Coastguard Worker // for the same MIME type to appear in kPrimaryMappings and
105*6777b538SAndroid Build Coastguard Worker // kSecondaryMappings.
106*6777b538SAndroid Build Coastguard Worker //
107*6777b538SAndroid Build Coastguard Worker // The MIME maps are used for three types of lookups:
108*6777b538SAndroid Build Coastguard Worker //
109*6777b538SAndroid Build Coastguard Worker // 1) MIME type -> file extension. Implemented as
110*6777b538SAndroid Build Coastguard Worker // GetPreferredExtensionForMimeType().
111*6777b538SAndroid Build Coastguard Worker //
112*6777b538SAndroid Build Coastguard Worker // Sources are consulted in the following order:
113*6777b538SAndroid Build Coastguard Worker //
114*6777b538SAndroid Build Coastguard Worker // a) As a special case application/octet-stream is mapped to nothing. Web
115*6777b538SAndroid Build Coastguard Worker // sites are supposed to use this MIME type to indicate that the content
116*6777b538SAndroid Build Coastguard Worker // is opaque and shouldn't be parsed as any specific type of content. It
117*6777b538SAndroid Build Coastguard Worker // doesn't make sense to map this to anything.
118*6777b538SAndroid Build Coastguard Worker //
119*6777b538SAndroid Build Coastguard Worker // b) The underlying platform. If the operating system has a mapping from
120*6777b538SAndroid Build Coastguard Worker // the MIME type to a file extension, then that takes priority. The
121*6777b538SAndroid Build Coastguard Worker // platform is assumed to represent the user's preference.
122*6777b538SAndroid Build Coastguard Worker //
123*6777b538SAndroid Build Coastguard Worker // c) kPrimaryMappings. Order doesn't matter since there should only be at
124*6777b538SAndroid Build Coastguard Worker // most one entry per MIME type.
125*6777b538SAndroid Build Coastguard Worker //
126*6777b538SAndroid Build Coastguard Worker // d) kSecondaryMappings. Again, order doesn't matter.
127*6777b538SAndroid Build Coastguard Worker //
128*6777b538SAndroid Build Coastguard Worker // 2) File extension -> MIME type. Implemented in GetMimeTypeFromExtension().
129*6777b538SAndroid Build Coastguard Worker //
130*6777b538SAndroid Build Coastguard Worker // Sources are considered in the following order:
131*6777b538SAndroid Build Coastguard Worker //
132*6777b538SAndroid Build Coastguard Worker // a) kPrimaryMappings. Order matters here since file extensions can appear
133*6777b538SAndroid Build Coastguard Worker // multiple times on these lists. The first mapping in order of
134*6777b538SAndroid Build Coastguard Worker // appearance in the list wins.
135*6777b538SAndroid Build Coastguard Worker //
136*6777b538SAndroid Build Coastguard Worker // b) Underlying platform.
137*6777b538SAndroid Build Coastguard Worker //
138*6777b538SAndroid Build Coastguard Worker // c) kSecondaryMappings. Again, the order matters.
139*6777b538SAndroid Build Coastguard Worker //
140*6777b538SAndroid Build Coastguard Worker // 3) File extension -> Well known MIME type. Implemented as
141*6777b538SAndroid Build Coastguard Worker // GetWellKnownMimeTypeFromExtension().
142*6777b538SAndroid Build Coastguard Worker //
143*6777b538SAndroid Build Coastguard Worker // This is similar to 2), with the exception that b) is skipped. I.e. Only
144*6777b538SAndroid Build Coastguard Worker // considers the hardcoded mappings in kPrimaryMappings and
145*6777b538SAndroid Build Coastguard Worker // kSecondaryMappings.
146*6777b538SAndroid Build Coastguard Worker
147*6777b538SAndroid Build Coastguard Worker // See comments above for details on how this list is used.
148*6777b538SAndroid Build Coastguard Worker static const MimeInfo kPrimaryMappings[] = {
149*6777b538SAndroid Build Coastguard Worker // Must precede audio/webm .
150*6777b538SAndroid Build Coastguard Worker {"video/webm", "webm"},
151*6777b538SAndroid Build Coastguard Worker
152*6777b538SAndroid Build Coastguard Worker // Must precede audio/mp3
153*6777b538SAndroid Build Coastguard Worker {"audio/mpeg", "mp3"},
154*6777b538SAndroid Build Coastguard Worker
155*6777b538SAndroid Build Coastguard Worker {"application/wasm", "wasm"},
156*6777b538SAndroid Build Coastguard Worker {"application/x-chrome-extension", "crx"},
157*6777b538SAndroid Build Coastguard Worker {"application/xhtml+xml", "xhtml,xht,xhtm"},
158*6777b538SAndroid Build Coastguard Worker {"audio/flac", "flac"},
159*6777b538SAndroid Build Coastguard Worker {"audio/mp3", "mp3"},
160*6777b538SAndroid Build Coastguard Worker {"audio/ogg", "ogg,oga,opus"},
161*6777b538SAndroid Build Coastguard Worker {"audio/wav", "wav"},
162*6777b538SAndroid Build Coastguard Worker {"audio/webm", "webm"},
163*6777b538SAndroid Build Coastguard Worker {"audio/x-m4a", "m4a"},
164*6777b538SAndroid Build Coastguard Worker {"image/avif", "avif"},
165*6777b538SAndroid Build Coastguard Worker {"image/gif", "gif"},
166*6777b538SAndroid Build Coastguard Worker {"image/jpeg", "jpeg,jpg"},
167*6777b538SAndroid Build Coastguard Worker {"image/png", "png"},
168*6777b538SAndroid Build Coastguard Worker {"image/apng", "png,apng"},
169*6777b538SAndroid Build Coastguard Worker {"image/svg+xml", "svg,svgz"},
170*6777b538SAndroid Build Coastguard Worker {"image/webp", "webp"},
171*6777b538SAndroid Build Coastguard Worker {"multipart/related", "mht,mhtml"},
172*6777b538SAndroid Build Coastguard Worker {"text/css", "css"},
173*6777b538SAndroid Build Coastguard Worker {"text/html", "html,htm,shtml,shtm"},
174*6777b538SAndroid Build Coastguard Worker {"text/javascript", "js,mjs"},
175*6777b538SAndroid Build Coastguard Worker {"text/xml", "xml"},
176*6777b538SAndroid Build Coastguard Worker {"video/mp4", "mp4,m4v"},
177*6777b538SAndroid Build Coastguard Worker {"video/ogg", "ogv,ogm"},
178*6777b538SAndroid Build Coastguard Worker
179*6777b538SAndroid Build Coastguard Worker // This is a primary mapping (overrides the platform) rather than secondary
180*6777b538SAndroid Build Coastguard Worker // to work around an issue when Excel is installed on Windows. Excel
181*6777b538SAndroid Build Coastguard Worker // registers csv as application/vnd.ms-excel instead of text/csv from RFC
182*6777b538SAndroid Build Coastguard Worker // 4180. See https://crbug.com/139105.
183*6777b538SAndroid Build Coastguard Worker {"text/csv", "csv"},
184*6777b538SAndroid Build Coastguard Worker };
185*6777b538SAndroid Build Coastguard Worker
186*6777b538SAndroid Build Coastguard Worker // See comments above for details on how this list is used.
187*6777b538SAndroid Build Coastguard Worker static const MimeInfo kSecondaryMappings[] = {
188*6777b538SAndroid Build Coastguard Worker // Must precede image/vnd.microsoft.icon .
189*6777b538SAndroid Build Coastguard Worker {"image/x-icon", "ico"},
190*6777b538SAndroid Build Coastguard Worker
191*6777b538SAndroid Build Coastguard Worker {"application/epub+zip", "epub"},
192*6777b538SAndroid Build Coastguard Worker {"application/font-woff", "woff"},
193*6777b538SAndroid Build Coastguard Worker {"application/gzip", "gz,tgz"},
194*6777b538SAndroid Build Coastguard Worker {"application/javascript", "js"},
195*6777b538SAndroid Build Coastguard Worker {"application/json", "json"}, // Per http://www.ietf.org/rfc/rfc4627.txt.
196*6777b538SAndroid Build Coastguard Worker {"application/msword", "doc,dot"},
197*6777b538SAndroid Build Coastguard Worker {"application/octet-stream", "bin,exe,com"},
198*6777b538SAndroid Build Coastguard Worker {"application/pdf", "pdf"},
199*6777b538SAndroid Build Coastguard Worker {"application/pkcs7-mime", "p7m,p7c,p7z"},
200*6777b538SAndroid Build Coastguard Worker {"application/pkcs7-signature", "p7s"},
201*6777b538SAndroid Build Coastguard Worker {"application/postscript", "ps,eps,ai"},
202*6777b538SAndroid Build Coastguard Worker {"application/rdf+xml", "rdf"},
203*6777b538SAndroid Build Coastguard Worker {"application/rss+xml", "rss"},
204*6777b538SAndroid Build Coastguard Worker {"application/rtf", "rtf"},
205*6777b538SAndroid Build Coastguard Worker {"application/vnd.android.package-archive", "apk"},
206*6777b538SAndroid Build Coastguard Worker {"application/vnd.mozilla.xul+xml", "xul"},
207*6777b538SAndroid Build Coastguard Worker {"application/vnd.ms-excel", "xls"},
208*6777b538SAndroid Build Coastguard Worker {"application/vnd.ms-powerpoint", "ppt"},
209*6777b538SAndroid Build Coastguard Worker {"application/"
210*6777b538SAndroid Build Coastguard Worker "vnd.openxmlformats-officedocument.presentationml.presentation",
211*6777b538SAndroid Build Coastguard Worker "pptx"},
212*6777b538SAndroid Build Coastguard Worker {"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
213*6777b538SAndroid Build Coastguard Worker "xlsx"},
214*6777b538SAndroid Build Coastguard Worker {"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
215*6777b538SAndroid Build Coastguard Worker "docx"},
216*6777b538SAndroid Build Coastguard Worker {"application/x-gzip", "gz,tgz"},
217*6777b538SAndroid Build Coastguard Worker {"application/x-mpegurl", "m3u8"},
218*6777b538SAndroid Build Coastguard Worker {"application/x-shockwave-flash", "swf,swl"},
219*6777b538SAndroid Build Coastguard Worker {"application/x-tar", "tar"},
220*6777b538SAndroid Build Coastguard Worker {"application/x-x509-ca-cert", "cer,crt"},
221*6777b538SAndroid Build Coastguard Worker {"application/zip", "zip"},
222*6777b538SAndroid Build Coastguard Worker // This is the platform mapping on recent versions of Windows 10.
223*6777b538SAndroid Build Coastguard Worker {"audio/webm", "weba"},
224*6777b538SAndroid Build Coastguard Worker {"image/bmp", "bmp"},
225*6777b538SAndroid Build Coastguard Worker {"image/jpeg", "jfif,pjpeg,pjp"},
226*6777b538SAndroid Build Coastguard Worker {"image/tiff", "tiff,tif"},
227*6777b538SAndroid Build Coastguard Worker {"image/vnd.microsoft.icon", "ico"},
228*6777b538SAndroid Build Coastguard Worker {"image/x-png", "png"},
229*6777b538SAndroid Build Coastguard Worker {"image/x-xbitmap", "xbm"},
230*6777b538SAndroid Build Coastguard Worker {"message/rfc822", "eml"},
231*6777b538SAndroid Build Coastguard Worker {"text/calendar", "ics"},
232*6777b538SAndroid Build Coastguard Worker {"text/html", "ehtml"},
233*6777b538SAndroid Build Coastguard Worker {"text/plain", "txt,text"},
234*6777b538SAndroid Build Coastguard Worker {"text/x-sh", "sh"},
235*6777b538SAndroid Build Coastguard Worker {"text/xml", "xsl,xbl,xslt"},
236*6777b538SAndroid Build Coastguard Worker {"video/mpeg", "mpeg,mpg"},
237*6777b538SAndroid Build Coastguard Worker };
238*6777b538SAndroid Build Coastguard Worker
239*6777b538SAndroid Build Coastguard Worker // Finds mime type of |ext| from |mappings|.
240*6777b538SAndroid Build Coastguard Worker template <size_t num_mappings>
FindMimeType(const MimeInfo (& mappings)[num_mappings],const std::string & ext)241*6777b538SAndroid Build Coastguard Worker static const char* FindMimeType(const MimeInfo (&mappings)[num_mappings],
242*6777b538SAndroid Build Coastguard Worker const std::string& ext) {
243*6777b538SAndroid Build Coastguard Worker for (const auto& mapping : mappings) {
244*6777b538SAndroid Build Coastguard Worker const char* extensions = mapping.extensions;
245*6777b538SAndroid Build Coastguard Worker for (;;) {
246*6777b538SAndroid Build Coastguard Worker size_t end_pos = strcspn(extensions, ",");
247*6777b538SAndroid Build Coastguard Worker // The length check is required to prevent the StringPiece below from
248*6777b538SAndroid Build Coastguard Worker // including uninitialized memory if ext is longer than extensions.
249*6777b538SAndroid Build Coastguard Worker if (end_pos == ext.size() &&
250*6777b538SAndroid Build Coastguard Worker base::EqualsCaseInsensitiveASCII(
251*6777b538SAndroid Build Coastguard Worker std::string_view(extensions, ext.size()), ext)) {
252*6777b538SAndroid Build Coastguard Worker return mapping.mime_type;
253*6777b538SAndroid Build Coastguard Worker }
254*6777b538SAndroid Build Coastguard Worker extensions += end_pos;
255*6777b538SAndroid Build Coastguard Worker if (!*extensions)
256*6777b538SAndroid Build Coastguard Worker break;
257*6777b538SAndroid Build Coastguard Worker extensions += 1; // skip over comma
258*6777b538SAndroid Build Coastguard Worker }
259*6777b538SAndroid Build Coastguard Worker }
260*6777b538SAndroid Build Coastguard Worker return nullptr;
261*6777b538SAndroid Build Coastguard Worker }
262*6777b538SAndroid Build Coastguard Worker
StringToFilePathStringType(std::string_view string_piece)263*6777b538SAndroid Build Coastguard Worker static base::FilePath::StringType StringToFilePathStringType(
264*6777b538SAndroid Build Coastguard Worker std::string_view string_piece) {
265*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
266*6777b538SAndroid Build Coastguard Worker return base::UTF8ToWide(string_piece);
267*6777b538SAndroid Build Coastguard Worker #else
268*6777b538SAndroid Build Coastguard Worker return std::string(string_piece);
269*6777b538SAndroid Build Coastguard Worker #endif
270*6777b538SAndroid Build Coastguard Worker }
271*6777b538SAndroid Build Coastguard Worker
272*6777b538SAndroid Build Coastguard Worker // Helper used in MimeUtil::GetPreferredExtensionForMimeType() to search
273*6777b538SAndroid Build Coastguard Worker // preferred extension in MimeInfo arrays.
274*6777b538SAndroid Build Coastguard Worker template <size_t num_mappings>
FindPreferredExtension(const MimeInfo (& mappings)[num_mappings],const std::string & mime_type,base::FilePath::StringType * result)275*6777b538SAndroid Build Coastguard Worker static bool FindPreferredExtension(const MimeInfo (&mappings)[num_mappings],
276*6777b538SAndroid Build Coastguard Worker const std::string& mime_type,
277*6777b538SAndroid Build Coastguard Worker base::FilePath::StringType* result) {
278*6777b538SAndroid Build Coastguard Worker // There is no preferred extension for "application/octet-stream".
279*6777b538SAndroid Build Coastguard Worker if (mime_type == "application/octet-stream")
280*6777b538SAndroid Build Coastguard Worker return false;
281*6777b538SAndroid Build Coastguard Worker
282*6777b538SAndroid Build Coastguard Worker for (const auto& mapping : mappings) {
283*6777b538SAndroid Build Coastguard Worker if (mapping.mime_type == mime_type) {
284*6777b538SAndroid Build Coastguard Worker const char* extensions = mapping.extensions;
285*6777b538SAndroid Build Coastguard Worker const char* extension_end = strchr(extensions, ',');
286*6777b538SAndroid Build Coastguard Worker size_t len =
287*6777b538SAndroid Build Coastguard Worker extension_end ? extension_end - extensions : strlen(extensions);
288*6777b538SAndroid Build Coastguard Worker *result = StringToFilePathStringType(std::string_view(extensions, len));
289*6777b538SAndroid Build Coastguard Worker return true;
290*6777b538SAndroid Build Coastguard Worker }
291*6777b538SAndroid Build Coastguard Worker }
292*6777b538SAndroid Build Coastguard Worker return false;
293*6777b538SAndroid Build Coastguard Worker }
294*6777b538SAndroid Build Coastguard Worker
GetMimeTypeFromExtension(const base::FilePath::StringType & ext,string * result) const295*6777b538SAndroid Build Coastguard Worker bool MimeUtil::GetMimeTypeFromExtension(const base::FilePath::StringType& ext,
296*6777b538SAndroid Build Coastguard Worker string* result) const {
297*6777b538SAndroid Build Coastguard Worker return GetMimeTypeFromExtensionHelper(ext, true, result);
298*6777b538SAndroid Build Coastguard Worker }
299*6777b538SAndroid Build Coastguard Worker
GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType & ext,string * result) const300*6777b538SAndroid Build Coastguard Worker bool MimeUtil::GetWellKnownMimeTypeFromExtension(
301*6777b538SAndroid Build Coastguard Worker const base::FilePath::StringType& ext,
302*6777b538SAndroid Build Coastguard Worker string* result) const {
303*6777b538SAndroid Build Coastguard Worker return GetMimeTypeFromExtensionHelper(ext, false, result);
304*6777b538SAndroid Build Coastguard Worker }
305*6777b538SAndroid Build Coastguard Worker
GetPreferredExtensionForMimeType(const std::string & mime_type,base::FilePath::StringType * extension) const306*6777b538SAndroid Build Coastguard Worker bool MimeUtil::GetPreferredExtensionForMimeType(
307*6777b538SAndroid Build Coastguard Worker const std::string& mime_type,
308*6777b538SAndroid Build Coastguard Worker base::FilePath::StringType* extension) const {
309*6777b538SAndroid Build Coastguard Worker // Search the MIME type in the platform DB first, then in kPrimaryMappings and
310*6777b538SAndroid Build Coastguard Worker // kSecondaryMappings.
311*6777b538SAndroid Build Coastguard Worker return GetPlatformPreferredExtensionForMimeType(mime_type, extension) ||
312*6777b538SAndroid Build Coastguard Worker FindPreferredExtension(kPrimaryMappings, mime_type, extension) ||
313*6777b538SAndroid Build Coastguard Worker FindPreferredExtension(kSecondaryMappings, mime_type, extension);
314*6777b538SAndroid Build Coastguard Worker }
315*6777b538SAndroid Build Coastguard Worker
GetMimeTypeFromFile(const base::FilePath & file_path,string * result) const316*6777b538SAndroid Build Coastguard Worker bool MimeUtil::GetMimeTypeFromFile(const base::FilePath& file_path,
317*6777b538SAndroid Build Coastguard Worker string* result) const {
318*6777b538SAndroid Build Coastguard Worker base::FilePath::StringType file_name_str = file_path.Extension();
319*6777b538SAndroid Build Coastguard Worker if (file_name_str.empty())
320*6777b538SAndroid Build Coastguard Worker return false;
321*6777b538SAndroid Build Coastguard Worker return GetMimeTypeFromExtension(file_name_str.substr(1), result);
322*6777b538SAndroid Build Coastguard Worker }
323*6777b538SAndroid Build Coastguard Worker
GetMimeTypeFromExtensionHelper(const base::FilePath::StringType & ext,bool include_platform_types,string * result) const324*6777b538SAndroid Build Coastguard Worker bool MimeUtil::GetMimeTypeFromExtensionHelper(
325*6777b538SAndroid Build Coastguard Worker const base::FilePath::StringType& ext,
326*6777b538SAndroid Build Coastguard Worker bool include_platform_types,
327*6777b538SAndroid Build Coastguard Worker string* result) const {
328*6777b538SAndroid Build Coastguard Worker DCHECK(ext.empty() || ext[0] != '.')
329*6777b538SAndroid Build Coastguard Worker << "extension passed in must not include leading dot";
330*6777b538SAndroid Build Coastguard Worker
331*6777b538SAndroid Build Coastguard Worker // Avoids crash when unable to handle a long file path. See crbug.com/48733.
332*6777b538SAndroid Build Coastguard Worker const unsigned kMaxFilePathSize = 65536;
333*6777b538SAndroid Build Coastguard Worker if (ext.length() > kMaxFilePathSize)
334*6777b538SAndroid Build Coastguard Worker return false;
335*6777b538SAndroid Build Coastguard Worker
336*6777b538SAndroid Build Coastguard Worker // Reject a string which contains null character.
337*6777b538SAndroid Build Coastguard Worker base::FilePath::StringType::size_type nul_pos =
338*6777b538SAndroid Build Coastguard Worker ext.find(FILE_PATH_LITERAL('\0'));
339*6777b538SAndroid Build Coastguard Worker if (nul_pos != base::FilePath::StringType::npos)
340*6777b538SAndroid Build Coastguard Worker return false;
341*6777b538SAndroid Build Coastguard Worker
342*6777b538SAndroid Build Coastguard Worker // We implement the same algorithm as Mozilla for mapping a file extension to
343*6777b538SAndroid Build Coastguard Worker // a mime type. That is, we first check a hard-coded list (that cannot be
344*6777b538SAndroid Build Coastguard Worker // overridden), and then if not found there, we defer to the system registry.
345*6777b538SAndroid Build Coastguard Worker // Finally, we scan a secondary hard-coded list to catch types that we can
346*6777b538SAndroid Build Coastguard Worker // deduce but that we also want to allow the OS to override.
347*6777b538SAndroid Build Coastguard Worker
348*6777b538SAndroid Build Coastguard Worker base::FilePath path_ext(ext);
349*6777b538SAndroid Build Coastguard Worker const string ext_narrow_str = path_ext.AsUTF8Unsafe();
350*6777b538SAndroid Build Coastguard Worker const char* mime_type = FindMimeType(kPrimaryMappings, ext_narrow_str);
351*6777b538SAndroid Build Coastguard Worker if (mime_type) {
352*6777b538SAndroid Build Coastguard Worker *result = mime_type;
353*6777b538SAndroid Build Coastguard Worker return true;
354*6777b538SAndroid Build Coastguard Worker }
355*6777b538SAndroid Build Coastguard Worker
356*6777b538SAndroid Build Coastguard Worker if (include_platform_types && GetPlatformMimeTypeFromExtension(ext, result))
357*6777b538SAndroid Build Coastguard Worker return true;
358*6777b538SAndroid Build Coastguard Worker
359*6777b538SAndroid Build Coastguard Worker mime_type = FindMimeType(kSecondaryMappings, ext_narrow_str);
360*6777b538SAndroid Build Coastguard Worker if (mime_type) {
361*6777b538SAndroid Build Coastguard Worker *result = mime_type;
362*6777b538SAndroid Build Coastguard Worker return true;
363*6777b538SAndroid Build Coastguard Worker }
364*6777b538SAndroid Build Coastguard Worker
365*6777b538SAndroid Build Coastguard Worker return false;
366*6777b538SAndroid Build Coastguard Worker }
367*6777b538SAndroid Build Coastguard Worker
368*6777b538SAndroid Build Coastguard Worker MimeUtil::MimeUtil() = default;
369*6777b538SAndroid Build Coastguard Worker
370*6777b538SAndroid Build Coastguard Worker // Tests for MIME parameter equality. Each parameter in the |mime_type_pattern|
371*6777b538SAndroid Build Coastguard Worker // must be matched by a parameter in the |mime_type|. If there are no
372*6777b538SAndroid Build Coastguard Worker // parameters in the pattern, the match is a success.
373*6777b538SAndroid Build Coastguard Worker //
374*6777b538SAndroid Build Coastguard Worker // According rfc2045 keys of parameters are case-insensitive, while values may
375*6777b538SAndroid Build Coastguard Worker // or may not be case-sensitive, but they are usually case-sensitive. So, this
376*6777b538SAndroid Build Coastguard Worker // function matches values in *case-sensitive* manner, however note that this
377*6777b538SAndroid Build Coastguard Worker // may produce some false negatives.
MatchesMimeTypeParameters(const std::string & mime_type_pattern,const std::string & mime_type)378*6777b538SAndroid Build Coastguard Worker bool MatchesMimeTypeParameters(const std::string& mime_type_pattern,
379*6777b538SAndroid Build Coastguard Worker const std::string& mime_type) {
380*6777b538SAndroid Build Coastguard Worker typedef std::map<std::string, std::string> StringPairMap;
381*6777b538SAndroid Build Coastguard Worker
382*6777b538SAndroid Build Coastguard Worker const std::string::size_type semicolon = mime_type_pattern.find(';');
383*6777b538SAndroid Build Coastguard Worker const std::string::size_type test_semicolon = mime_type.find(';');
384*6777b538SAndroid Build Coastguard Worker if (semicolon != std::string::npos) {
385*6777b538SAndroid Build Coastguard Worker if (test_semicolon == std::string::npos)
386*6777b538SAndroid Build Coastguard Worker return false;
387*6777b538SAndroid Build Coastguard Worker
388*6777b538SAndroid Build Coastguard Worker base::StringPairs pattern_parameters;
389*6777b538SAndroid Build Coastguard Worker base::SplitStringIntoKeyValuePairs(mime_type_pattern.substr(semicolon + 1),
390*6777b538SAndroid Build Coastguard Worker '=', ';', &pattern_parameters);
391*6777b538SAndroid Build Coastguard Worker base::StringPairs test_parameters;
392*6777b538SAndroid Build Coastguard Worker base::SplitStringIntoKeyValuePairs(mime_type.substr(test_semicolon + 1),
393*6777b538SAndroid Build Coastguard Worker '=', ';', &test_parameters);
394*6777b538SAndroid Build Coastguard Worker
395*6777b538SAndroid Build Coastguard Worker // Put the parameters to maps with the keys converted to lower case.
396*6777b538SAndroid Build Coastguard Worker StringPairMap pattern_parameter_map;
397*6777b538SAndroid Build Coastguard Worker for (const auto& pair : pattern_parameters) {
398*6777b538SAndroid Build Coastguard Worker pattern_parameter_map[base::ToLowerASCII(pair.first)] = pair.second;
399*6777b538SAndroid Build Coastguard Worker }
400*6777b538SAndroid Build Coastguard Worker
401*6777b538SAndroid Build Coastguard Worker StringPairMap test_parameter_map;
402*6777b538SAndroid Build Coastguard Worker for (const auto& pair : test_parameters) {
403*6777b538SAndroid Build Coastguard Worker test_parameter_map[base::ToLowerASCII(pair.first)] = pair.second;
404*6777b538SAndroid Build Coastguard Worker }
405*6777b538SAndroid Build Coastguard Worker
406*6777b538SAndroid Build Coastguard Worker if (pattern_parameter_map.size() > test_parameter_map.size())
407*6777b538SAndroid Build Coastguard Worker return false;
408*6777b538SAndroid Build Coastguard Worker
409*6777b538SAndroid Build Coastguard Worker for (const auto& parameter_pair : pattern_parameter_map) {
410*6777b538SAndroid Build Coastguard Worker const auto& test_parameter_pair_it =
411*6777b538SAndroid Build Coastguard Worker test_parameter_map.find(parameter_pair.first);
412*6777b538SAndroid Build Coastguard Worker if (test_parameter_pair_it == test_parameter_map.end())
413*6777b538SAndroid Build Coastguard Worker return false;
414*6777b538SAndroid Build Coastguard Worker if (parameter_pair.second != test_parameter_pair_it->second)
415*6777b538SAndroid Build Coastguard Worker return false;
416*6777b538SAndroid Build Coastguard Worker }
417*6777b538SAndroid Build Coastguard Worker }
418*6777b538SAndroid Build Coastguard Worker
419*6777b538SAndroid Build Coastguard Worker return true;
420*6777b538SAndroid Build Coastguard Worker }
421*6777b538SAndroid Build Coastguard Worker
422*6777b538SAndroid Build Coastguard Worker // This comparison handles absolute maching and also basic
423*6777b538SAndroid Build Coastguard Worker // wildcards. The plugin mime types could be:
424*6777b538SAndroid Build Coastguard Worker // application/x-foo
425*6777b538SAndroid Build Coastguard Worker // application/*
426*6777b538SAndroid Build Coastguard Worker // application/*+xml
427*6777b538SAndroid Build Coastguard Worker // *
428*6777b538SAndroid Build Coastguard Worker // Also tests mime parameters -- all parameters in the pattern must be present
429*6777b538SAndroid Build Coastguard Worker // in the tested type for a match to succeed.
MatchesMimeType(const std::string & mime_type_pattern,const std::string & mime_type) const430*6777b538SAndroid Build Coastguard Worker bool MimeUtil::MatchesMimeType(const std::string& mime_type_pattern,
431*6777b538SAndroid Build Coastguard Worker const std::string& mime_type) const {
432*6777b538SAndroid Build Coastguard Worker if (mime_type_pattern.empty())
433*6777b538SAndroid Build Coastguard Worker return false;
434*6777b538SAndroid Build Coastguard Worker
435*6777b538SAndroid Build Coastguard Worker std::string::size_type semicolon = mime_type_pattern.find(';');
436*6777b538SAndroid Build Coastguard Worker const std::string base_pattern(mime_type_pattern.substr(0, semicolon));
437*6777b538SAndroid Build Coastguard Worker semicolon = mime_type.find(';');
438*6777b538SAndroid Build Coastguard Worker const std::string base_type(mime_type.substr(0, semicolon));
439*6777b538SAndroid Build Coastguard Worker
440*6777b538SAndroid Build Coastguard Worker if (base_pattern == "*" || base_pattern == "*/*")
441*6777b538SAndroid Build Coastguard Worker return MatchesMimeTypeParameters(mime_type_pattern, mime_type);
442*6777b538SAndroid Build Coastguard Worker
443*6777b538SAndroid Build Coastguard Worker const std::string::size_type star = base_pattern.find('*');
444*6777b538SAndroid Build Coastguard Worker if (star == std::string::npos) {
445*6777b538SAndroid Build Coastguard Worker if (base::EqualsCaseInsensitiveASCII(base_pattern, base_type))
446*6777b538SAndroid Build Coastguard Worker return MatchesMimeTypeParameters(mime_type_pattern, mime_type);
447*6777b538SAndroid Build Coastguard Worker else
448*6777b538SAndroid Build Coastguard Worker return false;
449*6777b538SAndroid Build Coastguard Worker }
450*6777b538SAndroid Build Coastguard Worker
451*6777b538SAndroid Build Coastguard Worker // Test length to prevent overlap between |left| and |right|.
452*6777b538SAndroid Build Coastguard Worker if (base_type.length() < base_pattern.length() - 1)
453*6777b538SAndroid Build Coastguard Worker return false;
454*6777b538SAndroid Build Coastguard Worker
455*6777b538SAndroid Build Coastguard Worker std::string_view base_pattern_piece(base_pattern);
456*6777b538SAndroid Build Coastguard Worker std::string_view left(base_pattern_piece.substr(0, star));
457*6777b538SAndroid Build Coastguard Worker std::string_view right(base_pattern_piece.substr(star + 1));
458*6777b538SAndroid Build Coastguard Worker
459*6777b538SAndroid Build Coastguard Worker if (!base::StartsWith(base_type, left, base::CompareCase::INSENSITIVE_ASCII))
460*6777b538SAndroid Build Coastguard Worker return false;
461*6777b538SAndroid Build Coastguard Worker
462*6777b538SAndroid Build Coastguard Worker if (!right.empty() &&
463*6777b538SAndroid Build Coastguard Worker !base::EndsWith(base_type, right, base::CompareCase::INSENSITIVE_ASCII))
464*6777b538SAndroid Build Coastguard Worker return false;
465*6777b538SAndroid Build Coastguard Worker
466*6777b538SAndroid Build Coastguard Worker return MatchesMimeTypeParameters(mime_type_pattern, mime_type);
467*6777b538SAndroid Build Coastguard Worker }
468*6777b538SAndroid Build Coastguard Worker
ParseMimeType(const std::string & type_str,std::string * mime_type,base::StringPairs * params)469*6777b538SAndroid Build Coastguard Worker bool ParseMimeType(const std::string& type_str,
470*6777b538SAndroid Build Coastguard Worker std::string* mime_type,
471*6777b538SAndroid Build Coastguard Worker base::StringPairs* params) {
472*6777b538SAndroid Build Coastguard Worker // Trim leading and trailing whitespace from type. We include '(' in
473*6777b538SAndroid Build Coastguard Worker // the trailing trim set to catch media-type comments, which are not at all
474*6777b538SAndroid Build Coastguard Worker // standard, but may occur in rare cases.
475*6777b538SAndroid Build Coastguard Worker size_t type_val = type_str.find_first_not_of(HTTP_LWS);
476*6777b538SAndroid Build Coastguard Worker type_val = std::min(type_val, type_str.length());
477*6777b538SAndroid Build Coastguard Worker size_t type_end = type_str.find_first_of(HTTP_LWS ";(", type_val);
478*6777b538SAndroid Build Coastguard Worker if (type_end == std::string::npos)
479*6777b538SAndroid Build Coastguard Worker type_end = type_str.length();
480*6777b538SAndroid Build Coastguard Worker
481*6777b538SAndroid Build Coastguard Worker // Reject a mime-type if it does not include a slash.
482*6777b538SAndroid Build Coastguard Worker size_t slash_pos = type_str.find_first_of('/');
483*6777b538SAndroid Build Coastguard Worker if (slash_pos == std::string::npos || slash_pos > type_end)
484*6777b538SAndroid Build Coastguard Worker return false;
485*6777b538SAndroid Build Coastguard Worker if (mime_type)
486*6777b538SAndroid Build Coastguard Worker *mime_type = type_str.substr(type_val, type_end - type_val);
487*6777b538SAndroid Build Coastguard Worker
488*6777b538SAndroid Build Coastguard Worker // Iterate over parameters. Can't split the string around semicolons
489*6777b538SAndroid Build Coastguard Worker // preemptively because quoted strings may include semicolons. Mostly matches
490*6777b538SAndroid Build Coastguard Worker // logic in https://mimesniff.spec.whatwg.org/. Main differences: Does not
491*6777b538SAndroid Build Coastguard Worker // validate characters are HTTP token code points / HTTP quoted-string token
492*6777b538SAndroid Build Coastguard Worker // code points, and ignores spaces after "=" in parameters.
493*6777b538SAndroid Build Coastguard Worker if (params)
494*6777b538SAndroid Build Coastguard Worker params->clear();
495*6777b538SAndroid Build Coastguard Worker std::string::size_type offset = type_str.find_first_of(';', type_end);
496*6777b538SAndroid Build Coastguard Worker while (offset < type_str.size()) {
497*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(';', type_str[offset]);
498*6777b538SAndroid Build Coastguard Worker // Trim off the semicolon.
499*6777b538SAndroid Build Coastguard Worker ++offset;
500*6777b538SAndroid Build Coastguard Worker
501*6777b538SAndroid Build Coastguard Worker // Trim off any following spaces.
502*6777b538SAndroid Build Coastguard Worker offset = type_str.find_first_not_of(HTTP_LWS, offset);
503*6777b538SAndroid Build Coastguard Worker std::string::size_type param_name_start = offset;
504*6777b538SAndroid Build Coastguard Worker
505*6777b538SAndroid Build Coastguard Worker // Extend parameter name until run into a semicolon or equals sign. Per
506*6777b538SAndroid Build Coastguard Worker // spec, trailing spaces are not removed.
507*6777b538SAndroid Build Coastguard Worker offset = type_str.find_first_of(";=", offset);
508*6777b538SAndroid Build Coastguard Worker
509*6777b538SAndroid Build Coastguard Worker // Nothing more to do if at end of string, or if there's no parameter
510*6777b538SAndroid Build Coastguard Worker // value, since names without values aren't allowed.
511*6777b538SAndroid Build Coastguard Worker if (offset == std::string::npos || type_str[offset] == ';')
512*6777b538SAndroid Build Coastguard Worker continue;
513*6777b538SAndroid Build Coastguard Worker
514*6777b538SAndroid Build Coastguard Worker auto param_name = base::MakeStringPiece(type_str.begin() + param_name_start,
515*6777b538SAndroid Build Coastguard Worker type_str.begin() + offset);
516*6777b538SAndroid Build Coastguard Worker
517*6777b538SAndroid Build Coastguard Worker // Now parse the value.
518*6777b538SAndroid Build Coastguard Worker DCHECK_EQ('=', type_str[offset]);
519*6777b538SAndroid Build Coastguard Worker // Trim off the '='.
520*6777b538SAndroid Build Coastguard Worker offset++;
521*6777b538SAndroid Build Coastguard Worker
522*6777b538SAndroid Build Coastguard Worker // Remove leading spaces. This violates the spec, though it matches
523*6777b538SAndroid Build Coastguard Worker // pre-existing behavior.
524*6777b538SAndroid Build Coastguard Worker //
525*6777b538SAndroid Build Coastguard Worker // TODO(mmenke): Consider doing this (only?) after parsing quotes, which
526*6777b538SAndroid Build Coastguard Worker // seems to align more with the spec - not the content-type spec, but the
527*6777b538SAndroid Build Coastguard Worker // GET spec's way of getting an encoding, and the spec for handling
528*6777b538SAndroid Build Coastguard Worker // boundary values as well.
529*6777b538SAndroid Build Coastguard Worker // See https://encoding.spec.whatwg.org/#names-and-labels.
530*6777b538SAndroid Build Coastguard Worker offset = type_str.find_first_not_of(HTTP_LWS, offset);
531*6777b538SAndroid Build Coastguard Worker
532*6777b538SAndroid Build Coastguard Worker std::string param_value;
533*6777b538SAndroid Build Coastguard Worker if (offset == std::string::npos || type_str[offset] == ';') {
534*6777b538SAndroid Build Coastguard Worker // Nothing to do here - an unquoted string of only whitespace should be
535*6777b538SAndroid Build Coastguard Worker // skipped.
536*6777b538SAndroid Build Coastguard Worker continue;
537*6777b538SAndroid Build Coastguard Worker } else if (type_str[offset] != '"') {
538*6777b538SAndroid Build Coastguard Worker // If the first character is not a quotation mark, copy data directly.
539*6777b538SAndroid Build Coastguard Worker std::string::size_type value_start = offset;
540*6777b538SAndroid Build Coastguard Worker offset = type_str.find_first_of(';', offset);
541*6777b538SAndroid Build Coastguard Worker std::string::size_type value_end = offset;
542*6777b538SAndroid Build Coastguard Worker
543*6777b538SAndroid Build Coastguard Worker // Remove terminal whitespace. If ran off the end of the string, have to
544*6777b538SAndroid Build Coastguard Worker // update |value_end| first.
545*6777b538SAndroid Build Coastguard Worker if (value_end == std::string::npos)
546*6777b538SAndroid Build Coastguard Worker value_end = type_str.size();
547*6777b538SAndroid Build Coastguard Worker while (value_end > value_start &&
548*6777b538SAndroid Build Coastguard Worker HttpUtil::IsLWS(type_str[value_end - 1])) {
549*6777b538SAndroid Build Coastguard Worker --value_end;
550*6777b538SAndroid Build Coastguard Worker }
551*6777b538SAndroid Build Coastguard Worker
552*6777b538SAndroid Build Coastguard Worker param_value = type_str.substr(value_start, value_end - value_start);
553*6777b538SAndroid Build Coastguard Worker } else {
554*6777b538SAndroid Build Coastguard Worker // Otherwise, append data, with special handling for backslashes, until
555*6777b538SAndroid Build Coastguard Worker // a close quote. Do not trim whitespace for quoted-string.
556*6777b538SAndroid Build Coastguard Worker
557*6777b538SAndroid Build Coastguard Worker // Skip open quote.
558*6777b538SAndroid Build Coastguard Worker DCHECK_EQ('"', type_str[offset]);
559*6777b538SAndroid Build Coastguard Worker ++offset;
560*6777b538SAndroid Build Coastguard Worker
561*6777b538SAndroid Build Coastguard Worker while (offset < type_str.size() && type_str[offset] != '"') {
562*6777b538SAndroid Build Coastguard Worker // Skip over backslash and append the next character, when not at
563*6777b538SAndroid Build Coastguard Worker // the end of the string. Otherwise, copy the next character (Which may
564*6777b538SAndroid Build Coastguard Worker // be a backslash).
565*6777b538SAndroid Build Coastguard Worker if (type_str[offset] == '\\' && offset + 1 < type_str.size()) {
566*6777b538SAndroid Build Coastguard Worker ++offset;
567*6777b538SAndroid Build Coastguard Worker }
568*6777b538SAndroid Build Coastguard Worker param_value += type_str[offset];
569*6777b538SAndroid Build Coastguard Worker ++offset;
570*6777b538SAndroid Build Coastguard Worker }
571*6777b538SAndroid Build Coastguard Worker
572*6777b538SAndroid Build Coastguard Worker offset = type_str.find_first_of(';', offset);
573*6777b538SAndroid Build Coastguard Worker }
574*6777b538SAndroid Build Coastguard Worker if (params)
575*6777b538SAndroid Build Coastguard Worker params->emplace_back(param_name, param_value);
576*6777b538SAndroid Build Coastguard Worker }
577*6777b538SAndroid Build Coastguard Worker return true;
578*6777b538SAndroid Build Coastguard Worker }
579*6777b538SAndroid Build Coastguard Worker
ParseMimeTypeWithoutParameter(std::string_view type_string,std::string * top_level_type,std::string * subtype) const580*6777b538SAndroid Build Coastguard Worker bool MimeUtil::ParseMimeTypeWithoutParameter(std::string_view type_string,
581*6777b538SAndroid Build Coastguard Worker std::string* top_level_type,
582*6777b538SAndroid Build Coastguard Worker std::string* subtype) const {
583*6777b538SAndroid Build Coastguard Worker std::vector<std::string_view> components = base::SplitStringPiece(
584*6777b538SAndroid Build Coastguard Worker type_string, "/", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
585*6777b538SAndroid Build Coastguard Worker if (components.size() != 2)
586*6777b538SAndroid Build Coastguard Worker return false;
587*6777b538SAndroid Build Coastguard Worker components[0] = TrimWhitespaceASCII(components[0], base::TRIM_LEADING);
588*6777b538SAndroid Build Coastguard Worker components[1] = TrimWhitespaceASCII(components[1], base::TRIM_TRAILING);
589*6777b538SAndroid Build Coastguard Worker if (!HttpUtil::IsToken(components[0]) || !HttpUtil::IsToken(components[1]))
590*6777b538SAndroid Build Coastguard Worker return false;
591*6777b538SAndroid Build Coastguard Worker
592*6777b538SAndroid Build Coastguard Worker if (top_level_type)
593*6777b538SAndroid Build Coastguard Worker top_level_type->assign(std::string(components[0]));
594*6777b538SAndroid Build Coastguard Worker
595*6777b538SAndroid Build Coastguard Worker if (subtype)
596*6777b538SAndroid Build Coastguard Worker subtype->assign(std::string(components[1]));
597*6777b538SAndroid Build Coastguard Worker
598*6777b538SAndroid Build Coastguard Worker return true;
599*6777b538SAndroid Build Coastguard Worker }
600*6777b538SAndroid Build Coastguard Worker
601*6777b538SAndroid Build Coastguard Worker // See https://www.iana.org/assignments/media-types/media-types.xhtml
602*6777b538SAndroid Build Coastguard Worker static const char* const kLegalTopLevelTypes[] = {
603*6777b538SAndroid Build Coastguard Worker "application", "audio", "example", "font", "image",
604*6777b538SAndroid Build Coastguard Worker "message", "model", "multipart", "text", "video",
605*6777b538SAndroid Build Coastguard Worker };
606*6777b538SAndroid Build Coastguard Worker
IsValidTopLevelMimeType(const std::string & type_string) const607*6777b538SAndroid Build Coastguard Worker bool MimeUtil::IsValidTopLevelMimeType(const std::string& type_string) const {
608*6777b538SAndroid Build Coastguard Worker std::string lower_type = base::ToLowerASCII(type_string);
609*6777b538SAndroid Build Coastguard Worker for (const char* const legal_type : kLegalTopLevelTypes) {
610*6777b538SAndroid Build Coastguard Worker if (lower_type.compare(legal_type) == 0)
611*6777b538SAndroid Build Coastguard Worker return true;
612*6777b538SAndroid Build Coastguard Worker }
613*6777b538SAndroid Build Coastguard Worker
614*6777b538SAndroid Build Coastguard Worker return type_string.size() > 2 &&
615*6777b538SAndroid Build Coastguard Worker base::StartsWith(type_string, "x-",
616*6777b538SAndroid Build Coastguard Worker base::CompareCase::INSENSITIVE_ASCII);
617*6777b538SAndroid Build Coastguard Worker }
618*6777b538SAndroid Build Coastguard Worker
619*6777b538SAndroid Build Coastguard Worker //----------------------------------------------------------------------------
620*6777b538SAndroid Build Coastguard Worker // Wrappers for the singleton
621*6777b538SAndroid Build Coastguard Worker //----------------------------------------------------------------------------
622*6777b538SAndroid Build Coastguard Worker
GetMimeTypeFromExtension(const base::FilePath::StringType & ext,std::string * mime_type)623*6777b538SAndroid Build Coastguard Worker bool GetMimeTypeFromExtension(const base::FilePath::StringType& ext,
624*6777b538SAndroid Build Coastguard Worker std::string* mime_type) {
625*6777b538SAndroid Build Coastguard Worker return g_mime_util.Get().GetMimeTypeFromExtension(ext, mime_type);
626*6777b538SAndroid Build Coastguard Worker }
627*6777b538SAndroid Build Coastguard Worker
GetMimeTypeFromFile(const base::FilePath & file_path,std::string * mime_type)628*6777b538SAndroid Build Coastguard Worker bool GetMimeTypeFromFile(const base::FilePath& file_path,
629*6777b538SAndroid Build Coastguard Worker std::string* mime_type) {
630*6777b538SAndroid Build Coastguard Worker return g_mime_util.Get().GetMimeTypeFromFile(file_path, mime_type);
631*6777b538SAndroid Build Coastguard Worker }
632*6777b538SAndroid Build Coastguard Worker
GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType & ext,std::string * mime_type)633*6777b538SAndroid Build Coastguard Worker bool GetWellKnownMimeTypeFromExtension(const base::FilePath::StringType& ext,
634*6777b538SAndroid Build Coastguard Worker std::string* mime_type) {
635*6777b538SAndroid Build Coastguard Worker return g_mime_util.Get().GetWellKnownMimeTypeFromExtension(ext, mime_type);
636*6777b538SAndroid Build Coastguard Worker }
637*6777b538SAndroid Build Coastguard Worker
GetPreferredExtensionForMimeType(const std::string & mime_type,base::FilePath::StringType * extension)638*6777b538SAndroid Build Coastguard Worker bool GetPreferredExtensionForMimeType(const std::string& mime_type,
639*6777b538SAndroid Build Coastguard Worker base::FilePath::StringType* extension) {
640*6777b538SAndroid Build Coastguard Worker return g_mime_util.Get().GetPreferredExtensionForMimeType(mime_type,
641*6777b538SAndroid Build Coastguard Worker extension);
642*6777b538SAndroid Build Coastguard Worker }
643*6777b538SAndroid Build Coastguard Worker
MatchesMimeType(const std::string & mime_type_pattern,const std::string & mime_type)644*6777b538SAndroid Build Coastguard Worker bool MatchesMimeType(const std::string& mime_type_pattern,
645*6777b538SAndroid Build Coastguard Worker const std::string& mime_type) {
646*6777b538SAndroid Build Coastguard Worker return g_mime_util.Get().MatchesMimeType(mime_type_pattern, mime_type);
647*6777b538SAndroid Build Coastguard Worker }
648*6777b538SAndroid Build Coastguard Worker
ParseMimeTypeWithoutParameter(std::string_view type_string,std::string * top_level_type,std::string * subtype)649*6777b538SAndroid Build Coastguard Worker bool ParseMimeTypeWithoutParameter(std::string_view type_string,
650*6777b538SAndroid Build Coastguard Worker std::string* top_level_type,
651*6777b538SAndroid Build Coastguard Worker std::string* subtype) {
652*6777b538SAndroid Build Coastguard Worker return g_mime_util.Get().ParseMimeTypeWithoutParameter(
653*6777b538SAndroid Build Coastguard Worker type_string, top_level_type, subtype);
654*6777b538SAndroid Build Coastguard Worker }
655*6777b538SAndroid Build Coastguard Worker
IsValidTopLevelMimeType(const std::string & type_string)656*6777b538SAndroid Build Coastguard Worker bool IsValidTopLevelMimeType(const std::string& type_string) {
657*6777b538SAndroid Build Coastguard Worker return g_mime_util.Get().IsValidTopLevelMimeType(type_string);
658*6777b538SAndroid Build Coastguard Worker }
659*6777b538SAndroid Build Coastguard Worker
660*6777b538SAndroid Build Coastguard Worker namespace {
661*6777b538SAndroid Build Coastguard Worker
662*6777b538SAndroid Build Coastguard Worker // From http://www.w3schools.com/media/media_mimeref.asp and
663*6777b538SAndroid Build Coastguard Worker // http://plugindoc.mozdev.org/winmime.php
664*6777b538SAndroid Build Coastguard Worker static const char* const kStandardImageTypes[] = {"image/avif",
665*6777b538SAndroid Build Coastguard Worker "image/bmp",
666*6777b538SAndroid Build Coastguard Worker "image/cis-cod",
667*6777b538SAndroid Build Coastguard Worker "image/gif",
668*6777b538SAndroid Build Coastguard Worker "image/ief",
669*6777b538SAndroid Build Coastguard Worker "image/jpeg",
670*6777b538SAndroid Build Coastguard Worker "image/webp",
671*6777b538SAndroid Build Coastguard Worker "image/pict",
672*6777b538SAndroid Build Coastguard Worker "image/pipeg",
673*6777b538SAndroid Build Coastguard Worker "image/png",
674*6777b538SAndroid Build Coastguard Worker "image/svg+xml",
675*6777b538SAndroid Build Coastguard Worker "image/tiff",
676*6777b538SAndroid Build Coastguard Worker "image/vnd.microsoft.icon",
677*6777b538SAndroid Build Coastguard Worker "image/x-cmu-raster",
678*6777b538SAndroid Build Coastguard Worker "image/x-cmx",
679*6777b538SAndroid Build Coastguard Worker "image/x-icon",
680*6777b538SAndroid Build Coastguard Worker "image/x-portable-anymap",
681*6777b538SAndroid Build Coastguard Worker "image/x-portable-bitmap",
682*6777b538SAndroid Build Coastguard Worker "image/x-portable-graymap",
683*6777b538SAndroid Build Coastguard Worker "image/x-portable-pixmap",
684*6777b538SAndroid Build Coastguard Worker "image/x-rgb",
685*6777b538SAndroid Build Coastguard Worker "image/x-xbitmap",
686*6777b538SAndroid Build Coastguard Worker "image/x-xpixmap",
687*6777b538SAndroid Build Coastguard Worker "image/x-xwindowdump"};
688*6777b538SAndroid Build Coastguard Worker static const char* const kStandardAudioTypes[] = {
689*6777b538SAndroid Build Coastguard Worker "audio/aac",
690*6777b538SAndroid Build Coastguard Worker "audio/aiff",
691*6777b538SAndroid Build Coastguard Worker "audio/amr",
692*6777b538SAndroid Build Coastguard Worker "audio/basic",
693*6777b538SAndroid Build Coastguard Worker "audio/flac",
694*6777b538SAndroid Build Coastguard Worker "audio/midi",
695*6777b538SAndroid Build Coastguard Worker "audio/mp3",
696*6777b538SAndroid Build Coastguard Worker "audio/mp4",
697*6777b538SAndroid Build Coastguard Worker "audio/mpeg",
698*6777b538SAndroid Build Coastguard Worker "audio/mpeg3",
699*6777b538SAndroid Build Coastguard Worker "audio/ogg",
700*6777b538SAndroid Build Coastguard Worker "audio/vorbis",
701*6777b538SAndroid Build Coastguard Worker "audio/wav",
702*6777b538SAndroid Build Coastguard Worker "audio/webm",
703*6777b538SAndroid Build Coastguard Worker "audio/x-m4a",
704*6777b538SAndroid Build Coastguard Worker "audio/x-ms-wma",
705*6777b538SAndroid Build Coastguard Worker "audio/vnd.rn-realaudio",
706*6777b538SAndroid Build Coastguard Worker "audio/vnd.wave"
707*6777b538SAndroid Build Coastguard Worker };
708*6777b538SAndroid Build Coastguard Worker // https://tools.ietf.org/html/rfc8081
709*6777b538SAndroid Build Coastguard Worker static const char* const kStandardFontTypes[] = {
710*6777b538SAndroid Build Coastguard Worker "font/collection", "font/otf", "font/sfnt",
711*6777b538SAndroid Build Coastguard Worker "font/ttf", "font/woff", "font/woff2",
712*6777b538SAndroid Build Coastguard Worker };
713*6777b538SAndroid Build Coastguard Worker static const char* const kStandardVideoTypes[] = {
714*6777b538SAndroid Build Coastguard Worker "video/avi",
715*6777b538SAndroid Build Coastguard Worker "video/divx",
716*6777b538SAndroid Build Coastguard Worker "video/flc",
717*6777b538SAndroid Build Coastguard Worker "video/mp4",
718*6777b538SAndroid Build Coastguard Worker "video/mpeg",
719*6777b538SAndroid Build Coastguard Worker "video/ogg",
720*6777b538SAndroid Build Coastguard Worker "video/quicktime",
721*6777b538SAndroid Build Coastguard Worker "video/sd-video",
722*6777b538SAndroid Build Coastguard Worker "video/webm",
723*6777b538SAndroid Build Coastguard Worker "video/x-dv",
724*6777b538SAndroid Build Coastguard Worker "video/x-m4v",
725*6777b538SAndroid Build Coastguard Worker "video/x-mpeg",
726*6777b538SAndroid Build Coastguard Worker "video/x-ms-asf",
727*6777b538SAndroid Build Coastguard Worker "video/x-ms-wmv"
728*6777b538SAndroid Build Coastguard Worker };
729*6777b538SAndroid Build Coastguard Worker
730*6777b538SAndroid Build Coastguard Worker struct StandardType {
731*6777b538SAndroid Build Coastguard Worker const char* const leading_mime_type;
732*6777b538SAndroid Build Coastguard Worker base::span<const char* const> standard_types;
733*6777b538SAndroid Build Coastguard Worker };
734*6777b538SAndroid Build Coastguard Worker static const StandardType kStandardTypes[] = {{"image/", kStandardImageTypes},
735*6777b538SAndroid Build Coastguard Worker {"audio/", kStandardAudioTypes},
736*6777b538SAndroid Build Coastguard Worker {"font/", kStandardFontTypes},
737*6777b538SAndroid Build Coastguard Worker {"video/", kStandardVideoTypes},
738*6777b538SAndroid Build Coastguard Worker {nullptr, {}}};
739*6777b538SAndroid Build Coastguard Worker
740*6777b538SAndroid Build Coastguard Worker // GetExtensionsFromHardCodedMappings() adds file extensions (without a leading
741*6777b538SAndroid Build Coastguard Worker // dot) to the set |extensions|, for all MIME types matching |mime_type|.
742*6777b538SAndroid Build Coastguard Worker //
743*6777b538SAndroid Build Coastguard Worker // The meaning of |mime_type| depends on the value of |prefix_match|:
744*6777b538SAndroid Build Coastguard Worker //
745*6777b538SAndroid Build Coastguard Worker // * If |prefix_match = false| then |mime_type| is an exact (case-insensitive)
746*6777b538SAndroid Build Coastguard Worker // string such as "text/plain".
747*6777b538SAndroid Build Coastguard Worker //
748*6777b538SAndroid Build Coastguard Worker // * If |prefix_match = true| then |mime_type| is treated as the prefix for a
749*6777b538SAndroid Build Coastguard Worker // (case-insensitive) string. For instance "Text/" would match "text/plain".
GetExtensionsFromHardCodedMappings(base::span<const MimeInfo> mappings,const std::string & mime_type,bool prefix_match,std::unordered_set<base::FilePath::StringType> * extensions)750*6777b538SAndroid Build Coastguard Worker void GetExtensionsFromHardCodedMappings(
751*6777b538SAndroid Build Coastguard Worker base::span<const MimeInfo> mappings,
752*6777b538SAndroid Build Coastguard Worker const std::string& mime_type,
753*6777b538SAndroid Build Coastguard Worker bool prefix_match,
754*6777b538SAndroid Build Coastguard Worker std::unordered_set<base::FilePath::StringType>* extensions) {
755*6777b538SAndroid Build Coastguard Worker for (const auto& mapping : mappings) {
756*6777b538SAndroid Build Coastguard Worker std::string_view cur_mime_type(mapping.mime_type);
757*6777b538SAndroid Build Coastguard Worker
758*6777b538SAndroid Build Coastguard Worker if (base::StartsWith(cur_mime_type, mime_type,
759*6777b538SAndroid Build Coastguard Worker base::CompareCase::INSENSITIVE_ASCII) &&
760*6777b538SAndroid Build Coastguard Worker (prefix_match || (cur_mime_type.length() == mime_type.length()))) {
761*6777b538SAndroid Build Coastguard Worker for (std::string_view this_extension : base::SplitStringPiece(
762*6777b538SAndroid Build Coastguard Worker mapping.extensions, ",", base::TRIM_WHITESPACE,
763*6777b538SAndroid Build Coastguard Worker base::SPLIT_WANT_ALL)) {
764*6777b538SAndroid Build Coastguard Worker extensions->insert(StringToFilePathStringType(this_extension));
765*6777b538SAndroid Build Coastguard Worker }
766*6777b538SAndroid Build Coastguard Worker }
767*6777b538SAndroid Build Coastguard Worker }
768*6777b538SAndroid Build Coastguard Worker }
769*6777b538SAndroid Build Coastguard Worker
GetExtensionsHelper(base::span<const char * const> standard_types,const std::string & leading_mime_type,std::unordered_set<base::FilePath::StringType> * extensions)770*6777b538SAndroid Build Coastguard Worker void GetExtensionsHelper(
771*6777b538SAndroid Build Coastguard Worker base::span<const char* const> standard_types,
772*6777b538SAndroid Build Coastguard Worker const std::string& leading_mime_type,
773*6777b538SAndroid Build Coastguard Worker std::unordered_set<base::FilePath::StringType>* extensions) {
774*6777b538SAndroid Build Coastguard Worker for (auto* standard_type : standard_types) {
775*6777b538SAndroid Build Coastguard Worker g_mime_util.Get().GetPlatformExtensionsForMimeType(standard_type,
776*6777b538SAndroid Build Coastguard Worker extensions);
777*6777b538SAndroid Build Coastguard Worker }
778*6777b538SAndroid Build Coastguard Worker
779*6777b538SAndroid Build Coastguard Worker // Also look up the extensions from hard-coded mappings in case that some
780*6777b538SAndroid Build Coastguard Worker // supported extensions are not registered in the system registry, like ogg.
781*6777b538SAndroid Build Coastguard Worker GetExtensionsFromHardCodedMappings(kPrimaryMappings, leading_mime_type, true,
782*6777b538SAndroid Build Coastguard Worker extensions);
783*6777b538SAndroid Build Coastguard Worker
784*6777b538SAndroid Build Coastguard Worker GetExtensionsFromHardCodedMappings(kSecondaryMappings, leading_mime_type,
785*6777b538SAndroid Build Coastguard Worker true, extensions);
786*6777b538SAndroid Build Coastguard Worker }
787*6777b538SAndroid Build Coastguard Worker
788*6777b538SAndroid Build Coastguard Worker // Note that the elements in the source set will be appended to the target
789*6777b538SAndroid Build Coastguard Worker // vector.
790*6777b538SAndroid Build Coastguard Worker template <class T>
UnorderedSetToVector(std::unordered_set<T> * source,std::vector<T> * target)791*6777b538SAndroid Build Coastguard Worker void UnorderedSetToVector(std::unordered_set<T>* source,
792*6777b538SAndroid Build Coastguard Worker std::vector<T>* target) {
793*6777b538SAndroid Build Coastguard Worker size_t old_target_size = target->size();
794*6777b538SAndroid Build Coastguard Worker target->resize(old_target_size + source->size());
795*6777b538SAndroid Build Coastguard Worker size_t i = 0;
796*6777b538SAndroid Build Coastguard Worker for (auto iter = source->begin(); iter != source->end(); ++iter, ++i)
797*6777b538SAndroid Build Coastguard Worker (*target)[old_target_size + i] = *iter;
798*6777b538SAndroid Build Coastguard Worker }
799*6777b538SAndroid Build Coastguard Worker
800*6777b538SAndroid Build Coastguard Worker // Characters to be used for mime multipart boundary.
801*6777b538SAndroid Build Coastguard Worker //
802*6777b538SAndroid Build Coastguard Worker // TODO(rsleevi): crbug.com/575779: Follow the spec or fix the spec.
803*6777b538SAndroid Build Coastguard Worker // The RFC 2046 spec says the alphanumeric characters plus the
804*6777b538SAndroid Build Coastguard Worker // following characters are legal for boundaries: '()+_,-./:=?
805*6777b538SAndroid Build Coastguard Worker // However the following characters, though legal, cause some sites
806*6777b538SAndroid Build Coastguard Worker // to fail: (),./:=+
807*6777b538SAndroid Build Coastguard Worker constexpr std::string_view kMimeBoundaryCharacters(
808*6777b538SAndroid Build Coastguard Worker "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
809*6777b538SAndroid Build Coastguard Worker
810*6777b538SAndroid Build Coastguard Worker // Size of mime multipart boundary.
811*6777b538SAndroid Build Coastguard Worker const size_t kMimeBoundarySize = 69;
812*6777b538SAndroid Build Coastguard Worker
813*6777b538SAndroid Build Coastguard Worker } // namespace
814*6777b538SAndroid Build Coastguard Worker
GetExtensionsForMimeType(const std::string & unsafe_mime_type,std::vector<base::FilePath::StringType> * extensions)815*6777b538SAndroid Build Coastguard Worker void GetExtensionsForMimeType(
816*6777b538SAndroid Build Coastguard Worker const std::string& unsafe_mime_type,
817*6777b538SAndroid Build Coastguard Worker std::vector<base::FilePath::StringType>* extensions) {
818*6777b538SAndroid Build Coastguard Worker if (unsafe_mime_type == "*/*" || unsafe_mime_type == "*")
819*6777b538SAndroid Build Coastguard Worker return;
820*6777b538SAndroid Build Coastguard Worker
821*6777b538SAndroid Build Coastguard Worker const std::string mime_type = base::ToLowerASCII(unsafe_mime_type);
822*6777b538SAndroid Build Coastguard Worker std::unordered_set<base::FilePath::StringType> unique_extensions;
823*6777b538SAndroid Build Coastguard Worker
824*6777b538SAndroid Build Coastguard Worker if (base::EndsWith(mime_type, "/*", base::CompareCase::INSENSITIVE_ASCII)) {
825*6777b538SAndroid Build Coastguard Worker std::string leading_mime_type = mime_type.substr(0, mime_type.length() - 1);
826*6777b538SAndroid Build Coastguard Worker
827*6777b538SAndroid Build Coastguard Worker // Find the matching StandardType from within kStandardTypes, or fall
828*6777b538SAndroid Build Coastguard Worker // through to the last (default) StandardType.
829*6777b538SAndroid Build Coastguard Worker const StandardType* type = nullptr;
830*6777b538SAndroid Build Coastguard Worker for (const StandardType& standard_type : kStandardTypes) {
831*6777b538SAndroid Build Coastguard Worker type = &standard_type;
832*6777b538SAndroid Build Coastguard Worker if (type->leading_mime_type &&
833*6777b538SAndroid Build Coastguard Worker leading_mime_type == type->leading_mime_type) {
834*6777b538SAndroid Build Coastguard Worker break;
835*6777b538SAndroid Build Coastguard Worker }
836*6777b538SAndroid Build Coastguard Worker }
837*6777b538SAndroid Build Coastguard Worker DCHECK(type);
838*6777b538SAndroid Build Coastguard Worker GetExtensionsHelper(type->standard_types,
839*6777b538SAndroid Build Coastguard Worker leading_mime_type,
840*6777b538SAndroid Build Coastguard Worker &unique_extensions);
841*6777b538SAndroid Build Coastguard Worker } else {
842*6777b538SAndroid Build Coastguard Worker g_mime_util.Get().GetPlatformExtensionsForMimeType(mime_type,
843*6777b538SAndroid Build Coastguard Worker &unique_extensions);
844*6777b538SAndroid Build Coastguard Worker
845*6777b538SAndroid Build Coastguard Worker // Also look up the extensions from hard-coded mappings in case that some
846*6777b538SAndroid Build Coastguard Worker // supported extensions are not registered in the system registry, like ogg.
847*6777b538SAndroid Build Coastguard Worker GetExtensionsFromHardCodedMappings(kPrimaryMappings, mime_type, false,
848*6777b538SAndroid Build Coastguard Worker &unique_extensions);
849*6777b538SAndroid Build Coastguard Worker
850*6777b538SAndroid Build Coastguard Worker GetExtensionsFromHardCodedMappings(kSecondaryMappings, mime_type, false,
851*6777b538SAndroid Build Coastguard Worker &unique_extensions);
852*6777b538SAndroid Build Coastguard Worker }
853*6777b538SAndroid Build Coastguard Worker
854*6777b538SAndroid Build Coastguard Worker UnorderedSetToVector(&unique_extensions, extensions);
855*6777b538SAndroid Build Coastguard Worker }
856*6777b538SAndroid Build Coastguard Worker
GenerateMimeMultipartBoundary()857*6777b538SAndroid Build Coastguard Worker NET_EXPORT std::string GenerateMimeMultipartBoundary() {
858*6777b538SAndroid Build Coastguard Worker // Based on RFC 1341, section "7.2.1 Multipart: The common syntax":
859*6777b538SAndroid Build Coastguard Worker // Because encapsulation boundaries must not appear in the body parts being
860*6777b538SAndroid Build Coastguard Worker // encapsulated, a user agent must exercise care to choose a unique
861*6777b538SAndroid Build Coastguard Worker // boundary. The boundary in the example above could have been the result of
862*6777b538SAndroid Build Coastguard Worker // an algorithm designed to produce boundaries with a very low probability
863*6777b538SAndroid Build Coastguard Worker // of already existing in the data to be encapsulated without having to
864*6777b538SAndroid Build Coastguard Worker // prescan the data.
865*6777b538SAndroid Build Coastguard Worker // [...]
866*6777b538SAndroid Build Coastguard Worker // the boundary parameter [...] consists of 1 to 70 characters from a set of
867*6777b538SAndroid Build Coastguard Worker // characters known to be very robust through email gateways, and NOT ending
868*6777b538SAndroid Build Coastguard Worker // with white space.
869*6777b538SAndroid Build Coastguard Worker // [...]
870*6777b538SAndroid Build Coastguard Worker // boundary := 0*69<bchars> bcharsnospace
871*6777b538SAndroid Build Coastguard Worker // bchars := bcharsnospace / " "
872*6777b538SAndroid Build Coastguard Worker // bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" / "+" /
873*6777b538SAndroid Build Coastguard Worker // "_" / "," / "-" / "." / "/" / ":" / "=" / "?"
874*6777b538SAndroid Build Coastguard Worker
875*6777b538SAndroid Build Coastguard Worker std::string result;
876*6777b538SAndroid Build Coastguard Worker result.reserve(kMimeBoundarySize);
877*6777b538SAndroid Build Coastguard Worker result.append("----MultipartBoundary--");
878*6777b538SAndroid Build Coastguard Worker while (result.size() < (kMimeBoundarySize - 4)) {
879*6777b538SAndroid Build Coastguard Worker char c = kMimeBoundaryCharacters[base::RandInt(
880*6777b538SAndroid Build Coastguard Worker 0, kMimeBoundaryCharacters.size() - 1)];
881*6777b538SAndroid Build Coastguard Worker result.push_back(c);
882*6777b538SAndroid Build Coastguard Worker }
883*6777b538SAndroid Build Coastguard Worker result.append("----");
884*6777b538SAndroid Build Coastguard Worker
885*6777b538SAndroid Build Coastguard Worker // Not a strict requirement - documentation only.
886*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(kMimeBoundarySize, result.size());
887*6777b538SAndroid Build Coastguard Worker
888*6777b538SAndroid Build Coastguard Worker return result;
889*6777b538SAndroid Build Coastguard Worker }
890*6777b538SAndroid Build Coastguard Worker
AddMultipartValueForUpload(const std::string & value_name,const std::string & value,const std::string & mime_boundary,const std::string & content_type,std::string * post_data)891*6777b538SAndroid Build Coastguard Worker void AddMultipartValueForUpload(const std::string& value_name,
892*6777b538SAndroid Build Coastguard Worker const std::string& value,
893*6777b538SAndroid Build Coastguard Worker const std::string& mime_boundary,
894*6777b538SAndroid Build Coastguard Worker const std::string& content_type,
895*6777b538SAndroid Build Coastguard Worker std::string* post_data) {
896*6777b538SAndroid Build Coastguard Worker DCHECK(post_data);
897*6777b538SAndroid Build Coastguard Worker // First line is the boundary.
898*6777b538SAndroid Build Coastguard Worker post_data->append("--" + mime_boundary + "\r\n");
899*6777b538SAndroid Build Coastguard Worker // Next line is the Content-disposition.
900*6777b538SAndroid Build Coastguard Worker post_data->append("Content-Disposition: form-data; name=\"" +
901*6777b538SAndroid Build Coastguard Worker value_name + "\"\r\n");
902*6777b538SAndroid Build Coastguard Worker if (!content_type.empty()) {
903*6777b538SAndroid Build Coastguard Worker // If Content-type is specified, the next line is that.
904*6777b538SAndroid Build Coastguard Worker post_data->append("Content-Type: " + content_type + "\r\n");
905*6777b538SAndroid Build Coastguard Worker }
906*6777b538SAndroid Build Coastguard Worker // Leave an empty line and append the value.
907*6777b538SAndroid Build Coastguard Worker post_data->append("\r\n" + value + "\r\n");
908*6777b538SAndroid Build Coastguard Worker }
909*6777b538SAndroid Build Coastguard Worker
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)910*6777b538SAndroid Build Coastguard Worker void AddMultipartValueForUploadWithFileName(const std::string& value_name,
911*6777b538SAndroid Build Coastguard Worker const std::string& file_name,
912*6777b538SAndroid Build Coastguard Worker const std::string& value,
913*6777b538SAndroid Build Coastguard Worker const std::string& mime_boundary,
914*6777b538SAndroid Build Coastguard Worker const std::string& content_type,
915*6777b538SAndroid Build Coastguard Worker std::string* post_data) {
916*6777b538SAndroid Build Coastguard Worker DCHECK(post_data);
917*6777b538SAndroid Build Coastguard Worker // First line is the boundary.
918*6777b538SAndroid Build Coastguard Worker post_data->append("--" + mime_boundary + "\r\n");
919*6777b538SAndroid Build Coastguard Worker // Next line is the Content-disposition.
920*6777b538SAndroid Build Coastguard Worker post_data->append("Content-Disposition: form-data; name=\"" + value_name +
921*6777b538SAndroid Build Coastguard Worker "\"; filename=\"" + file_name + "\"\r\n");
922*6777b538SAndroid Build Coastguard Worker if (!content_type.empty()) {
923*6777b538SAndroid Build Coastguard Worker // If Content-type is specified, the next line is that.
924*6777b538SAndroid Build Coastguard Worker post_data->append("Content-Type: " + content_type + "\r\n");
925*6777b538SAndroid Build Coastguard Worker }
926*6777b538SAndroid Build Coastguard Worker // Leave an empty line and append the value.
927*6777b538SAndroid Build Coastguard Worker post_data->append("\r\n" + value + "\r\n");
928*6777b538SAndroid Build Coastguard Worker }
929*6777b538SAndroid Build Coastguard Worker
AddMultipartFinalDelimiterForUpload(const std::string & mime_boundary,std::string * post_data)930*6777b538SAndroid Build Coastguard Worker void AddMultipartFinalDelimiterForUpload(const std::string& mime_boundary,
931*6777b538SAndroid Build Coastguard Worker std::string* post_data) {
932*6777b538SAndroid Build Coastguard Worker DCHECK(post_data);
933*6777b538SAndroid Build Coastguard Worker post_data->append("--" + mime_boundary + "--\r\n");
934*6777b538SAndroid Build Coastguard Worker }
935*6777b538SAndroid Build Coastguard Worker
936*6777b538SAndroid Build Coastguard Worker // TODO(toyoshim): We may prefer to implement a strict RFC2616 media-type
937*6777b538SAndroid Build Coastguard Worker // (https://tools.ietf.org/html/rfc2616#section-3.7) parser.
ExtractMimeTypeFromMediaType(const std::string & type_string,bool accept_comma_separated)938*6777b538SAndroid Build Coastguard Worker std::optional<std::string> ExtractMimeTypeFromMediaType(
939*6777b538SAndroid Build Coastguard Worker const std::string& type_string,
940*6777b538SAndroid Build Coastguard Worker bool accept_comma_separated) {
941*6777b538SAndroid Build Coastguard Worker std::string::size_type end = type_string.find(';');
942*6777b538SAndroid Build Coastguard Worker if (accept_comma_separated) {
943*6777b538SAndroid Build Coastguard Worker end = std::min(end, type_string.find(','));
944*6777b538SAndroid Build Coastguard Worker }
945*6777b538SAndroid Build Coastguard Worker std::string top_level_type;
946*6777b538SAndroid Build Coastguard Worker std::string subtype;
947*6777b538SAndroid Build Coastguard Worker if (ParseMimeTypeWithoutParameter(type_string.substr(0, end), &top_level_type,
948*6777b538SAndroid Build Coastguard Worker &subtype)) {
949*6777b538SAndroid Build Coastguard Worker return top_level_type + "/" + subtype;
950*6777b538SAndroid Build Coastguard Worker }
951*6777b538SAndroid Build Coastguard Worker return std::nullopt;
952*6777b538SAndroid Build Coastguard Worker }
953*6777b538SAndroid Build Coastguard Worker
954*6777b538SAndroid Build Coastguard Worker } // namespace net
955