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 // Detecting mime types is a tricky business because we need to balance
6*6777b538SAndroid Build Coastguard Worker // compatibility concerns with security issues. Here is a survey of how other
7*6777b538SAndroid Build Coastguard Worker // browsers behave and then a description of how we intend to behave.
8*6777b538SAndroid Build Coastguard Worker //
9*6777b538SAndroid Build Coastguard Worker // HTML payload, no Content-Type header:
10*6777b538SAndroid Build Coastguard Worker // * IE 7: Render as HTML
11*6777b538SAndroid Build Coastguard Worker // * Firefox 2: Render as HTML
12*6777b538SAndroid Build Coastguard Worker // * Safari 3: Render as HTML
13*6777b538SAndroid Build Coastguard Worker // * Opera 9: Render as HTML
14*6777b538SAndroid Build Coastguard Worker //
15*6777b538SAndroid Build Coastguard Worker // Here the choice seems clear:
16*6777b538SAndroid Build Coastguard Worker // => Chrome: Render as HTML
17*6777b538SAndroid Build Coastguard Worker //
18*6777b538SAndroid Build Coastguard Worker // HTML payload, Content-Type: "text/plain":
19*6777b538SAndroid Build Coastguard Worker // * IE 7: Render as HTML
20*6777b538SAndroid Build Coastguard Worker // * Firefox 2: Render as text
21*6777b538SAndroid Build Coastguard Worker // * Safari 3: Render as text (Note: Safari will Render as HTML if the URL
22*6777b538SAndroid Build Coastguard Worker // has an HTML extension)
23*6777b538SAndroid Build Coastguard Worker // * Opera 9: Render as text
24*6777b538SAndroid Build Coastguard Worker //
25*6777b538SAndroid Build Coastguard Worker // Here we choose to follow the majority (and break some compatibility with IE).
26*6777b538SAndroid Build Coastguard Worker // Many folks dislike IE's behavior here.
27*6777b538SAndroid Build Coastguard Worker // => Chrome: Render as text
28*6777b538SAndroid Build Coastguard Worker // We generalize this as follows. If the Content-Type header is text/plain
29*6777b538SAndroid Build Coastguard Worker // we won't detect dangerous mime types (those that can execute script).
30*6777b538SAndroid Build Coastguard Worker //
31*6777b538SAndroid Build Coastguard Worker // HTML payload, Content-Type: "application/octet-stream":
32*6777b538SAndroid Build Coastguard Worker // * IE 7: Render as HTML
33*6777b538SAndroid Build Coastguard Worker // * Firefox 2: Download as application/octet-stream
34*6777b538SAndroid Build Coastguard Worker // * Safari 3: Render as HTML
35*6777b538SAndroid Build Coastguard Worker // * Opera 9: Render as HTML
36*6777b538SAndroid Build Coastguard Worker //
37*6777b538SAndroid Build Coastguard Worker // We follow Firefox.
38*6777b538SAndroid Build Coastguard Worker // => Chrome: Download as application/octet-stream
39*6777b538SAndroid Build Coastguard Worker // One factor in this decision is that IIS 4 and 5 will send
40*6777b538SAndroid Build Coastguard Worker // application/octet-stream for .xhtml files (because they don't recognize
41*6777b538SAndroid Build Coastguard Worker // the extension). We did some experiments and it looks like this doesn't occur
42*6777b538SAndroid Build Coastguard Worker // very often on the web. We choose the more secure option.
43*6777b538SAndroid Build Coastguard Worker //
44*6777b538SAndroid Build Coastguard Worker // GIF payload, no Content-Type header:
45*6777b538SAndroid Build Coastguard Worker // * IE 7: Render as GIF
46*6777b538SAndroid Build Coastguard Worker // * Firefox 2: Render as GIF
47*6777b538SAndroid Build Coastguard Worker // * Safari 3: Download as Unknown (Note: Safari will Render as GIF if the
48*6777b538SAndroid Build Coastguard Worker // URL has an GIF extension)
49*6777b538SAndroid Build Coastguard Worker // * Opera 9: Render as GIF
50*6777b538SAndroid Build Coastguard Worker //
51*6777b538SAndroid Build Coastguard Worker // The choice is clear.
52*6777b538SAndroid Build Coastguard Worker // => Chrome: Render as GIF
53*6777b538SAndroid Build Coastguard Worker // Once we decide to render HTML without a Content-Type header, there isn't much
54*6777b538SAndroid Build Coastguard Worker // reason not to render GIFs.
55*6777b538SAndroid Build Coastguard Worker //
56*6777b538SAndroid Build Coastguard Worker // GIF payload, Content-Type: "text/plain":
57*6777b538SAndroid Build Coastguard Worker // * IE 7: Render as GIF
58*6777b538SAndroid Build Coastguard Worker // * Firefox 2: Download as application/octet-stream (Note: Firefox will
59*6777b538SAndroid Build Coastguard Worker // Download as GIF if the URL has an GIF extension)
60*6777b538SAndroid Build Coastguard Worker // * Safari 3: Download as Unknown (Note: Safari will Render as GIF if the
61*6777b538SAndroid Build Coastguard Worker // URL has an GIF extension)
62*6777b538SAndroid Build Coastguard Worker // * Opera 9: Render as GIF
63*6777b538SAndroid Build Coastguard Worker //
64*6777b538SAndroid Build Coastguard Worker // Displaying as text/plain makes little sense as the content will look like
65*6777b538SAndroid Build Coastguard Worker // gibberish. Here, we could change our minds and download.
66*6777b538SAndroid Build Coastguard Worker // => Chrome: Render as GIF
67*6777b538SAndroid Build Coastguard Worker //
68*6777b538SAndroid Build Coastguard Worker // GIF payload, Content-Type: "application/octet-stream":
69*6777b538SAndroid Build Coastguard Worker // * IE 7: Render as GIF
70*6777b538SAndroid Build Coastguard Worker // * Firefox 2: Download as application/octet-stream (Note: Firefox will
71*6777b538SAndroid Build Coastguard Worker // Download as GIF if the URL has an GIF extension)
72*6777b538SAndroid Build Coastguard Worker // * Safari 3: Download as Unknown (Note: Safari will Render as GIF if the
73*6777b538SAndroid Build Coastguard Worker // URL has an GIF extension)
74*6777b538SAndroid Build Coastguard Worker // * Opera 9: Render as GIF
75*6777b538SAndroid Build Coastguard Worker //
76*6777b538SAndroid Build Coastguard Worker // We used to render as GIF here, but the problem is that some sites want to
77*6777b538SAndroid Build Coastguard Worker // trigger downloads by sending application/octet-stream (even though they
78*6777b538SAndroid Build Coastguard Worker // should be sending Content-Disposition: attachment). Although it is safe
79*6777b538SAndroid Build Coastguard Worker // to render as GIF from a security perspective, we actually get better
80*6777b538SAndroid Build Coastguard Worker // compatibility if we don't sniff from application/octet stream at all.
81*6777b538SAndroid Build Coastguard Worker // => Chrome: Download as application/octet-stream
82*6777b538SAndroid Build Coastguard Worker //
83*6777b538SAndroid Build Coastguard Worker // Note that our definition of HTML payload is much stricter than IE's
84*6777b538SAndroid Build Coastguard Worker // definition and roughly the same as Firefox's definition.
85*6777b538SAndroid Build Coastguard Worker
86*6777b538SAndroid Build Coastguard Worker #include <stdint.h>
87*6777b538SAndroid Build Coastguard Worker #include <string>
88*6777b538SAndroid Build Coastguard Worker
89*6777b538SAndroid Build Coastguard Worker #include "net/base/mime_sniffer.h"
90*6777b538SAndroid Build Coastguard Worker
91*6777b538SAndroid Build Coastguard Worker #include "base/check_op.h"
92*6777b538SAndroid Build Coastguard Worker #include "base/containers/span.h"
93*6777b538SAndroid Build Coastguard Worker #include "base/notreached.h"
94*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
95*6777b538SAndroid Build Coastguard Worker #include "build/build_config.h"
96*6777b538SAndroid Build Coastguard Worker #include "url/gurl.h"
97*6777b538SAndroid Build Coastguard Worker
98*6777b538SAndroid Build Coastguard Worker namespace net {
99*6777b538SAndroid Build Coastguard Worker
100*6777b538SAndroid Build Coastguard Worker // The number of content bytes we need to use all our magic numbers. Feel free
101*6777b538SAndroid Build Coastguard Worker // to increase this number if you add a longer magic number.
102*6777b538SAndroid Build Coastguard Worker static const size_t kBytesRequiredForMagic = 42;
103*6777b538SAndroid Build Coastguard Worker
104*6777b538SAndroid Build Coastguard Worker struct MagicNumber {
105*6777b538SAndroid Build Coastguard Worker const char* const mime_type;
106*6777b538SAndroid Build Coastguard Worker const std::string_view magic;
107*6777b538SAndroid Build Coastguard Worker bool is_string;
108*6777b538SAndroid Build Coastguard Worker const char* const mask; // if set, must have same length as |magic|
109*6777b538SAndroid Build Coastguard Worker };
110*6777b538SAndroid Build Coastguard Worker
111*6777b538SAndroid Build Coastguard Worker #define MAGIC_NUMBER(mime_type, magic) \
112*6777b538SAndroid Build Coastguard Worker { (mime_type), std::string_view((magic), sizeof(magic) - 1), false, nullptr }
113*6777b538SAndroid Build Coastguard Worker
114*6777b538SAndroid Build Coastguard Worker template <int MagicSize, int MaskSize>
115*6777b538SAndroid Build Coastguard Worker class VerifySizes {
116*6777b538SAndroid Build Coastguard Worker static_assert(MagicSize == MaskSize, "sizes must be equal");
117*6777b538SAndroid Build Coastguard Worker
118*6777b538SAndroid Build Coastguard Worker public:
119*6777b538SAndroid Build Coastguard Worker enum { SIZES = MagicSize };
120*6777b538SAndroid Build Coastguard Worker };
121*6777b538SAndroid Build Coastguard Worker
122*6777b538SAndroid Build Coastguard Worker #define verified_sizeof(magic, mask) \
123*6777b538SAndroid Build Coastguard Worker VerifySizes<sizeof(magic), sizeof(mask)>::SIZES
124*6777b538SAndroid Build Coastguard Worker
125*6777b538SAndroid Build Coastguard Worker #define MAGIC_MASK(mime_type, magic, mask) \
126*6777b538SAndroid Build Coastguard Worker { \
127*6777b538SAndroid Build Coastguard Worker (mime_type), std::string_view((magic), verified_sizeof(magic, mask) - 1), \
128*6777b538SAndroid Build Coastguard Worker false, (mask) \
129*6777b538SAndroid Build Coastguard Worker }
130*6777b538SAndroid Build Coastguard Worker
131*6777b538SAndroid Build Coastguard Worker // Magic strings are case insensitive and must not include '\0' characters
132*6777b538SAndroid Build Coastguard Worker #define MAGIC_STRING(mime_type, magic) \
133*6777b538SAndroid Build Coastguard Worker { (mime_type), std::string_view((magic), sizeof(magic) - 1), true, nullptr }
134*6777b538SAndroid Build Coastguard Worker
135*6777b538SAndroid Build Coastguard Worker static const MagicNumber kMagicNumbers[] = {
136*6777b538SAndroid Build Coastguard Worker // Source: HTML 5 specification
137*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("application/pdf", "%PDF-"),
138*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("application/postscript", "%!PS-Adobe-"),
139*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/gif", "GIF87a"),
140*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/gif", "GIF89a"),
141*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/png", "\x89" "PNG\x0D\x0A\x1A\x0A"),
142*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/jpeg", "\xFF\xD8\xFF"),
143*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/bmp", "BM"),
144*6777b538SAndroid Build Coastguard Worker // Source: Mozilla
145*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("text/plain", "#!"), // Script
146*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("text/plain", "%!"), // Script, similar to PS
147*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("text/plain", "From"),
148*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("text/plain", ">From"),
149*6777b538SAndroid Build Coastguard Worker // Chrome specific
150*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("application/x-gzip", "\x1F\x8B\x08"),
151*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("audio/x-pn-realaudio", "\x2E\x52\x4D\x46"),
152*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("video/x-ms-asf",
153*6777b538SAndroid Build Coastguard Worker "\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C"),
154*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/tiff", "I I"),
155*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/tiff", "II*"),
156*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/tiff", "MM\x00*"),
157*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("audio/mpeg", "ID3"),
158*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/webp", "RIFF....WEBPVP"),
159*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("video/webm", "\x1A\x45\xDF\xA3"),
160*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("application/zip", "PK\x03\x04"),
161*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("application/x-rar-compressed", "Rar!\x1A\x07\x00"),
162*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("application/x-msmetafile", "\xD7\xCD\xC6\x9A"),
163*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("application/octet-stream", "MZ"), // EXE
164*6777b538SAndroid Build Coastguard Worker // Sniffing for Flash:
165*6777b538SAndroid Build Coastguard Worker //
166*6777b538SAndroid Build Coastguard Worker // MAGIC_NUMBER("application/x-shockwave-flash", "CWS"),
167*6777b538SAndroid Build Coastguard Worker // MAGIC_NUMBER("application/x-shockwave-flash", "FLV"),
168*6777b538SAndroid Build Coastguard Worker // MAGIC_NUMBER("application/x-shockwave-flash", "FWS"),
169*6777b538SAndroid Build Coastguard Worker //
170*6777b538SAndroid Build Coastguard Worker // Including these magic number for Flash is a trade off.
171*6777b538SAndroid Build Coastguard Worker //
172*6777b538SAndroid Build Coastguard Worker // Pros:
173*6777b538SAndroid Build Coastguard Worker // * Flash is an important and popular file format
174*6777b538SAndroid Build Coastguard Worker //
175*6777b538SAndroid Build Coastguard Worker // Cons:
176*6777b538SAndroid Build Coastguard Worker // * These patterns are fairly weak
177*6777b538SAndroid Build Coastguard Worker // * If we mistakenly decide something is Flash, we will execute it
178*6777b538SAndroid Build Coastguard Worker // in the origin of an unsuspecting site. This could be a security
179*6777b538SAndroid Build Coastguard Worker // vulnerability if the site allows users to upload content.
180*6777b538SAndroid Build Coastguard Worker //
181*6777b538SAndroid Build Coastguard Worker // On balance, we do not include these patterns.
182*6777b538SAndroid Build Coastguard Worker };
183*6777b538SAndroid Build Coastguard Worker
184*6777b538SAndroid Build Coastguard Worker // The number of content bytes we need to use all our Microsoft Office magic
185*6777b538SAndroid Build Coastguard Worker // numbers.
186*6777b538SAndroid Build Coastguard Worker static const size_t kBytesRequiredForOfficeMagic = 8;
187*6777b538SAndroid Build Coastguard Worker
188*6777b538SAndroid Build Coastguard Worker static const MagicNumber kOfficeMagicNumbers[] = {
189*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("CFB", "\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"),
190*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("OOXML", "PK\x03\x04"),
191*6777b538SAndroid Build Coastguard Worker };
192*6777b538SAndroid Build Coastguard Worker
193*6777b538SAndroid Build Coastguard Worker enum OfficeDocType {
194*6777b538SAndroid Build Coastguard Worker DOC_TYPE_WORD,
195*6777b538SAndroid Build Coastguard Worker DOC_TYPE_EXCEL,
196*6777b538SAndroid Build Coastguard Worker DOC_TYPE_POWERPOINT,
197*6777b538SAndroid Build Coastguard Worker DOC_TYPE_NONE
198*6777b538SAndroid Build Coastguard Worker };
199*6777b538SAndroid Build Coastguard Worker
200*6777b538SAndroid Build Coastguard Worker struct OfficeExtensionType {
201*6777b538SAndroid Build Coastguard Worker OfficeDocType doc_type;
202*6777b538SAndroid Build Coastguard Worker const std::string_view extension;
203*6777b538SAndroid Build Coastguard Worker };
204*6777b538SAndroid Build Coastguard Worker
205*6777b538SAndroid Build Coastguard Worker #define OFFICE_EXTENSION(type, extension) \
206*6777b538SAndroid Build Coastguard Worker { (type), std::string_view((extension), sizeof(extension) - 1) }
207*6777b538SAndroid Build Coastguard Worker
208*6777b538SAndroid Build Coastguard Worker static const OfficeExtensionType kOfficeExtensionTypes[] = {
209*6777b538SAndroid Build Coastguard Worker OFFICE_EXTENSION(DOC_TYPE_WORD, ".doc"),
210*6777b538SAndroid Build Coastguard Worker OFFICE_EXTENSION(DOC_TYPE_EXCEL, ".xls"),
211*6777b538SAndroid Build Coastguard Worker OFFICE_EXTENSION(DOC_TYPE_POWERPOINT, ".ppt"),
212*6777b538SAndroid Build Coastguard Worker OFFICE_EXTENSION(DOC_TYPE_WORD, ".docx"),
213*6777b538SAndroid Build Coastguard Worker OFFICE_EXTENSION(DOC_TYPE_EXCEL, ".xlsx"),
214*6777b538SAndroid Build Coastguard Worker OFFICE_EXTENSION(DOC_TYPE_POWERPOINT, ".pptx"),
215*6777b538SAndroid Build Coastguard Worker };
216*6777b538SAndroid Build Coastguard Worker
217*6777b538SAndroid Build Coastguard Worker static const MagicNumber kExtraMagicNumbers[] = {
218*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-xbitmap", "#define"),
219*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-icon", "\x00\x00\x01\x00"),
220*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("audio/wav", "RIFF....WAVEfmt "),
221*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("video/avi", "RIFF....AVI LIST"),
222*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("audio/ogg", "OggS\0"),
223*6777b538SAndroid Build Coastguard Worker MAGIC_MASK("video/mpeg", "\x00\x00\x01\xB0", "\xFF\xFF\xFF\xF0"),
224*6777b538SAndroid Build Coastguard Worker MAGIC_MASK("audio/mpeg", "\xFF\xE0", "\xFF\xE0"),
225*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("video/3gpp", "....ftyp3g"),
226*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("video/3gpp", "....ftypavcl"),
227*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("video/mp4", "....ftyp"),
228*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("video/quicktime", "....moov"),
229*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("application/x-shockwave-flash", "CWS"),
230*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("application/x-shockwave-flash", "FWS"),
231*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("video/x-flv", "FLV"),
232*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("audio/x-flac", "fLaC"),
233*6777b538SAndroid Build Coastguard Worker // Per https://tools.ietf.org/html/rfc3267#section-8.1
234*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("audio/amr", "#!AMR\n"),
235*6777b538SAndroid Build Coastguard Worker
236*6777b538SAndroid Build Coastguard Worker // RAW image types.
237*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-canon-cr2", "II\x2a\x00\x10\x00\x00\x00CR"),
238*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-canon-crw", "II\x1a\x00\x00\x00HEAPCCDR"),
239*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-minolta-mrw", "\x00MRM"),
240*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-olympus-orf", "MMOR"), // big-endian
241*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-olympus-orf", "IIRO"), // little-endian
242*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-olympus-orf", "IIRS"), // little-endian
243*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-fuji-raf", "FUJIFILMCCD-RAW "),
244*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-panasonic-raw",
245*6777b538SAndroid Build Coastguard Worker "IIU\x00\x08\x00\x00\x00"), // Panasonic .raw
246*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-panasonic-raw",
247*6777b538SAndroid Build Coastguard Worker "IIU\x00\x18\x00\x00\x00"), // Panasonic .rw2
248*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-phaseone-raw", "MMMMRaw"),
249*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("image/x-x3f", "FOVb"),
250*6777b538SAndroid Build Coastguard Worker };
251*6777b538SAndroid Build Coastguard Worker
252*6777b538SAndroid Build Coastguard Worker // Our HTML sniffer differs slightly from Mozilla. For example, Mozilla will
253*6777b538SAndroid Build Coastguard Worker // decide that a document that begins "<!DOCTYPE SOAP-ENV:Envelope PUBLIC " is
254*6777b538SAndroid Build Coastguard Worker // HTML, but we will not.
255*6777b538SAndroid Build Coastguard Worker
256*6777b538SAndroid Build Coastguard Worker #define MAGIC_HTML_TAG(tag) \
257*6777b538SAndroid Build Coastguard Worker MAGIC_STRING("text/html", "<" tag)
258*6777b538SAndroid Build Coastguard Worker
259*6777b538SAndroid Build Coastguard Worker static const MagicNumber kSniffableTags[] = {
260*6777b538SAndroid Build Coastguard Worker // XML processing directive. Although this is not an HTML mime type, we sniff
261*6777b538SAndroid Build Coastguard Worker // for this in the HTML phase because text/xml is just as powerful as HTML and
262*6777b538SAndroid Build Coastguard Worker // we want to leverage our white space skipping technology.
263*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("text/xml", "<?xml"), // Mozilla
264*6777b538SAndroid Build Coastguard Worker // DOCTYPEs
265*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("!DOCTYPE html"), // HTML5 spec
266*6777b538SAndroid Build Coastguard Worker // Sniffable tags, ordered by how often they occur in sniffable documents.
267*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("script"), // HTML5 spec, Mozilla
268*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("html"), // HTML5 spec, Mozilla
269*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("!--"),
270*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("head"), // HTML5 spec, Mozilla
271*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("iframe"), // Mozilla
272*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("h1"), // Mozilla
273*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("div"), // Mozilla
274*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("font"), // Mozilla
275*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("table"), // Mozilla
276*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("a"), // Mozilla
277*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("style"), // Mozilla
278*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("title"), // Mozilla
279*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("b"), // Mozilla
280*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("body"), // Mozilla
281*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("br"),
282*6777b538SAndroid Build Coastguard Worker MAGIC_HTML_TAG("p"), // Mozilla
283*6777b538SAndroid Build Coastguard Worker };
284*6777b538SAndroid Build Coastguard Worker
285*6777b538SAndroid Build Coastguard Worker // Compare content header to a magic number where magic_entry can contain '.'
286*6777b538SAndroid Build Coastguard Worker // for single character of anything, allowing some bytes to be skipped.
MagicCmp(std::string_view content,std::string_view magic_entry)287*6777b538SAndroid Build Coastguard Worker static bool MagicCmp(std::string_view content, std::string_view magic_entry) {
288*6777b538SAndroid Build Coastguard Worker DCHECK_GE(content.length(), magic_entry.length());
289*6777b538SAndroid Build Coastguard Worker
290*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < magic_entry.length(); ++i) {
291*6777b538SAndroid Build Coastguard Worker if (magic_entry[i] != '.' && magic_entry[i] != content[i])
292*6777b538SAndroid Build Coastguard Worker return false;
293*6777b538SAndroid Build Coastguard Worker }
294*6777b538SAndroid Build Coastguard Worker return true;
295*6777b538SAndroid Build Coastguard Worker }
296*6777b538SAndroid Build Coastguard Worker
297*6777b538SAndroid Build Coastguard Worker // Like MagicCmp() except that it ANDs each byte with a mask before
298*6777b538SAndroid Build Coastguard Worker // the comparison, because there are some bits we don't care about.
MagicMaskCmp(std::string_view content,std::string_view magic_entry,std::string_view magic_mask)299*6777b538SAndroid Build Coastguard Worker static bool MagicMaskCmp(std::string_view content,
300*6777b538SAndroid Build Coastguard Worker std::string_view magic_entry,
301*6777b538SAndroid Build Coastguard Worker std::string_view magic_mask) {
302*6777b538SAndroid Build Coastguard Worker DCHECK_GE(content.length(), magic_entry.length());
303*6777b538SAndroid Build Coastguard Worker
304*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < magic_entry.length(); ++i) {
305*6777b538SAndroid Build Coastguard Worker if (magic_entry[i] != '.' && magic_entry[i] != (magic_mask[i] & content[i]))
306*6777b538SAndroid Build Coastguard Worker return false;
307*6777b538SAndroid Build Coastguard Worker }
308*6777b538SAndroid Build Coastguard Worker return true;
309*6777b538SAndroid Build Coastguard Worker }
310*6777b538SAndroid Build Coastguard Worker
MatchMagicNumber(std::string_view content,const MagicNumber & magic_entry,std::string * result)311*6777b538SAndroid Build Coastguard Worker static bool MatchMagicNumber(std::string_view content,
312*6777b538SAndroid Build Coastguard Worker const MagicNumber& magic_entry,
313*6777b538SAndroid Build Coastguard Worker std::string* result) {
314*6777b538SAndroid Build Coastguard Worker // Keep kBytesRequiredForMagic honest.
315*6777b538SAndroid Build Coastguard Worker DCHECK_LE(magic_entry.magic.length(), kBytesRequiredForMagic);
316*6777b538SAndroid Build Coastguard Worker
317*6777b538SAndroid Build Coastguard Worker bool match = false;
318*6777b538SAndroid Build Coastguard Worker if (content.length() >= magic_entry.magic.length()) {
319*6777b538SAndroid Build Coastguard Worker if (magic_entry.is_string) {
320*6777b538SAndroid Build Coastguard Worker // Consistency check - string entries should have no embedded nulls.
321*6777b538SAndroid Build Coastguard Worker DCHECK_EQ(std::string_view::npos, magic_entry.magic.find('\0'));
322*6777b538SAndroid Build Coastguard Worker
323*6777b538SAndroid Build Coastguard Worker // Do a case-insensitive prefix comparison.
324*6777b538SAndroid Build Coastguard Worker match = base::StartsWith(content, magic_entry.magic,
325*6777b538SAndroid Build Coastguard Worker base::CompareCase::INSENSITIVE_ASCII);
326*6777b538SAndroid Build Coastguard Worker } else if (!magic_entry.mask) {
327*6777b538SAndroid Build Coastguard Worker match = MagicCmp(content, magic_entry.magic);
328*6777b538SAndroid Build Coastguard Worker } else {
329*6777b538SAndroid Build Coastguard Worker std::string_view magic_mask(magic_entry.mask, magic_entry.magic.length());
330*6777b538SAndroid Build Coastguard Worker match = MagicMaskCmp(content, magic_entry.magic, magic_mask);
331*6777b538SAndroid Build Coastguard Worker }
332*6777b538SAndroid Build Coastguard Worker }
333*6777b538SAndroid Build Coastguard Worker
334*6777b538SAndroid Build Coastguard Worker if (match) {
335*6777b538SAndroid Build Coastguard Worker result->assign(magic_entry.mime_type);
336*6777b538SAndroid Build Coastguard Worker return true;
337*6777b538SAndroid Build Coastguard Worker }
338*6777b538SAndroid Build Coastguard Worker return false;
339*6777b538SAndroid Build Coastguard Worker }
340*6777b538SAndroid Build Coastguard Worker
CheckForMagicNumbers(std::string_view content,base::span<const MagicNumber> magic_numbers,std::string * result)341*6777b538SAndroid Build Coastguard Worker static bool CheckForMagicNumbers(std::string_view content,
342*6777b538SAndroid Build Coastguard Worker base::span<const MagicNumber> magic_numbers,
343*6777b538SAndroid Build Coastguard Worker std::string* result) {
344*6777b538SAndroid Build Coastguard Worker for (const MagicNumber& magic : magic_numbers) {
345*6777b538SAndroid Build Coastguard Worker if (MatchMagicNumber(content, magic, result))
346*6777b538SAndroid Build Coastguard Worker return true;
347*6777b538SAndroid Build Coastguard Worker }
348*6777b538SAndroid Build Coastguard Worker return false;
349*6777b538SAndroid Build Coastguard Worker }
350*6777b538SAndroid Build Coastguard Worker
351*6777b538SAndroid Build Coastguard Worker // Truncates |string_piece| to length |max_size| and returns true if
352*6777b538SAndroid Build Coastguard Worker // |string_piece| is now exactly |max_size|.
TruncateStringPiece(const size_t max_size,std::string_view * string_piece)353*6777b538SAndroid Build Coastguard Worker static bool TruncateStringPiece(const size_t max_size,
354*6777b538SAndroid Build Coastguard Worker std::string_view* string_piece) {
355*6777b538SAndroid Build Coastguard Worker // Keep kMaxBytesToSniff honest.
356*6777b538SAndroid Build Coastguard Worker DCHECK_LE(static_cast<int>(max_size), kMaxBytesToSniff);
357*6777b538SAndroid Build Coastguard Worker
358*6777b538SAndroid Build Coastguard Worker *string_piece = string_piece->substr(0, max_size);
359*6777b538SAndroid Build Coastguard Worker return string_piece->length() == max_size;
360*6777b538SAndroid Build Coastguard Worker }
361*6777b538SAndroid Build Coastguard Worker
362*6777b538SAndroid Build Coastguard Worker // Returns true and sets result if the content appears to be HTML.
363*6777b538SAndroid Build Coastguard Worker // Clears have_enough_content if more data could possibly change the result.
SniffForHTML(std::string_view content,bool * have_enough_content,std::string * result)364*6777b538SAndroid Build Coastguard Worker static bool SniffForHTML(std::string_view content,
365*6777b538SAndroid Build Coastguard Worker bool* have_enough_content,
366*6777b538SAndroid Build Coastguard Worker std::string* result) {
367*6777b538SAndroid Build Coastguard Worker // For HTML, we are willing to consider up to 512 bytes. This may be overly
368*6777b538SAndroid Build Coastguard Worker // conservative as IE only considers 256.
369*6777b538SAndroid Build Coastguard Worker *have_enough_content &= TruncateStringPiece(512, &content);
370*6777b538SAndroid Build Coastguard Worker
371*6777b538SAndroid Build Coastguard Worker // We adopt a strategy similar to that used by Mozilla to sniff HTML tags,
372*6777b538SAndroid Build Coastguard Worker // but with some modifications to better match the HTML5 spec.
373*6777b538SAndroid Build Coastguard Worker std::string_view trimmed =
374*6777b538SAndroid Build Coastguard Worker base::TrimWhitespaceASCII(content, base::TRIM_LEADING);
375*6777b538SAndroid Build Coastguard Worker
376*6777b538SAndroid Build Coastguard Worker // |trimmed| now starts at first non-whitespace character (or is empty).
377*6777b538SAndroid Build Coastguard Worker return CheckForMagicNumbers(trimmed, kSniffableTags, result);
378*6777b538SAndroid Build Coastguard Worker }
379*6777b538SAndroid Build Coastguard Worker
380*6777b538SAndroid Build Coastguard Worker // Returns true and sets result if the content matches any of kMagicNumbers.
381*6777b538SAndroid Build Coastguard Worker // Clears have_enough_content if more data could possibly change the result.
SniffForMagicNumbers(std::string_view content,bool * have_enough_content,std::string * result)382*6777b538SAndroid Build Coastguard Worker static bool SniffForMagicNumbers(std::string_view content,
383*6777b538SAndroid Build Coastguard Worker bool* have_enough_content,
384*6777b538SAndroid Build Coastguard Worker std::string* result) {
385*6777b538SAndroid Build Coastguard Worker *have_enough_content &= TruncateStringPiece(kBytesRequiredForMagic, &content);
386*6777b538SAndroid Build Coastguard Worker
387*6777b538SAndroid Build Coastguard Worker // Check our big table of Magic Numbers
388*6777b538SAndroid Build Coastguard Worker return CheckForMagicNumbers(content, kMagicNumbers, result);
389*6777b538SAndroid Build Coastguard Worker }
390*6777b538SAndroid Build Coastguard Worker
391*6777b538SAndroid Build Coastguard Worker // Returns true and sets result if the content matches any of
392*6777b538SAndroid Build Coastguard Worker // kOfficeMagicNumbers, and the URL has the proper extension.
393*6777b538SAndroid Build Coastguard Worker // Clears |have_enough_content| if more data could possibly change the result.
SniffForOfficeDocs(std::string_view content,const GURL & url,bool * have_enough_content,std::string * result)394*6777b538SAndroid Build Coastguard Worker static bool SniffForOfficeDocs(std::string_view content,
395*6777b538SAndroid Build Coastguard Worker const GURL& url,
396*6777b538SAndroid Build Coastguard Worker bool* have_enough_content,
397*6777b538SAndroid Build Coastguard Worker std::string* result) {
398*6777b538SAndroid Build Coastguard Worker *have_enough_content &=
399*6777b538SAndroid Build Coastguard Worker TruncateStringPiece(kBytesRequiredForOfficeMagic, &content);
400*6777b538SAndroid Build Coastguard Worker
401*6777b538SAndroid Build Coastguard Worker // Check our table of magic numbers for Office file types.
402*6777b538SAndroid Build Coastguard Worker std::string office_version;
403*6777b538SAndroid Build Coastguard Worker if (!CheckForMagicNumbers(content, kOfficeMagicNumbers, &office_version))
404*6777b538SAndroid Build Coastguard Worker return false;
405*6777b538SAndroid Build Coastguard Worker
406*6777b538SAndroid Build Coastguard Worker OfficeDocType type = DOC_TYPE_NONE;
407*6777b538SAndroid Build Coastguard Worker std::string_view url_path = url.path_piece();
408*6777b538SAndroid Build Coastguard Worker for (const auto& office_extension : kOfficeExtensionTypes) {
409*6777b538SAndroid Build Coastguard Worker if (base::EndsWith(url_path, office_extension.extension,
410*6777b538SAndroid Build Coastguard Worker base::CompareCase::INSENSITIVE_ASCII)) {
411*6777b538SAndroid Build Coastguard Worker type = office_extension.doc_type;
412*6777b538SAndroid Build Coastguard Worker break;
413*6777b538SAndroid Build Coastguard Worker }
414*6777b538SAndroid Build Coastguard Worker }
415*6777b538SAndroid Build Coastguard Worker
416*6777b538SAndroid Build Coastguard Worker if (type == DOC_TYPE_NONE)
417*6777b538SAndroid Build Coastguard Worker return false;
418*6777b538SAndroid Build Coastguard Worker
419*6777b538SAndroid Build Coastguard Worker if (office_version == "CFB") {
420*6777b538SAndroid Build Coastguard Worker switch (type) {
421*6777b538SAndroid Build Coastguard Worker case DOC_TYPE_WORD:
422*6777b538SAndroid Build Coastguard Worker *result = "application/msword";
423*6777b538SAndroid Build Coastguard Worker return true;
424*6777b538SAndroid Build Coastguard Worker case DOC_TYPE_EXCEL:
425*6777b538SAndroid Build Coastguard Worker *result = "application/vnd.ms-excel";
426*6777b538SAndroid Build Coastguard Worker return true;
427*6777b538SAndroid Build Coastguard Worker case DOC_TYPE_POWERPOINT:
428*6777b538SAndroid Build Coastguard Worker *result = "application/vnd.ms-powerpoint";
429*6777b538SAndroid Build Coastguard Worker return true;
430*6777b538SAndroid Build Coastguard Worker case DOC_TYPE_NONE:
431*6777b538SAndroid Build Coastguard Worker NOTREACHED();
432*6777b538SAndroid Build Coastguard Worker return false;
433*6777b538SAndroid Build Coastguard Worker }
434*6777b538SAndroid Build Coastguard Worker } else if (office_version == "OOXML") {
435*6777b538SAndroid Build Coastguard Worker switch (type) {
436*6777b538SAndroid Build Coastguard Worker case DOC_TYPE_WORD:
437*6777b538SAndroid Build Coastguard Worker *result = "application/vnd.openxmlformats-officedocument."
438*6777b538SAndroid Build Coastguard Worker "wordprocessingml.document";
439*6777b538SAndroid Build Coastguard Worker return true;
440*6777b538SAndroid Build Coastguard Worker case DOC_TYPE_EXCEL:
441*6777b538SAndroid Build Coastguard Worker *result = "application/vnd.openxmlformats-officedocument."
442*6777b538SAndroid Build Coastguard Worker "spreadsheetml.sheet";
443*6777b538SAndroid Build Coastguard Worker return true;
444*6777b538SAndroid Build Coastguard Worker case DOC_TYPE_POWERPOINT:
445*6777b538SAndroid Build Coastguard Worker *result = "application/vnd.openxmlformats-officedocument."
446*6777b538SAndroid Build Coastguard Worker "presentationml.presentation";
447*6777b538SAndroid Build Coastguard Worker return true;
448*6777b538SAndroid Build Coastguard Worker case DOC_TYPE_NONE:
449*6777b538SAndroid Build Coastguard Worker NOTREACHED();
450*6777b538SAndroid Build Coastguard Worker return false;
451*6777b538SAndroid Build Coastguard Worker }
452*6777b538SAndroid Build Coastguard Worker }
453*6777b538SAndroid Build Coastguard Worker
454*6777b538SAndroid Build Coastguard Worker NOTREACHED();
455*6777b538SAndroid Build Coastguard Worker return false;
456*6777b538SAndroid Build Coastguard Worker }
457*6777b538SAndroid Build Coastguard Worker
IsOfficeType(const std::string & type_hint)458*6777b538SAndroid Build Coastguard Worker static bool IsOfficeType(const std::string& type_hint) {
459*6777b538SAndroid Build Coastguard Worker return (type_hint == "application/msword" ||
460*6777b538SAndroid Build Coastguard Worker type_hint == "application/vnd.ms-excel" ||
461*6777b538SAndroid Build Coastguard Worker type_hint == "application/vnd.ms-powerpoint" ||
462*6777b538SAndroid Build Coastguard Worker type_hint == "application/vnd.openxmlformats-officedocument."
463*6777b538SAndroid Build Coastguard Worker "wordprocessingml.document" ||
464*6777b538SAndroid Build Coastguard Worker type_hint == "application/vnd.openxmlformats-officedocument."
465*6777b538SAndroid Build Coastguard Worker "spreadsheetml.sheet" ||
466*6777b538SAndroid Build Coastguard Worker type_hint == "application/vnd.openxmlformats-officedocument."
467*6777b538SAndroid Build Coastguard Worker "presentationml.presentation" ||
468*6777b538SAndroid Build Coastguard Worker type_hint == "application/vnd.ms-excel.sheet.macroenabled.12" ||
469*6777b538SAndroid Build Coastguard Worker type_hint == "application/vnd.ms-word.document.macroenabled.12" ||
470*6777b538SAndroid Build Coastguard Worker type_hint == "application/vnd.ms-powerpoint.presentation."
471*6777b538SAndroid Build Coastguard Worker "macroenabled.12" ||
472*6777b538SAndroid Build Coastguard Worker type_hint == "application/mspowerpoint" ||
473*6777b538SAndroid Build Coastguard Worker type_hint == "application/msexcel" ||
474*6777b538SAndroid Build Coastguard Worker type_hint == "application/vnd.ms-word" ||
475*6777b538SAndroid Build Coastguard Worker type_hint == "application/vnd.ms-word.document.12" ||
476*6777b538SAndroid Build Coastguard Worker type_hint == "application/vnd.msword");
477*6777b538SAndroid Build Coastguard Worker }
478*6777b538SAndroid Build Coastguard Worker
479*6777b538SAndroid Build Coastguard Worker // This function checks for files that have a Microsoft Office MIME type
480*6777b538SAndroid Build Coastguard Worker // set, but are not actually Office files.
481*6777b538SAndroid Build Coastguard Worker //
482*6777b538SAndroid Build Coastguard Worker // If this is not actually an Office file, |*result| is set to
483*6777b538SAndroid Build Coastguard Worker // "application/octet-stream", otherwise it is not modified.
484*6777b538SAndroid Build Coastguard Worker //
485*6777b538SAndroid Build Coastguard Worker // Returns false if additional data is required to determine the file type, or
486*6777b538SAndroid Build Coastguard Worker // true if there is enough data to make a decision.
SniffForInvalidOfficeDocs(std::string_view content,const GURL & url,std::string * result)487*6777b538SAndroid Build Coastguard Worker static bool SniffForInvalidOfficeDocs(std::string_view content,
488*6777b538SAndroid Build Coastguard Worker const GURL& url,
489*6777b538SAndroid Build Coastguard Worker std::string* result) {
490*6777b538SAndroid Build Coastguard Worker if (!TruncateStringPiece(kBytesRequiredForOfficeMagic, &content))
491*6777b538SAndroid Build Coastguard Worker return false;
492*6777b538SAndroid Build Coastguard Worker
493*6777b538SAndroid Build Coastguard Worker // Check our table of magic numbers for Office file types. If it does not
494*6777b538SAndroid Build Coastguard Worker // match one, the MIME type was invalid. Set it instead to a safe value.
495*6777b538SAndroid Build Coastguard Worker std::string office_version;
496*6777b538SAndroid Build Coastguard Worker if (!CheckForMagicNumbers(content, kOfficeMagicNumbers, &office_version)) {
497*6777b538SAndroid Build Coastguard Worker *result = "application/octet-stream";
498*6777b538SAndroid Build Coastguard Worker }
499*6777b538SAndroid Build Coastguard Worker
500*6777b538SAndroid Build Coastguard Worker // We have enough information to determine if this was a Microsoft Office
501*6777b538SAndroid Build Coastguard Worker // document or not, so sniffing is completed.
502*6777b538SAndroid Build Coastguard Worker return true;
503*6777b538SAndroid Build Coastguard Worker }
504*6777b538SAndroid Build Coastguard Worker
505*6777b538SAndroid Build Coastguard Worker // Tags that indicate the content is likely XML.
506*6777b538SAndroid Build Coastguard Worker static const MagicNumber kMagicXML[] = {
507*6777b538SAndroid Build Coastguard Worker MAGIC_STRING("application/atom+xml", "<feed"),
508*6777b538SAndroid Build Coastguard Worker MAGIC_STRING("application/rss+xml", "<rss"),
509*6777b538SAndroid Build Coastguard Worker };
510*6777b538SAndroid Build Coastguard Worker
511*6777b538SAndroid Build Coastguard Worker // Returns true and sets result if the content appears to contain XHTML or a
512*6777b538SAndroid Build Coastguard Worker // feed.
513*6777b538SAndroid Build Coastguard Worker // Clears have_enough_content if more data could possibly change the result.
514*6777b538SAndroid Build Coastguard Worker //
515*6777b538SAndroid Build Coastguard Worker // TODO(evanm): this is similar but more conservative than what Safari does,
516*6777b538SAndroid Build Coastguard Worker // while HTML5 has a different recommendation -- what should we do?
517*6777b538SAndroid Build Coastguard Worker // TODO(evanm): this is incorrect for documents whose encoding isn't a superset
518*6777b538SAndroid Build Coastguard Worker // of ASCII -- do we care?
SniffXML(std::string_view content,bool * have_enough_content,std::string * result)519*6777b538SAndroid Build Coastguard Worker static bool SniffXML(std::string_view content,
520*6777b538SAndroid Build Coastguard Worker bool* have_enough_content,
521*6777b538SAndroid Build Coastguard Worker std::string* result) {
522*6777b538SAndroid Build Coastguard Worker // We allow at most 300 bytes of content before we expect the opening tag.
523*6777b538SAndroid Build Coastguard Worker *have_enough_content &= TruncateStringPiece(300, &content);
524*6777b538SAndroid Build Coastguard Worker
525*6777b538SAndroid Build Coastguard Worker // This loop iterates through tag-looking offsets in the file.
526*6777b538SAndroid Build Coastguard Worker // We want to skip XML processing instructions (of the form "<?xml ...")
527*6777b538SAndroid Build Coastguard Worker // and stop at the first "plain" tag, then make a decision on the mime-type
528*6777b538SAndroid Build Coastguard Worker // based on the name (or possibly attributes) of that tag.
529*6777b538SAndroid Build Coastguard Worker const int kMaxTagIterations = 5;
530*6777b538SAndroid Build Coastguard Worker size_t pos = 0;
531*6777b538SAndroid Build Coastguard Worker for (size_t i = 0; i < kMaxTagIterations && pos < content.length(); ++i) {
532*6777b538SAndroid Build Coastguard Worker pos = content.find('<', pos);
533*6777b538SAndroid Build Coastguard Worker if (pos == std::string_view::npos) {
534*6777b538SAndroid Build Coastguard Worker return false;
535*6777b538SAndroid Build Coastguard Worker }
536*6777b538SAndroid Build Coastguard Worker
537*6777b538SAndroid Build Coastguard Worker std::string_view current = content.substr(pos);
538*6777b538SAndroid Build Coastguard Worker
539*6777b538SAndroid Build Coastguard Worker // Skip XML and DOCTYPE declarations.
540*6777b538SAndroid Build Coastguard Worker static constexpr std::string_view kXmlPrefix("<?xml");
541*6777b538SAndroid Build Coastguard Worker static constexpr std::string_view kDocTypePrefix("<!DOCTYPE");
542*6777b538SAndroid Build Coastguard Worker if (base::StartsWith(current, kXmlPrefix,
543*6777b538SAndroid Build Coastguard Worker base::CompareCase::INSENSITIVE_ASCII) ||
544*6777b538SAndroid Build Coastguard Worker base::StartsWith(current, kDocTypePrefix,
545*6777b538SAndroid Build Coastguard Worker base::CompareCase::INSENSITIVE_ASCII)) {
546*6777b538SAndroid Build Coastguard Worker ++pos;
547*6777b538SAndroid Build Coastguard Worker continue;
548*6777b538SAndroid Build Coastguard Worker }
549*6777b538SAndroid Build Coastguard Worker
550*6777b538SAndroid Build Coastguard Worker if (CheckForMagicNumbers(current, kMagicXML, result))
551*6777b538SAndroid Build Coastguard Worker return true;
552*6777b538SAndroid Build Coastguard Worker
553*6777b538SAndroid Build Coastguard Worker // TODO(evanm): handle RSS 1.0, which is an RDF format and more difficult
554*6777b538SAndroid Build Coastguard Worker // to identify.
555*6777b538SAndroid Build Coastguard Worker
556*6777b538SAndroid Build Coastguard Worker // If we get here, we've hit an initial tag that hasn't matched one of the
557*6777b538SAndroid Build Coastguard Worker // above tests. Abort.
558*6777b538SAndroid Build Coastguard Worker return true;
559*6777b538SAndroid Build Coastguard Worker }
560*6777b538SAndroid Build Coastguard Worker
561*6777b538SAndroid Build Coastguard Worker // We iterated too far without finding a start tag.
562*6777b538SAndroid Build Coastguard Worker // If we have more content to look at, we aren't going to change our mind by
563*6777b538SAndroid Build Coastguard Worker // seeing more bytes from the network.
564*6777b538SAndroid Build Coastguard Worker return pos < content.length();
565*6777b538SAndroid Build Coastguard Worker }
566*6777b538SAndroid Build Coastguard Worker
567*6777b538SAndroid Build Coastguard Worker // Byte order marks
568*6777b538SAndroid Build Coastguard Worker static const MagicNumber kByteOrderMark[] = {
569*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("text/plain", "\xFE\xFF"), // UTF-16BE
570*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("text/plain", "\xFF\xFE"), // UTF-16LE
571*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("text/plain", "\xEF\xBB\xBF"), // UTF-8
572*6777b538SAndroid Build Coastguard Worker };
573*6777b538SAndroid Build Coastguard Worker
574*6777b538SAndroid Build Coastguard Worker // Returns true and sets result to "application/octet-stream" if the content
575*6777b538SAndroid Build Coastguard Worker // appears to be binary data. Otherwise, returns false and sets "text/plain".
576*6777b538SAndroid Build Coastguard Worker // Clears have_enough_content if more data could possibly change the result.
SniffBinary(std::string_view content,bool * have_enough_content,std::string * result)577*6777b538SAndroid Build Coastguard Worker static bool SniffBinary(std::string_view content,
578*6777b538SAndroid Build Coastguard Worker bool* have_enough_content,
579*6777b538SAndroid Build Coastguard Worker std::string* result) {
580*6777b538SAndroid Build Coastguard Worker // There is no consensus about exactly how to sniff for binary content.
581*6777b538SAndroid Build Coastguard Worker // * IE 7: Don't sniff for binary looking bytes, but trust the file extension.
582*6777b538SAndroid Build Coastguard Worker // * Firefox 3.5: Sniff first 4096 bytes for a binary looking byte.
583*6777b538SAndroid Build Coastguard Worker // Here, we side with FF, but with a smaller buffer. This size was chosen
584*6777b538SAndroid Build Coastguard Worker // because it is small enough to comfortably fit into a single packet (after
585*6777b538SAndroid Build Coastguard Worker // allowing for headers) and yet large enough to account for binary formats
586*6777b538SAndroid Build Coastguard Worker // that have a significant amount of ASCII at the beginning (crbug.com/15314).
587*6777b538SAndroid Build Coastguard Worker const bool is_truncated = TruncateStringPiece(kMaxBytesToSniff, &content);
588*6777b538SAndroid Build Coastguard Worker
589*6777b538SAndroid Build Coastguard Worker // First, we look for a BOM.
590*6777b538SAndroid Build Coastguard Worker std::string unused;
591*6777b538SAndroid Build Coastguard Worker if (CheckForMagicNumbers(content, kByteOrderMark, &unused)) {
592*6777b538SAndroid Build Coastguard Worker // If there is BOM, we think the buffer is not binary.
593*6777b538SAndroid Build Coastguard Worker result->assign("text/plain");
594*6777b538SAndroid Build Coastguard Worker return false;
595*6777b538SAndroid Build Coastguard Worker }
596*6777b538SAndroid Build Coastguard Worker
597*6777b538SAndroid Build Coastguard Worker // Next we look to see if any of the bytes "look binary."
598*6777b538SAndroid Build Coastguard Worker if (LooksLikeBinary(content)) {
599*6777b538SAndroid Build Coastguard Worker result->assign("application/octet-stream");
600*6777b538SAndroid Build Coastguard Worker return true;
601*6777b538SAndroid Build Coastguard Worker }
602*6777b538SAndroid Build Coastguard Worker
603*6777b538SAndroid Build Coastguard Worker // No evidence either way. Default to non-binary and, if truncated, clear
604*6777b538SAndroid Build Coastguard Worker // have_enough_content because there could be a binary looking byte in the
605*6777b538SAndroid Build Coastguard Worker // truncated data.
606*6777b538SAndroid Build Coastguard Worker *have_enough_content &= is_truncated;
607*6777b538SAndroid Build Coastguard Worker result->assign("text/plain");
608*6777b538SAndroid Build Coastguard Worker return false;
609*6777b538SAndroid Build Coastguard Worker }
610*6777b538SAndroid Build Coastguard Worker
IsUnknownMimeType(std::string_view mime_type)611*6777b538SAndroid Build Coastguard Worker static bool IsUnknownMimeType(std::string_view mime_type) {
612*6777b538SAndroid Build Coastguard Worker // TODO(tc): Maybe reuse some code in net/http/http_response_headers.* here.
613*6777b538SAndroid Build Coastguard Worker // If we do, please be careful not to alter the semantics at all.
614*6777b538SAndroid Build Coastguard Worker static const char* const kUnknownMimeTypes[] = {
615*6777b538SAndroid Build Coastguard Worker // Empty mime types are as unknown as they get.
616*6777b538SAndroid Build Coastguard Worker "",
617*6777b538SAndroid Build Coastguard Worker // The unknown/unknown type is popular and uninformative
618*6777b538SAndroid Build Coastguard Worker "unknown/unknown",
619*6777b538SAndroid Build Coastguard Worker // The second most popular unknown mime type is application/unknown
620*6777b538SAndroid Build Coastguard Worker "application/unknown",
621*6777b538SAndroid Build Coastguard Worker // Firefox rejects a mime type if it is exactly */*
622*6777b538SAndroid Build Coastguard Worker "*/*",
623*6777b538SAndroid Build Coastguard Worker };
624*6777b538SAndroid Build Coastguard Worker for (const char* const unknown_mime_type : kUnknownMimeTypes) {
625*6777b538SAndroid Build Coastguard Worker if (mime_type == unknown_mime_type)
626*6777b538SAndroid Build Coastguard Worker return true;
627*6777b538SAndroid Build Coastguard Worker }
628*6777b538SAndroid Build Coastguard Worker if (mime_type.find('/') == std::string_view::npos) {
629*6777b538SAndroid Build Coastguard Worker // Firefox rejects a mime type if it does not contain a slash
630*6777b538SAndroid Build Coastguard Worker return true;
631*6777b538SAndroid Build Coastguard Worker }
632*6777b538SAndroid Build Coastguard Worker return false;
633*6777b538SAndroid Build Coastguard Worker }
634*6777b538SAndroid Build Coastguard Worker
635*6777b538SAndroid Build Coastguard Worker // Returns true and sets result if the content appears to be a crx (Chrome
636*6777b538SAndroid Build Coastguard Worker // extension) file.
637*6777b538SAndroid Build Coastguard Worker // Clears have_enough_content if more data could possibly change the result.
SniffCRX(std::string_view content,const GURL & url,bool * have_enough_content,std::string * result)638*6777b538SAndroid Build Coastguard Worker static bool SniffCRX(std::string_view content,
639*6777b538SAndroid Build Coastguard Worker const GURL& url,
640*6777b538SAndroid Build Coastguard Worker bool* have_enough_content,
641*6777b538SAndroid Build Coastguard Worker std::string* result) {
642*6777b538SAndroid Build Coastguard Worker // Technically, the crx magic number is just Cr24, but the bytes after that
643*6777b538SAndroid Build Coastguard Worker // are a version number which changes infrequently. Including it in the
644*6777b538SAndroid Build Coastguard Worker // sniffing gives us less room for error. If the version number ever changes,
645*6777b538SAndroid Build Coastguard Worker // we can just add an entry to this list.
646*6777b538SAndroid Build Coastguard Worker static const struct MagicNumber kCRXMagicNumbers[] = {
647*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("application/x-chrome-extension", "Cr24\x02\x00\x00\x00"),
648*6777b538SAndroid Build Coastguard Worker MAGIC_NUMBER("application/x-chrome-extension", "Cr24\x03\x00\x00\x00")};
649*6777b538SAndroid Build Coastguard Worker
650*6777b538SAndroid Build Coastguard Worker // Only consider files that have the extension ".crx".
651*6777b538SAndroid Build Coastguard Worker if (!url.path_piece().ends_with(".crx")) {
652*6777b538SAndroid Build Coastguard Worker return false;
653*6777b538SAndroid Build Coastguard Worker }
654*6777b538SAndroid Build Coastguard Worker
655*6777b538SAndroid Build Coastguard Worker *have_enough_content &= TruncateStringPiece(kBytesRequiredForMagic, &content);
656*6777b538SAndroid Build Coastguard Worker return CheckForMagicNumbers(content, kCRXMagicNumbers, result);
657*6777b538SAndroid Build Coastguard Worker }
658*6777b538SAndroid Build Coastguard Worker
ShouldSniffMimeType(const GURL & url,std::string_view mime_type)659*6777b538SAndroid Build Coastguard Worker bool ShouldSniffMimeType(const GURL& url, std::string_view mime_type) {
660*6777b538SAndroid Build Coastguard Worker bool sniffable_scheme = url.is_empty() || url.SchemeIsHTTPOrHTTPS() ||
661*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID)
662*6777b538SAndroid Build Coastguard Worker url.SchemeIs("content") ||
663*6777b538SAndroid Build Coastguard Worker #endif
664*6777b538SAndroid Build Coastguard Worker url.SchemeIsFile() || url.SchemeIsFileSystem();
665*6777b538SAndroid Build Coastguard Worker if (!sniffable_scheme)
666*6777b538SAndroid Build Coastguard Worker return false;
667*6777b538SAndroid Build Coastguard Worker
668*6777b538SAndroid Build Coastguard Worker static const char* const kSniffableTypes[] = {
669*6777b538SAndroid Build Coastguard Worker // Many web servers are misconfigured to send text/plain for many
670*6777b538SAndroid Build Coastguard Worker // different types of content.
671*6777b538SAndroid Build Coastguard Worker "text/plain",
672*6777b538SAndroid Build Coastguard Worker // We want to sniff application/octet-stream for
673*6777b538SAndroid Build Coastguard Worker // application/x-chrome-extension, but nothing else.
674*6777b538SAndroid Build Coastguard Worker "application/octet-stream",
675*6777b538SAndroid Build Coastguard Worker // XHTML and Atom/RSS feeds are often served as plain xml instead of
676*6777b538SAndroid Build Coastguard Worker // their more specific mime types.
677*6777b538SAndroid Build Coastguard Worker "text/xml",
678*6777b538SAndroid Build Coastguard Worker "application/xml",
679*6777b538SAndroid Build Coastguard Worker // Check for false Microsoft Office MIME types.
680*6777b538SAndroid Build Coastguard Worker "application/msword",
681*6777b538SAndroid Build Coastguard Worker "application/vnd.ms-excel",
682*6777b538SAndroid Build Coastguard Worker "application/vnd.ms-powerpoint",
683*6777b538SAndroid Build Coastguard Worker "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
684*6777b538SAndroid Build Coastguard Worker "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
685*6777b538SAndroid Build Coastguard Worker "application/vnd.openxmlformats-officedocument.presentationml.presentation",
686*6777b538SAndroid Build Coastguard Worker "application/vnd.ms-excel.sheet.macroenabled.12",
687*6777b538SAndroid Build Coastguard Worker "application/vnd.ms-word.document.macroenabled.12",
688*6777b538SAndroid Build Coastguard Worker "application/vnd.ms-powerpoint.presentation.macroenabled.12",
689*6777b538SAndroid Build Coastguard Worker "application/mspowerpoint",
690*6777b538SAndroid Build Coastguard Worker "application/msexcel",
691*6777b538SAndroid Build Coastguard Worker "application/vnd.ms-word",
692*6777b538SAndroid Build Coastguard Worker "application/vnd.ms-word.document.12",
693*6777b538SAndroid Build Coastguard Worker "application/vnd.msword",
694*6777b538SAndroid Build Coastguard Worker };
695*6777b538SAndroid Build Coastguard Worker for (const char* const sniffable_type : kSniffableTypes) {
696*6777b538SAndroid Build Coastguard Worker if (mime_type == sniffable_type)
697*6777b538SAndroid Build Coastguard Worker return true;
698*6777b538SAndroid Build Coastguard Worker }
699*6777b538SAndroid Build Coastguard Worker if (IsUnknownMimeType(mime_type)) {
700*6777b538SAndroid Build Coastguard Worker // The web server didn't specify a content type or specified a mime
701*6777b538SAndroid Build Coastguard Worker // type that we ignore.
702*6777b538SAndroid Build Coastguard Worker return true;
703*6777b538SAndroid Build Coastguard Worker }
704*6777b538SAndroid Build Coastguard Worker return false;
705*6777b538SAndroid Build Coastguard Worker }
706*6777b538SAndroid Build Coastguard Worker
SniffMimeType(std::string_view content,const GURL & url,const std::string & type_hint,ForceSniffFileUrlsForHtml force_sniff_file_url_for_html,std::string * result)707*6777b538SAndroid Build Coastguard Worker bool SniffMimeType(std::string_view content,
708*6777b538SAndroid Build Coastguard Worker const GURL& url,
709*6777b538SAndroid Build Coastguard Worker const std::string& type_hint,
710*6777b538SAndroid Build Coastguard Worker ForceSniffFileUrlsForHtml force_sniff_file_url_for_html,
711*6777b538SAndroid Build Coastguard Worker std::string* result) {
712*6777b538SAndroid Build Coastguard Worker // Sanity check.
713*6777b538SAndroid Build Coastguard Worker DCHECK_LT(content.length(), 1000000U);
714*6777b538SAndroid Build Coastguard Worker DCHECK(result);
715*6777b538SAndroid Build Coastguard Worker
716*6777b538SAndroid Build Coastguard Worker // By default, we assume we have enough content.
717*6777b538SAndroid Build Coastguard Worker // Each sniff routine may unset this if it wasn't provided enough content.
718*6777b538SAndroid Build Coastguard Worker bool have_enough_content = true;
719*6777b538SAndroid Build Coastguard Worker
720*6777b538SAndroid Build Coastguard Worker // By default, we'll return the type hint.
721*6777b538SAndroid Build Coastguard Worker // Each sniff routine may modify this if it has a better guess..
722*6777b538SAndroid Build Coastguard Worker result->assign(type_hint);
723*6777b538SAndroid Build Coastguard Worker
724*6777b538SAndroid Build Coastguard Worker // If the file has a Microsoft Office MIME type, we should only check that it
725*6777b538SAndroid Build Coastguard Worker // is a valid Office file. Because this is the only reason we sniff files
726*6777b538SAndroid Build Coastguard Worker // with a Microsoft Office MIME type, we can return early.
727*6777b538SAndroid Build Coastguard Worker if (IsOfficeType(type_hint))
728*6777b538SAndroid Build Coastguard Worker return SniffForInvalidOfficeDocs(content, url, result);
729*6777b538SAndroid Build Coastguard Worker
730*6777b538SAndroid Build Coastguard Worker // Cache information about the type_hint
731*6777b538SAndroid Build Coastguard Worker bool hint_is_unknown_mime_type = IsUnknownMimeType(type_hint);
732*6777b538SAndroid Build Coastguard Worker
733*6777b538SAndroid Build Coastguard Worker // First check for HTML, unless it's a file URL and
734*6777b538SAndroid Build Coastguard Worker // |allow_sniffing_files_urls_as_html| is false.
735*6777b538SAndroid Build Coastguard Worker if (hint_is_unknown_mime_type &&
736*6777b538SAndroid Build Coastguard Worker (!url.SchemeIsFile() ||
737*6777b538SAndroid Build Coastguard Worker force_sniff_file_url_for_html == ForceSniffFileUrlsForHtml::kEnabled)) {
738*6777b538SAndroid Build Coastguard Worker // We're only willing to sniff HTML if the server has not supplied a mime
739*6777b538SAndroid Build Coastguard Worker // type, or if the type it did supply indicates that it doesn't know what
740*6777b538SAndroid Build Coastguard Worker // the type should be.
741*6777b538SAndroid Build Coastguard Worker if (SniffForHTML(content, &have_enough_content, result))
742*6777b538SAndroid Build Coastguard Worker return true; // We succeeded in sniffing HTML. No more content needed.
743*6777b538SAndroid Build Coastguard Worker }
744*6777b538SAndroid Build Coastguard Worker
745*6777b538SAndroid Build Coastguard Worker // We're only willing to sniff for binary in 3 cases:
746*6777b538SAndroid Build Coastguard Worker // 1. The server has not supplied a mime type.
747*6777b538SAndroid Build Coastguard Worker // 2. The type it did supply indicates that it doesn't know what the type
748*6777b538SAndroid Build Coastguard Worker // should be.
749*6777b538SAndroid Build Coastguard Worker // 3. The type is "text/plain" which is the default on some web servers and
750*6777b538SAndroid Build Coastguard Worker // could be indicative of a mis-configuration that we shield the user from.
751*6777b538SAndroid Build Coastguard Worker const bool hint_is_text_plain = (type_hint == "text/plain");
752*6777b538SAndroid Build Coastguard Worker if (hint_is_unknown_mime_type || hint_is_text_plain) {
753*6777b538SAndroid Build Coastguard Worker if (!SniffBinary(content, &have_enough_content, result)) {
754*6777b538SAndroid Build Coastguard Worker // If the server said the content was text/plain and it doesn't appear
755*6777b538SAndroid Build Coastguard Worker // to be binary, then we trust it.
756*6777b538SAndroid Build Coastguard Worker if (hint_is_text_plain) {
757*6777b538SAndroid Build Coastguard Worker return have_enough_content;
758*6777b538SAndroid Build Coastguard Worker }
759*6777b538SAndroid Build Coastguard Worker }
760*6777b538SAndroid Build Coastguard Worker }
761*6777b538SAndroid Build Coastguard Worker
762*6777b538SAndroid Build Coastguard Worker // If we have plain XML, sniff XML subtypes.
763*6777b538SAndroid Build Coastguard Worker if (type_hint == "text/xml" || type_hint == "application/xml") {
764*6777b538SAndroid Build Coastguard Worker // We're not interested in sniffing these types for images and the like.
765*6777b538SAndroid Build Coastguard Worker // Instead, we're looking explicitly for a feed. If we don't find one
766*6777b538SAndroid Build Coastguard Worker // we're done and return early.
767*6777b538SAndroid Build Coastguard Worker if (SniffXML(content, &have_enough_content, result))
768*6777b538SAndroid Build Coastguard Worker return true;
769*6777b538SAndroid Build Coastguard Worker return have_enough_content;
770*6777b538SAndroid Build Coastguard Worker }
771*6777b538SAndroid Build Coastguard Worker
772*6777b538SAndroid Build Coastguard Worker // CRX files (Chrome extensions) have a special sniffing algorithm. It is
773*6777b538SAndroid Build Coastguard Worker // tighter than the others because we don't have to match legacy behavior.
774*6777b538SAndroid Build Coastguard Worker if (SniffCRX(content, url, &have_enough_content, result))
775*6777b538SAndroid Build Coastguard Worker return true;
776*6777b538SAndroid Build Coastguard Worker
777*6777b538SAndroid Build Coastguard Worker // Check the file extension and magic numbers to see if this is an Office
778*6777b538SAndroid Build Coastguard Worker // document. This needs to be checked before the general magic numbers
779*6777b538SAndroid Build Coastguard Worker // because zip files and Office documents (OOXML) have the same magic number.
780*6777b538SAndroid Build Coastguard Worker if (SniffForOfficeDocs(content, url, &have_enough_content, result)) {
781*6777b538SAndroid Build Coastguard Worker return true; // We've matched a magic number. No more content needed.
782*6777b538SAndroid Build Coastguard Worker }
783*6777b538SAndroid Build Coastguard Worker
784*6777b538SAndroid Build Coastguard Worker // We're not interested in sniffing for magic numbers when the type_hint
785*6777b538SAndroid Build Coastguard Worker // is application/octet-stream. Time to bail out.
786*6777b538SAndroid Build Coastguard Worker if (type_hint == "application/octet-stream")
787*6777b538SAndroid Build Coastguard Worker return have_enough_content;
788*6777b538SAndroid Build Coastguard Worker
789*6777b538SAndroid Build Coastguard Worker // Now we look in our large table of magic numbers to see if we can find
790*6777b538SAndroid Build Coastguard Worker // anything that matches the content.
791*6777b538SAndroid Build Coastguard Worker if (SniffForMagicNumbers(content, &have_enough_content, result))
792*6777b538SAndroid Build Coastguard Worker return true; // We've matched a magic number. No more content needed.
793*6777b538SAndroid Build Coastguard Worker
794*6777b538SAndroid Build Coastguard Worker return have_enough_content;
795*6777b538SAndroid Build Coastguard Worker }
796*6777b538SAndroid Build Coastguard Worker
SniffMimeTypeFromLocalData(std::string_view content,std::string * result)797*6777b538SAndroid Build Coastguard Worker bool SniffMimeTypeFromLocalData(std::string_view content, std::string* result) {
798*6777b538SAndroid Build Coastguard Worker // First check the extra table.
799*6777b538SAndroid Build Coastguard Worker if (CheckForMagicNumbers(content, kExtraMagicNumbers, result))
800*6777b538SAndroid Build Coastguard Worker return true;
801*6777b538SAndroid Build Coastguard Worker // Finally check the original table.
802*6777b538SAndroid Build Coastguard Worker return CheckForMagicNumbers(content, kMagicNumbers, result);
803*6777b538SAndroid Build Coastguard Worker }
804*6777b538SAndroid Build Coastguard Worker
LooksLikeBinary(std::string_view content)805*6777b538SAndroid Build Coastguard Worker bool LooksLikeBinary(std::string_view content) {
806*6777b538SAndroid Build Coastguard Worker // The definition of "binary bytes" is from the spec at
807*6777b538SAndroid Build Coastguard Worker // https://mimesniff.spec.whatwg.org/#binary-data-byte
808*6777b538SAndroid Build Coastguard Worker //
809*6777b538SAndroid Build Coastguard Worker // The bytes which are considered to be "binary" are all < 0x20. Encode them
810*6777b538SAndroid Build Coastguard Worker // one bit per byte, with 1 for a "binary" bit, and 0 for a "text" bit. The
811*6777b538SAndroid Build Coastguard Worker // least-significant bit represents byte 0x00, the most-significant bit
812*6777b538SAndroid Build Coastguard Worker // represents byte 0x1F.
813*6777b538SAndroid Build Coastguard Worker const uint32_t kBinaryBits =
814*6777b538SAndroid Build Coastguard Worker ~(1u << '\t' | 1u << '\n' | 1u << '\r' | 1u << '\f' | 1u << '\x1b');
815*6777b538SAndroid Build Coastguard Worker for (char c : content) {
816*6777b538SAndroid Build Coastguard Worker uint8_t byte = static_cast<uint8_t>(c);
817*6777b538SAndroid Build Coastguard Worker if (byte < 0x20 && (kBinaryBits & (1u << byte)))
818*6777b538SAndroid Build Coastguard Worker return true;
819*6777b538SAndroid Build Coastguard Worker }
820*6777b538SAndroid Build Coastguard Worker return false;
821*6777b538SAndroid Build Coastguard Worker }
822*6777b538SAndroid Build Coastguard Worker
823*6777b538SAndroid Build Coastguard Worker } // namespace net
824