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