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