• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/http/http_content_disposition.h"
6 
7 #include "base/strings/utf_string_conversions.h"
8 #include "testing/gtest/include/gtest/gtest.h"
9 
10 namespace net {
11 
12 namespace {
13 
14 struct FileNameCDCase {
15   const char* header;
16   const char* referrer_charset;
17   const wchar_t* expected;
18 };
19 
20 }  // anonymous namespace
21 
TEST(HttpContentDispositionTest,Filename)22 TEST(HttpContentDispositionTest, Filename) {
23   const FileNameCDCase tests[] = {
24     // Test various forms of C-D header fields emitted by web servers.
25     {"inline; filename=\"abcde.pdf\"", "", L"abcde.pdf"},
26     {"attachment; filename=abcde.pdf", "", L"abcde.pdf"},
27     {"attachment; filename=abc,de.pdf", "", L"abc,de.pdf"},
28     {"filename=abcde.pdf", "", L"abcde.pdf"},
29     {"filename= abcde.pdf", "", L"abcde.pdf"},
30     {"filename =abcde.pdf", "", L"abcde.pdf"},
31     {"filename = abcde.pdf", "", L"abcde.pdf"},
32     {"filename\t=abcde.pdf", "", L"abcde.pdf"},
33     {"filename \t\t  =abcde.pdf", "", L"abcde.pdf"},
34     {"inline; filename=\"abc%20de.pdf\"", "",
35      L"abc de.pdf"},
36     // Name values are no longer synonyms for filename.
37     {"inline; name=\"abcde.pdf\"", "", L""},
38     {"attachment; name=abcde.pdf", "", L""},
39     {"name=abcde.pdf", "", L""},
40     // Unbalanced quotation mark
41     {"filename=\"abcdef.pdf", "", L"abcdef.pdf"},
42     // Whitespaces are converted to a space.
43     {"inline; filename=\"abc  \t\nde.pdf\"", "",
44      L"abc    de.pdf"},
45     // %-escaped UTF-8
46     {"attachment; filename=\"%EC%98%88%EC%88%A0%20"
47      "%EC%98%88%EC%88%A0.jpg\"", "", L"\xc608\xc220 \xc608\xc220.jpg"},
48     {"attachment; filename=\"%F0%90%8C%B0%F0%90%8C%B1"
49      "abc.jpg\"", "", L"\U00010330\U00010331abc.jpg"},
50     {"attachment; filename=\"%EC%98%88%EC%88%A0 \n"
51      "%EC%98%88%EC%88%A0.jpg\"", "", L"\xc608\xc220  \xc608\xc220.jpg"},
52     // Characters that are not supposed to be displayed should still be decoded.
53     {"attachment; filename=%E5%B2%A1%E3%80%80%E5%B2%A1.txt", "",
54      L"\u5ca1\u3000\u5ca1.txt"},
55     // RFC 2047 with various charsets and Q/B encodings
56     {"attachment; filename=\"=?EUC-JP?Q?=B7=DD=BD="
57      "D13=2Epng?=\"", "", L"\x82b8\x8853" L"3.png"},
58     {"attachment; filename==?eUc-Kr?b?v7m8+iAzLnBuZw==?=",
59      "", L"\xc608\xc220 3.png"},
60     {"attachment; filename==?utf-8?Q?=E8=8A=B8=E8"
61      "=A1=93_3=2Epng?=", "", L"\x82b8\x8853 3.png"},
62     {"attachment; filename==?utf-8?Q?=F0=90=8C=B0"
63      "_3=2Epng?=", "", L"\U00010330 3.png"},
64     {"inline; filename=\"=?iso88591?Q?caf=e9_=2epng?=\"",
65      "", L"caf\x00e9 .png"},
66     // Space after an encoded word should be removed.
67     {"inline; filename=\"=?iso88591?Q?caf=E9_?= .png\"",
68      "", L"caf\x00e9 .png"},
69     // Two encoded words with different charsets (not very likely to be emitted
70     // by web servers in the wild). Spaces between them are removed.
71     {"inline; filename=\"=?euc-kr?b?v7m8+iAz?="
72      " =?ksc5601?q?=BF=B9=BC=FA=2Epng?=\"", "",
73      L"\xc608\xc220 3\xc608\xc220.png"},
74     {"attachment; filename=\"=?windows-1252?Q?caf=E9?="
75      "  =?iso-8859-7?b?4eI=?= .png\"", "", L"caf\x00e9\x03b1\x03b2.png"},
76     // Non-ASCII string is passed through and treated as UTF-8 as long as
77     // it's valid as UTF-8 and regardless of |referrer_charset|.
78     {"attachment; filename=caf\xc3\xa9.png",
79      "iso-8859-1", L"caf\x00e9.png"},
80     {"attachment; filename=caf\xc3\xa9.png",
81      "", L"caf\x00e9.png"},
82     // Non-ASCII/Non-UTF-8 string. Fall back to the referrer charset.
83     {"attachment; filename=caf\xe5.png",
84      "windows-1253", L"caf\x03b5.png"},
85 #if 0
86     // Non-ASCII/Non-UTF-8 string. Fall back to the native codepage.
87     // TODO(jungshik): We need to set the OS default codepage
88     // to a specific value before testing. On Windows, we can use
89     // SetThreadLocale().
90     {"attachment; filename=\xb0\xa1\xb0\xa2.png",
91      "", L"\xac00\xac01.png"},
92 #endif
93     // Failure cases
94     // Invalid hex-digit "G"
95     {"attachment; filename==?iiso88591?Q?caf=EG?=", "",
96      L""},
97     // Incomplete RFC 2047 encoded-word (missing '='' at the end)
98     {"attachment; filename==?iso88591?Q?caf=E3?", "", L""},
99     // Extra character at the end of an encoded word
100     {"attachment; filename==?iso88591?Q?caf=E3?==",
101      "", L""},
102     // Extra token at the end of an encoded word
103     {"attachment; filename==?iso88591?Q?caf=E3?=?",
104      "", L""},
105     {"attachment; filename==?iso88591?Q?caf=E3?=?=",
106      "",  L""},
107     // Incomplete hex-escaped chars
108     {"attachment; filename==?windows-1252?Q?=63=61=E?=",
109      "", L""},
110     {"attachment; filename=%EC%98%88%EC%88%A", "", L""},
111     // %-escaped non-UTF-8 encoding is an "error"
112     {"attachment; filename=%B7%DD%BD%D1.png", "", L""},
113     // Two RFC 2047 encoded words in a row without a space is an error.
114     {"attachment; filename==?windows-1252?Q?caf=E3?="
115      "=?iso-8859-7?b?4eIucG5nCg==?=", "", L""},
116 
117     // RFC 5987 tests with Filename*  : see http://tools.ietf.org/html/rfc5987
118     {"attachment; filename*=foo.html", "", L""},
119     {"attachment; filename*=foo'.html", "", L""},
120     {"attachment; filename*=''foo'.html", "", L""},
121     {"attachment; filename*=''foo.html'", "", L""},
122     {"attachment; filename*=''f\"oo\".html'", "", L""},
123     {"attachment; filename*=bogus_charset''foo.html'",
124      "", L""},
125     {"attachment; filename*='en'foo.html'", "", L""},
126     {"attachment; filename*=iso-8859-1'en'foo.html", "",
127       L"foo.html"},
128     {"attachment; filename*=utf-8'en'foo.html", "",
129       L"foo.html"},
130     {"attachment; filename*=utf-8'en'%E5%B2%A1%E3%80%80%E5%B2%A1.txt", "",
131      L"\u5ca1\u3000\u5ca1.txt"},
132     // charset cannot be omitted.
133     {"attachment; filename*='es'f\xfa.html'", "", L""},
134     // Non-ASCII bytes are not allowed.
135     {"attachment; filename*=iso-8859-1'es'f\xfa.html", "",
136       L""},
137     {"attachment; filename*=utf-8'es'f\xce\xba.html", "",
138       L""},
139     // TODO(jshin): Space should be %-encoded, but currently, we allow
140     // spaces.
141     {"inline; filename*=iso88591''cafe foo.png", "",
142       L"cafe foo.png"},
143 
144     // Filename* tests converted from Q-encoded tests above.
145     {"attachment; filename*=EUC-JP''%B7%DD%BD%D13%2Epng",
146      "", L"\x82b8\x8853" L"3.png"},
147     {"attachment; filename*=utf-8''"
148       "%E8%8A%B8%E8%A1%93%203%2Epng", "", L"\x82b8\x8853 3.png"},
149     {"attachment; filename*=utf-8''%F0%90%8C%B0 3.png", "",
150       L"\U00010330 3.png"},
151     {"inline; filename*=Euc-Kr'ko'%BF%B9%BC%FA%2Epng", "",
152      L"\xc608\xc220.png"},
153     {"attachment; filename*=windows-1252''caf%E9.png", "",
154       L"caf\x00e9.png"},
155 
156     // Multiple filename, filename*, name parameters specified.
157     {"attachment; name=\"foo\"; filename=\"bar\"", "", L"bar"},
158     {"attachment; filename=\"bar\"; name=\"foo\"", "", L"bar"},
159     {"attachment; filename=\"bar\"; filename*=utf-8''baz", "", L"baz"},
160 
161     // http://greenbytes.de/tech/tc2231/ filename* test cases.
162     // attwithisofn2231iso
163     {"attachment; filename*=iso-8859-1''foo-%E4.html", "",
164       L"foo-\xe4.html"},
165     // attwithfn2231utf8
166     {"attachment; filename*="
167       "UTF-8''foo-%c3%a4-%e2%82%ac.html", "", L"foo-\xe4-\x20ac.html"},
168     // attwithfn2231noc : no encoding specified but UTF-8 is used.
169     {"attachment; filename*=''foo-%c3%a4-%e2%82%ac.html",
170       "", L""},
171     // attwithfn2231utf8comp
172     {"attachment; filename*=UTF-8''foo-a%cc%88.html", "",
173       L"foo-\xe4.html"},
174 #ifdef ICU_SHOULD_FAIL_CONVERSION_ON_INVALID_CHARACTER
175     // This does not work because we treat ISO-8859-1 synonymous with
176     // Windows-1252 per HTML5. For HTTP, in theory, we're not
177     // supposed to.
178     // attwithfn2231utf8-bad
179     {"attachment; filename*="
180       "iso-8859-1''foo-%c3%a4-%e2%82%ac.html", "", L""},
181 #endif
182     // attwithfn2231ws1
183     {"attachment; filename *=UTF-8''foo-%c3%a4.html", "",
184       L""},
185     // attwithfn2231ws2
186     {"attachment; filename*= UTF-8''foo-%c3%a4.html", "",
187       L"foo-\xe4.html"},
188     // attwithfn2231ws3
189     {"attachment; filename* =UTF-8''foo-%c3%a4.html", "",
190       L"foo-\xe4.html"},
191     // attwithfn2231quot
192     {"attachment; filename*=\"UTF-8''foo-%c3%a4.html\"",
193       "", L""},
194     // attfnboth
195     {"attachment; filename=\"foo-ae.html\"; "
196       "filename*=UTF-8''foo-%c3%a4.html", "", L"foo-\xe4.html"},
197     // attfnboth2
198     {"attachment; filename*=UTF-8''foo-%c3%a4.html; "
199       "filename=\"foo-ae.html\"", "", L"foo-\xe4.html"},
200     // attnewandfn
201     {"attachment; foobar=x; filename=\"foo.html\"", "",
202       L"foo.html"},
203   };
204   for (const auto& test : tests) {
205     HttpContentDisposition header(test.header, test.referrer_charset);
206     EXPECT_EQ(test.expected, base::UTF8ToWide(header.filename()))
207         << "Failed on input: " << test.header;
208   }
209 }
210 
211 // Test cases from http://greenbytes.de/tech/tc2231/
TEST(HttpContentDispositionTest,tc2231)212 TEST(HttpContentDispositionTest, tc2231) {
213   const struct FileNameCDCase {
214     const char* header;
215     HttpContentDisposition::Type expected_type;
216     const wchar_t* expected_filename;
217   } tests[] = {
218       // http://greenbytes.de/tech/tc2231/#inlonly
219       {"inline", HttpContentDisposition::INLINE, L""},
220       // http://greenbytes.de/tech/tc2231/#inlonlyquoted
221       {"\"inline\"", HttpContentDisposition::INLINE, L""},
222       // http://greenbytes.de/tech/tc2231/#inlwithasciifilename
223       {"inline; filename=\"foo.html\"", HttpContentDisposition::INLINE,
224        L"foo.html"},
225       // http://greenbytes.de/tech/tc2231/#inlwithfnattach
226       {"inline; filename=\"Not an attachment!\"",
227        HttpContentDisposition::INLINE, L"Not an attachment!"},
228       // http://greenbytes.de/tech/tc2231/#inlwithasciifilenamepdf
229       {"inline; filename=\"foo.pdf\"", HttpContentDisposition::INLINE,
230        L"foo.pdf"},
231       // http://greenbytes.de/tech/tc2231/#attonly
232       {"attachment", HttpContentDisposition::ATTACHMENT, L""},
233       // http://greenbytes.de/tech/tc2231/#attonlyquoted
234       {"\"attachment\"", HttpContentDisposition::INLINE, L""},
235       // http://greenbytes.de/tech/tc2231/#attonly403
236       // TODO(abarth): This isn't testable in this unit test.
237       // http://greenbytes.de/tech/tc2231/#attonlyucase
238       {"ATTACHMENT", HttpContentDisposition::ATTACHMENT, L""},
239       // http://greenbytes.de/tech/tc2231/#attwithasciifilename
240       {"attachment; filename=\"foo.html\"", HttpContentDisposition::ATTACHMENT,
241        L"foo.html"},
242       // http://greenbytes.de/tech/tc2231/#attwithasciifnescapedchar
243       {"attachment; filename=\"f\\oo.html\"",
244        HttpContentDisposition::ATTACHMENT, L"foo.html"},
245       // http://greenbytes.de/tech/tc2231/#attwithasciifnescapedquote
246       {"attachment; filename=\"\\\"quoting\\\" tested.html\"",
247        HttpContentDisposition::ATTACHMENT, L"\"quoting\" tested.html"},
248       // http://greenbytes.de/tech/tc2231/#attwithquotedsemicolon
249       {"attachment; filename=\"Here's a semicolon;.html\"",
250        HttpContentDisposition::ATTACHMENT, L"Here's a semicolon;.html"},
251       // http://greenbytes.de/tech/tc2231/#attwithfilenameandextparam
252       {"attachment; foo=\"bar\"; filename=\"foo.html\"",
253        HttpContentDisposition::ATTACHMENT, L"foo.html"},
254       // http://greenbytes.de/tech/tc2231/#attwithfilenameandextparamescaped
255       {"attachment; foo=\"\\\"\\\\\";filename=\"foo.html\"",
256        HttpContentDisposition::ATTACHMENT, L"foo.html"},
257       // http://greenbytes.de/tech/tc2231/#attwithasciifilenameucase
258       {"attachment; FILENAME=\"foo.html\"", HttpContentDisposition::ATTACHMENT,
259        L"foo.html"},
260       // http://greenbytes.de/tech/tc2231/#attwithasciifilenamenq
261       {"attachment; filename=foo.html", HttpContentDisposition::ATTACHMENT,
262        L"foo.html"},
263       // http://greenbytes.de/tech/tc2231/#attwithasciifilenamenqs
264       // Note: tc2231 says we should fail to parse this header.
265       {"attachment; filename=foo.html ;", HttpContentDisposition::ATTACHMENT,
266        L"foo.html"},
267       // http://greenbytes.de/tech/tc2231/#attemptyparam
268       // Note: tc2231 says we should fail to parse this header.
269       {"attachment; ;filename=foo", HttpContentDisposition::ATTACHMENT, L"foo"},
270       // http://greenbytes.de/tech/tc2231/#attwithasciifilenamenqws
271       // Note: tc2231 says we should fail to parse this header.
272       {"attachment; filename=foo bar.html", HttpContentDisposition::ATTACHMENT,
273        L"foo bar.html"},
274       // http://greenbytes.de/tech/tc2231/#attwithfntokensq
275       {"attachment; filename='foo.bar'", HttpContentDisposition::ATTACHMENT,
276        L"'foo.bar'"},
277 #ifdef ICU_SHOULD_FAIL_CONVERSION_ON_INVALID_CHARACTER
278       // http://greenbytes.de/tech/tc2231/#attwithisofnplain
279       {
280           "attachment; filename=\"foo-\xE4html\"",
281           HttpContentDisposition::ATTACHMENT,
282           L""  // Should be L"foo-\xE4.html"
283       },
284 #endif
285       // http://greenbytes.de/tech/tc2231/#attwithutf8fnplain
286       // Note: We'll UTF-8 decode the file name, even though tc2231 says not to.
287       {"attachment; filename=\"foo-\xC3\xA4.html\"",
288        HttpContentDisposition::ATTACHMENT, L"foo-\xE4.html"},
289       // http://greenbytes.de/tech/tc2231/#attwithfnrawpctenca
290       {
291           "attachment; filename=\"foo-%41.html\"",
292           HttpContentDisposition::ATTACHMENT,
293           L"foo-A.html"  // Should be L"foo-%41.html"
294       },
295       // http://greenbytes.de/tech/tc2231/#attwithfnusingpct
296       {"attachment; filename=\"50%.html\"", HttpContentDisposition::ATTACHMENT,
297        L"50%.html"},
298       // http://greenbytes.de/tech/tc2231/#attwithfnrawpctencaq
299       {
300           "attachment; filename=\"foo-%\\41.html\"",
301           HttpContentDisposition::ATTACHMENT,
302           L"foo-A.html"  // Should be L"foo-%41.html"
303       },
304       // http://greenbytes.de/tech/tc2231/#attwithnamepct
305       // Value is skipped like other UAs.
306       {"attachment; name=\"foo-%41.html\"", HttpContentDisposition::ATTACHMENT,
307        L""},
308 #ifdef ICU_SHOULD_FAIL_CONVERSION_ON_INVALID_CHARACTER
309       // http://greenbytes.de/tech/tc2231/#attwithfilenamepctandiso
310       {
311           "attachment; filename=\"\xE4-%41.html\"",
312           HttpContentDisposition::ATTACHMENT,
313           L""  // Should be L"\xE4-%41.htm"
314       },
315 #endif
316       // http://greenbytes.de/tech/tc2231/#attwithfnrawpctenclong
317       {
318           "attachment; filename=\"foo-%c3%a4-%e2%82%ac.html\"",
319           HttpContentDisposition::ATTACHMENT,
320           L"foo-\xE4-\u20AC.html"  // Should be L"foo-%c3%a4-%e2%82%ac.html"
321       },
322       // http://greenbytes.de/tech/tc2231/#attwithasciifilenamews1
323       {"attachment; filename =\"foo.html\"", HttpContentDisposition::ATTACHMENT,
324        L"foo.html"},
325       // http://greenbytes.de/tech/tc2231/#attwith2filenames
326       // Note: tc2231 says we should fail to parse this header.
327       {"attachment; filename=\"foo.html\"; filename=\"bar.html\"",
328        HttpContentDisposition::ATTACHMENT, L"foo.html"},
329       // http://greenbytes.de/tech/tc2231/#attfnbrokentoken
330       // Note: tc2231 says we should fail to parse this header.
331       {"attachment; filename=foo[1](2).html",
332        HttpContentDisposition::ATTACHMENT, L"foo[1](2).html"},
333 #ifdef ICU_SHOULD_FAIL_CONVERSION_ON_INVALID_CHARACTER
334       // http://greenbytes.de/tech/tc2231/#attfnbrokentokeniso
335       // Note: tc2231 says we should fail to parse this header.
336       {"attachment; filename=foo-\xE4.html", HttpContentDisposition::ATTACHMENT,
337        L""},
338 #endif
339       // http://greenbytes.de/tech/tc2231/#attfnbrokentokenutf
340       // Note: tc2231 says we should fail to parse this header.
341       {"attachment; filename=foo-\xC3\xA4.html",
342        HttpContentDisposition::ATTACHMENT, L"foo-\xE4.html"},
343       // http://greenbytes.de/tech/tc2231/#attmissingdisposition
344       // Note: tc2231 says we should fail to parse this header.
345       {"filename=foo.html", HttpContentDisposition::INLINE, L"foo.html"},
346       // http://greenbytes.de/tech/tc2231/#attmissingdisposition2
347       // Note: tc2231 says we should fail to parse this header.
348       {"x=y; filename=foo.html", HttpContentDisposition::INLINE, L"foo.html"},
349       // http://greenbytes.de/tech/tc2231/#attmissingdisposition3
350       // Note: tc2231 says we should fail to parse this header.
351       {
352           "\"foo; filename=bar;baz\"; filename=qux",
353           HttpContentDisposition::INLINE,
354           L""  // Firefox gets qux
355       },
356       // http://greenbytes.de/tech/tc2231/#attmissingdisposition4
357       // Note: tc2231 says we should fail to parse this header.
358       {"filename=foo.html, filename=bar.html", HttpContentDisposition::INLINE,
359        L"foo.html, filename=bar.html"},
360       // http://greenbytes.de/tech/tc2231/#emptydisposition
361       // Note: tc2231 says we should fail to parse this header.
362       {"; filename=foo.html", HttpContentDisposition::INLINE, L"foo.html"},
363       // http://greenbytes.de/tech/tc2231/#attandinline
364       // Note: tc2231 says we should fail to parse this header.
365       {"inline; attachment; filename=foo.html", HttpContentDisposition::INLINE,
366        L""},
367       // http://greenbytes.de/tech/tc2231/#attandinline2
368       // Note: tc2231 says we should fail to parse this header.
369       {"attachment; inline; filename=foo.html",
370        HttpContentDisposition::ATTACHMENT, L""},
371       // http://greenbytes.de/tech/tc2231/#attbrokenquotedfn
372       // Note: tc2231 says we should fail to parse this header.
373       {"attachment; filename=\"foo.html\".txt",
374        HttpContentDisposition::ATTACHMENT, L"foo.html\".txt"},
375       // http://greenbytes.de/tech/tc2231/#attbrokenquotedfn2
376       // Note: tc2231 says we should fail to parse this header.
377       {"attachment; filename=\"bar", HttpContentDisposition::ATTACHMENT,
378        L"bar"},
379       // http://greenbytes.de/tech/tc2231/#attbrokenquotedfn3
380       // Note: tc2231 says we should fail to parse this header.
381       {"attachment; filename=foo\"bar;baz\"qux",
382        HttpContentDisposition::ATTACHMENT, L"foo\"bar;baz\"qux"},
383       // http://greenbytes.de/tech/tc2231/#attmultinstances
384       // Note: tc2231 says we should fail to parse this header.
385       {"attachment; filename=foo.html, attachment; filename=bar.html",
386        HttpContentDisposition::ATTACHMENT, L"foo.html, attachment"},
387       // http://greenbytes.de/tech/tc2231/#attmissingdelim
388       {"attachment; foo=foo filename=bar", HttpContentDisposition::ATTACHMENT,
389        L""},
390       // http://greenbytes.de/tech/tc2231/#attreversed
391       // Note: tc2231 says we should fail to parse this header.
392       {"filename=foo.html; attachment", HttpContentDisposition::INLINE,
393        L"foo.html"},
394       // http://greenbytes.de/tech/tc2231/#attconfusedparam
395       {"attachment; xfilename=foo.html", HttpContentDisposition::ATTACHMENT,
396        L""},
397       // http://greenbytes.de/tech/tc2231/#attabspath
398       {"attachment; filename=\"/foo.html\"", HttpContentDisposition::ATTACHMENT,
399        L"/foo.html"},
400       // http://greenbytes.de/tech/tc2231/#attabspathwin
401       {"attachment; filename=\"\\\\foo.html\"",
402        HttpContentDisposition::ATTACHMENT, L"\\foo.html"},
403       // http://greenbytes.de/tech/tc2231/#dispext
404       {"foobar", HttpContentDisposition::ATTACHMENT, L""},
405       // http://greenbytes.de/tech/tc2231/#dispextbadfn
406       {"attachment; example=\"filename=example.txt\"",
407        HttpContentDisposition::ATTACHMENT, L""},
408       // http://greenbytes.de/tech/tc2231/#attnewandfn
409       {"attachment; foobar=x; filename=\"foo.html\"",
410        HttpContentDisposition::ATTACHMENT, L"foo.html"},
411       // TODO(abarth): Add the filename* tests, but check
412       //              HttpContentDispositionTest.Filename for overlap.
413       // TODO(abarth): http://greenbytes.de/tech/tc2231/#attrfc2047token
414       // TODO(abarth): http://greenbytes.de/tech/tc2231/#attrfc2047quoted
415   };
416   for (const auto& test : tests) {
417     HttpContentDisposition header(test.header, std::string());
418     EXPECT_EQ(test.expected_type, header.type())
419         << "Failed on input: " << test.header;
420     EXPECT_EQ(test.expected_filename, base::UTF8ToWide(header.filename()))
421         << "Failed on input: " << test.header;
422   }
423 }
424 
TEST(HttpContentDispositionTest,ParseResult)425 TEST(HttpContentDispositionTest, ParseResult) {
426   const struct ParseResultTestCase {
427     const char* header;
428     int expected_flags;
429   } kTestCases[] = {
430       // Basic feature tests
431       {"", HttpContentDisposition::INVALID},
432       {"example=x", HttpContentDisposition::INVALID},
433       {"attachment; filename=", HttpContentDisposition::HAS_DISPOSITION_TYPE},
434       {"attachment; name=", HttpContentDisposition::HAS_DISPOSITION_TYPE},
435       {"attachment; filename*=", HttpContentDisposition::HAS_DISPOSITION_TYPE},
436       {"attachment; filename==?utf-8?Q?\?=",
437        HttpContentDisposition::HAS_DISPOSITION_TYPE},
438       {"filename=x", HttpContentDisposition::HAS_FILENAME},
439       {"example; filename=x",
440        HttpContentDisposition::HAS_DISPOSITION_TYPE |
441            HttpContentDisposition::HAS_UNKNOWN_DISPOSITION_TYPE |
442            HttpContentDisposition::HAS_FILENAME},
443       {"attachment; filename=x", HttpContentDisposition::HAS_DISPOSITION_TYPE |
444                                      HttpContentDisposition::HAS_FILENAME},
445       {"attachment; filename='x'",
446        HttpContentDisposition::HAS_DISPOSITION_TYPE |
447            HttpContentDisposition::HAS_FILENAME |
448            HttpContentDisposition::HAS_SINGLE_QUOTED_FILENAME},
449       {"attachment; filename=x; name=y",
450        HttpContentDisposition::HAS_DISPOSITION_TYPE |
451            HttpContentDisposition::HAS_FILENAME},
452       {"attachment; name=y; filename*=utf-8''foo; name=x",
453        HttpContentDisposition::HAS_DISPOSITION_TYPE |
454            HttpContentDisposition::HAS_EXT_FILENAME},
455 
456       // Feature tests for 'filename' attribute.
457       {"filename=foo\xcc\x88",
458        HttpContentDisposition::HAS_FILENAME |
459            HttpContentDisposition::HAS_NON_ASCII_STRINGS},
460       {"filename=foo%cc%88",
461        HttpContentDisposition::HAS_FILENAME |
462            HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS},
463       {"filename==?utf-8?Q?foo?=",
464        HttpContentDisposition::HAS_FILENAME |
465            HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS},
466       {"filename=\"=?utf-8?Q?foo?=\"",
467        HttpContentDisposition::HAS_FILENAME |
468            HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS},
469       {"filename==?utf-8?Q?foo?", HttpContentDisposition::INVALID},
470 
471       // Test 'name' isn't a synonym for 'filename'.
472       {"name=foo\xcc\x88", HttpContentDisposition::INVALID},
473 
474       // Shouldn't set |has_non_ascii_strings| based on 'name' attribute.
475       {"filename=x; name=foo\xcc\x88", HttpContentDisposition::HAS_FILENAME},
476       {"filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?=",
477        HttpContentDisposition::HAS_FILENAME |
478            HttpContentDisposition::HAS_NON_ASCII_STRINGS |
479            HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS |
480            HttpContentDisposition::HAS_RFC2047_ENCODED_STRINGS},
481 
482       // If 'filename' attribute is invalid, should set any flags based on it.
483       {"filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?",
484        HttpContentDisposition::INVALID},
485       {"filename=foo\xcc\x88 foo%cc%88 =?utf-8?Q?foo?; name=x",
486        HttpContentDisposition::INVALID},
487   };
488 
489   for (size_t i = 0; i < std::size(kTestCases); ++i) {
490     const ParseResultTestCase& test_case = kTestCases[i];
491     HttpContentDisposition content_disposition(test_case.header, "utf-8");
492     int result = content_disposition.parse_result_flags();
493 
494     SCOPED_TRACE(testing::Message() << "Test case " << i
495                                     << " with header " << test_case.header);
496     EXPECT_EQ(test_case.expected_flags, result);
497   }
498 }
499 
TEST(HttpContentDispositionTest,ContainsNul)500 TEST(HttpContentDispositionTest, ContainsNul) {
501   const char kHeader[] = "filename=ab\0c";
502   const char kExpectedFilename[] = "ab\0c";
503   // Note: both header and expected_filename include the trailing NUL.
504   std::string header{kHeader, sizeof(kHeader)};
505   std::string expected_filename{kExpectedFilename, sizeof(kExpectedFilename)};
506 
507   HttpContentDisposition content_disposition(header, "utf-8");
508   EXPECT_EQ(expected_filename, content_disposition.filename());
509 }
510 
511 }  // namespace net
512