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