summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/mime/jsmime/test/unit/test_header.js
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/mime/jsmime/test/unit/test_header.js')
-rw-r--r--comm/mailnews/mime/jsmime/test/unit/test_header.js1224
1 files changed, 1224 insertions, 0 deletions
diff --git a/comm/mailnews/mime/jsmime/test/unit/test_header.js b/comm/mailnews/mime/jsmime/test/unit/test_header.js
new file mode 100644
index 0000000000..f00ec372ef
--- /dev/null
+++ b/comm/mailnews/mime/jsmime/test/unit/test_header.js
@@ -0,0 +1,1224 @@
+"use strict";
+define(function (require) {
+ var headerparser = require("jsmime").headerparser;
+ var assert = require("assert");
+
+ function smartDeepEqual(actual, expected) {
+ assert.deepEqual(actual, expected);
+ // XXX: instanceof Map don't work for actual. Unclear why.
+ if ("entries" in actual && "entries" in expected) {
+ assert.deepEqual(
+ Array.from(actual.entries()),
+ Array.from(expected.entries())
+ );
+ }
+ }
+
+ function arrayTest(data, fn) {
+ fn.toString = function () {
+ let text = Function.prototype.toString.call(this);
+ text = text.replace(/data\[([0-9]*)\]/g, function (m, p) {
+ return JSON.stringify(data[p]);
+ });
+ return text;
+ };
+ return test(data[0], fn);
+ }
+ suite("headerparser", function () {
+ suite("parseParameterHeader", function () {
+ let header_tests = [
+ ["multipart/related", ["multipart/related", {}]],
+ ["a ; b=v", ["a", { b: "v" }]],
+ ["a ; b='v'", ["a", { b: "'v'" }]],
+ ['a; b = "v"', ["a", { b: "v" }]],
+ ["a;b=1;b=2", ["a", { b: "1" }]],
+ ["a;b=2;b=1", ["a", { b: "2" }]],
+ ['a;b="a;b"', ["a", { b: "a;b" }]],
+ ['a;b="\\\\"', ["a", { b: "\\" }]],
+ ['a;b="a\\b\\c"', ["a", { b: "abc" }]],
+ ["a;b=1;c=2", ["a", { b: "1", c: "2" }]],
+ ['a;b="a\\', ["a", { b: "a" }]],
+ ["a;b", ["a", {}]],
+ ['a;b=";";c=d', ["a", { b: ";", c: "d" }]],
+ ];
+ header_tests.forEach(function (data) {
+ arrayTest(data, function () {
+ let testMap = new Map();
+ for (let key in data[1][1]) {
+ testMap.set(key, data[1][1][key]);
+ }
+ testMap.preSemi = data[1][0];
+ assert.deepEqual(
+ headerparser.parseParameterHeader(data[0], false, false),
+ testMap
+ );
+ });
+ });
+ });
+ suite("parseParameterHeader (2231/2047 support)", function () {
+ let header_tests = [
+ // Copied from test_MIME_params.js and adapted
+ ["attachment;", ["attachment", {}]],
+ ["attachment; filename=basic", ["attachment", { filename: "basic" }]],
+ ['attachment; filename="\\""', ["attachment", { filename: '"' }]],
+ ['attachment; filename="\\x"', ["attachment", { filename: "x" }]],
+ ['attachment; filename=""', ["attachment", { filename: "" }]],
+ ["attachment; filename=", ["attachment", { filename: "" }]],
+ ["attachment; filename X", ["attachment", {}]],
+ [
+ "attachment; filename = foo-A.html",
+ ["attachment", { filename: "foo-A.html" }],
+ ],
+ ['attachment; filename="', ["attachment", { filename: "" }]],
+ [
+ "attachment; filename=foo; trouble",
+ ["attachment", { filename: "foo" }],
+ ],
+ [
+ "attachment; filename=foo; trouble ",
+ ["attachment", { filename: "foo" }],
+ ],
+ ["attachment", ["attachment", {}]],
+ ["attachment; filename=foo", ["attachment", { filename: "foo" }]],
+ ['attachment; filename="foo"', ["attachment", { filename: "foo" }]],
+ ["attachment; filename='foo'", ["attachment", { filename: "'foo'" }]],
+ [
+ 'attachment; filename="=?UTF-8?Q?foo?="',
+ ["attachment", { filename: "foo" }],
+ ],
+ [
+ "attachment; filename==?UTF-8?Q?foo?=",
+ ["attachment", { filename: "foo" }],
+ ],
+ // 2231/5987 tests from test_MIME_params.js
+ [
+ "attachment; filename*=UTF-8''extended",
+ ["attachment", { filename: "extended" }],
+ ],
+ [
+ "attachment; filename=basic; filename*=UTF-8''extended",
+ ["attachment", { filename: "extended" }],
+ ],
+ [
+ "attachment; filename*=UTF-8''extended; filename=basic",
+ ["attachment", { filename: "extended" }],
+ ],
+ [
+ "attachment; filename*0=foo; filename*1=bar",
+ ["attachment", { filename: "foobar" }],
+ ],
+ [
+ "attachment; filename*0=first; filename*0=wrong; filename=basic",
+ ["attachment", { filename: "first" }],
+ ], // or basic?
+ [
+ "attachment; filename*0=first; filename*1=second; filename*0=wrong",
+ ["attachment", { filename: "firstsecond" }],
+ ], // or nothing?
+ [
+ "attachment; filename=basic; filename*0=foo; filename*1=bar",
+ ["attachment", { filename: "foobar" }],
+ ],
+ [
+ "attachment; filename=basic; filename*0=first; filename*0=wrong; " +
+ "filename*=UTF-8''extended",
+ ["attachment", { filename: "extended" }],
+ ],
+ [
+ "attachment; filename=basic; filename*=UTF-8''extended; filename*0=foo" +
+ "; filename*1=bar",
+ ["attachment", { filename: "extended" }],
+ ],
+ [
+ "attachment; filename*0=foo; filename*2=bar",
+ ["attachment", { filename: "foo" }],
+ ],
+ [
+ "attachment; filename*0=foo; filename*01=bar",
+ ["attachment", { filename: "foo" }],
+ ],
+ [
+ "attachment; filename=basic; filename*0*=UTF-8''multi; filename*1=line" +
+ "; filename*2*=%20extended",
+ ["attachment", { filename: "multiline extended" }],
+ ],
+ [
+ "attachment; filename=basic; filename*0*=UTF-8''multi; filename*1=line" +
+ "; filename*3*=%20extended",
+ ["attachment", { filename: "multiline" }],
+ ],
+ [
+ "attachment; filename=basic; filename*0*=UTF-8''multi; filename*1=line" +
+ "; filename*0*=UTF-8''wrong; filename*1=bad; filename*2=evil",
+ ["attachment", { filename: "multiline" }],
+ ],
+ [
+ "attachment; filename=basic; filename*0=UTF-8''multi; filename*=UTF-8'" +
+ "'extended; filename*1=line; filename*2*=%20extended",
+ ["attachment", { filename: "extended" }],
+ ],
+ [
+ "attachment; filename*0=UTF-8''unescaped; filename*1*=%20so%20includes" +
+ "%20UTF-8''%20in%20value",
+ [
+ "attachment",
+ { filename: "UTF-8''unescaped so includes UTF-8'' in value" },
+ ],
+ ],
+ [
+ "attachment; filename=basic; filename*0*=UTF-8''multi; filename*1=line" +
+ "; filename*0*=UTF-8''wrong; filename*1=bad; filename*2=evil",
+ ["attachment", { filename: "multiline" }],
+ ],
+ [
+ "attachment; filename=basic; filename*1=foo; filename*2=bar",
+ ["attachment", { filename: "basic" }],
+ ],
+ [
+ "attachment; filename=basic; filename*0*=UTF-8''0; filename*1=1; filen" +
+ "ame*2=2;filename*3=3;filename*4=4;filename*5=5;filename*6=6;filename" +
+ "*7=7;filename*8=8;filename*9=9;filename*10=a;filename*11=b;filename*" +
+ "12=c;filename*13=d;filename*14=e;filename*15=f",
+ ["attachment", { filename: "0123456789abcdef" }],
+ ],
+ [
+ "attachment; filename=basic; filename*0*=UTF-8''0; filename*1=1; filen" +
+ "ame*2=2;filename*3=3;filename*4=4;filename*5=5;filename*6=6;filename" +
+ "*7=7;filename*8=8;filename*9=9;filename*10=a;filename*11=b;filename*" +
+ "12=c;filename*14=e",
+ ["attachment", { filename: "0123456789abc" }],
+ ],
+ [
+ "attachment; filename*1=multi; filename*2=line; filename*3*=%20extended",
+ ["attachment", {}],
+ ],
+ [
+ "attachment; filename=basic; filename*0*=UTF-8''0; filename*1=1; filen" +
+ "ame*2=2;filename*3=3;filename*4=4;filename*5=5;filename*6=6;filename" +
+ "*7=7;filename*8=8;filename*9=9;filename*10=a;filename*11=b;filename*" +
+ "12=c;filename*13=d;filename*15=f;filename*14=e",
+ ["attachment", { filename: "0123456789abcdef" }],
+ ],
+ [
+ "attachment; filename=basic; filename*0*=UTF-8''0; filename*1a=1",
+ ["attachment", { filename: "0" }],
+ ],
+ [
+ "attachment; filename=basic; filename*0*=UTF-8''0; filename*1111111111" +
+ "1111111111111111111111111=1",
+ ["attachment", { filename: "0" }],
+ ],
+ [
+ "attachment; filename=basic; filename*0*=UTF-8''0; filename*-1=1",
+ ["attachment", { filename: "0" }],
+ ],
+ [
+ 'attachment; filename=basic; filename*0="0"; filename*1=1; filename*' +
+ "2*=%32",
+ ["attachment", { filename: "012" }],
+ ],
+ [
+ "attachment; filename=basic; filename**=UTF-8''0;",
+ ["attachment", { filename: "basic" }],
+ ],
+ [
+ "attachment; filename=IT839\x04\xB5(m8)2.pdf;",
+ ["attachment", { filename: "IT839\u00b5(m8)2.pdf" }],
+ ],
+ ["attachment; filename*=utf-8''%41", ["attachment", { filename: "A" }]],
+ // See bug 651185 and bug 703015
+ [
+ "attachment; filename*=\"utf-8''%41\"",
+ ["attachment", { filename: "A" }],
+ ],
+ ["attachment; filename *=utf-8''foo-%41", ["attachment", {}]],
+ ["attachment; filename*=''foo", ["attachment", {}]],
+ ["attachment; filename*=a''foo", ["attachment", {}]],
+ // Bug 692574: we should ignore this one...
+ [
+ "attachment; filename*=UTF-8'foo-%41",
+ ["attachment", { filename: "foo-A" }],
+ ],
+ ["attachment; filename*=foo-%41", ["attachment", {}]],
+ [
+ "attachment; filename*=UTF-8'foo-%41; filename=bar",
+ ["attachment", { filename: "foo-A" }],
+ ],
+ [
+ "attachment; filename*=ISO-8859-1''%c3%a4",
+ ["attachment", { filename: "\u00c3\u00a4" }],
+ ],
+ [
+ "attachment; filename*=ISO-8859-1''%e2%82%ac",
+ ["attachment", { filename: "\u00e2\u201a\u00ac" }],
+ ],
+ ["attachment; filename*=UTF-8''A%e4B", ["attachment", {}]],
+ [
+ "attachment; filename*=UTF-8''A%e4B; filename=fallback",
+ ["attachment", { filename: "fallback" }],
+ ],
+ [
+ "attachment; filename*0*=UTF-8''A%e4B; filename=fallback",
+ ["attachment", { filename: "fallback" }],
+ ],
+ [
+ "attachment; filename*0*=ISO-8859-15''euro-sign%3d%a4; filename*=ISO-8" +
+ "859-1''currency-sign%3d%a4",
+ ["attachment", { filename: "currency-sign=\u00a4" }],
+ ],
+ [
+ "attachment; filename*=ISO-8859-1''currency-sign%3d%a4; filename*0*=IS" +
+ "O-8859-15''euro-sign%3d%a4",
+ ["attachment", { filename: "currency-sign=\u00a4" }],
+ ],
+ [
+ 'attachment; filename=basic; filename*0="foo"; filename*1="\\b\\a\\' +
+ 'r"',
+ ["attachment", { filename: "foobar" }],
+ ],
+ [
+ 'attachment; filename=basic; filename*0="foo"; filename*1="\\b\\a\\',
+ ["attachment", { filename: "fooba" }],
+ ],
+ ['attachment; filename="\\b\\a\\', ["attachment", { filename: "ba" }]],
+ // According to comments and bugs, this works in necko, but it doesn't
+ // appear that it ought to. See bug 732369 for more info.
+ [
+ "attachment; extension=bla filename=foo",
+ ["attachment", { extension: "bla" }],
+ ],
+ [
+ "attachment; filename==?ISO-8859-1?Q?foo-=E4.html?=",
+ ["attachment", { filename: "foo-\u00e4.html" }],
+ ],
+ [
+ 'attachment; filename="=?ISO-8859-1?Q?foo-=E4.html?="',
+ ["attachment", { filename: "foo-\u00e4.html" }],
+ ],
+ [
+ 'attachment; filename="=?ISO-8859-1?Q?foo-=E4.html?="; filename*=UTF' +
+ "-8''5987",
+ ["attachment", { filename: "5987" }],
+ ],
+ // ABC\u202Etxt.zip dir switch char in middle.
+ [
+ "attachment; filename*=UTF-8''%41%42%43%E2%80%AE%2E%74%78%74%2E%7A%69%70",
+ ["attachment", { filename: "ABC .txt.zip" }],
+ ],
+ ];
+ header_tests.forEach(function (data) {
+ arrayTest(data, function () {
+ let testMap = new Map();
+ for (let key in data[1][1]) {
+ testMap.set(key, data[1][1][key]);
+ }
+ testMap.preSemi = data[1][0];
+ smartDeepEqual(
+ headerparser.parseParameterHeader(data[0], true, true),
+ testMap
+ );
+ });
+ });
+ });
+ suite("parseAddressingHeader", function () {
+ let header_tests = [
+ ["", []],
+ [
+ "Joe Schmoe <jschmoe@invalid.invalid>",
+ [{ name: "Joe Schmoe", email: "jschmoe@invalid.invalid" }],
+ ],
+ [
+ "user@tinderbox.invalid",
+ [{ name: "", email: "user@tinderbox.invalid" }],
+ ],
+ [
+ "Hello Kitty <a@b.c>, No Kitty <b@b.c>",
+ [
+ { name: "Hello Kitty", email: "a@b.c" },
+ { name: "No Kitty", email: "b@b.c" },
+ ],
+ ],
+ [
+ "undisclosed-recipients:;",
+ [{ name: "undisclosed-recipients", group: [] }],
+ ],
+ ["me@[127.0.0.1]", [{ name: "", email: "me@[127.0.0.1]" }]],
+ ['"me"@a.com', [{ name: "", email: "me@a.com" }]],
+ ['"!"@a.com', [{ name: "", email: '"!"@a.com' }]],
+ ['"\\!"@a.com', [{ name: "", email: '"!"@a.com' }]],
+ ['"\\\\!"@a.com', [{ name: "", email: '"\\\\!"@a.com' }]],
+ [
+ "Coward (not@email) <real@email.com>",
+ [{ name: "Coward (not@email)", email: "real@email.com" }],
+ ],
+ [
+ '"y@example.com" <xx@example.com>',
+ [{ name: "y@example.com", email: "xx@example.com" }],
+ ],
+ ["@@@", [{ name: "", email: '"@@"@' }]],
+ [
+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
+ [{ name: "", email: '"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"@' }],
+ ],
+ [
+ "Nat Ex <{admin@example.com|stocks@example.com|stockmarket@example.com|trades@example.com|money@example.com|broker@example.com|deals@example.com|offers@example.com|news@example.com|binary@example.com|forex@example.com|marketing@example.com|mail@example.com|inboxdeals@example.com|packages@example.com>",
+ [
+ {
+ name: "Nat Ex",
+ email:
+ '"{admin@example.com|stocks@example.com|stockmarket@example.com|trades@example.com|money@example.com|broker@example.com|deals@example.com|offers@example.com|news@example.com|binary@example.com|forex@example.com|marketing@example.com|mail@example.com|inboxdeals@example.com|packages"@example.com',
+ },
+ ],
+ ],
+ [
+ "Group: a@b.com, b@c.com;",
+ [
+ {
+ name: "Group",
+ group: [
+ { name: "", email: "a@b.com" },
+ { name: "", email: "b@c.com" },
+ ],
+ },
+ ],
+ ],
+ [
+ "a@invalid.invalid, Group: a@b.com;",
+ [
+ { name: "", email: "a@invalid.invalid" },
+ { name: "Group", group: [{ name: "", email: "a@b.com" }] },
+ ],
+ ],
+ [
+ "Group A: a@b.com;, Group B: b@b.com;",
+ [
+ { name: "Group A", group: [{ name: "", email: "a@b.com" }] },
+ { name: "Group B", group: [{ name: "", email: "b@b.com" }] },
+ ],
+ ],
+ [
+ 'Crazy (<Stupid "name") <simple@a.email>',
+ [{ name: "Crazy (<Stupid name)", email: "simple@a.email" }],
+ ],
+ [
+ "Group: Real <a@b.com>, Fake <a@b.com>",
+ [
+ {
+ name: "Group",
+ group: [
+ { name: "Real", email: "a@b.com" },
+ { name: "Fake", email: "a@b.com" },
+ ],
+ },
+ ],
+ ],
+ [
+ '"Joe Q. Public" <john.q.public@example.com>,' +
+ 'Test <"abc!x.yz"@foo.invalid>, Test <test@[xyz!]>,' +
+ '"Giant; \\"Big\\" Box" <sysservices@example.net>',
+ [
+ { name: "Joe Q. Public", email: "john.q.public@example.com" },
+ { name: "Test", email: '"abc!x.yz"@foo.invalid' },
+ { name: "Test", email: "test@[xyz!]" },
+ { name: 'Giant; "Big" Box', email: "sysservices@example.net" },
+ ],
+ ],
+ [
+ "Unfortunate breaking < so . many . spaces @ here . invalid >",
+ [
+ {
+ name: "Unfortunate breaking",
+ email: "so.many.spaces@here.invalid",
+ },
+ ],
+ ],
+ [
+ "so . many . spaces @ here . invalid",
+ [{ name: "", email: "so.many.spaces@here.invalid" }],
+ ],
+ ["abc@foo.invalid", [{ name: "", email: "abc@foo.invalid" }]],
+ ["foo <ghj@foo.invalid>", [{ name: "foo", email: "ghj@foo.invalid" }]],
+ [
+ "abc@foo.invalid, foo <ghj@foo.invalid>",
+ [
+ { name: "", email: "abc@foo.invalid" },
+ { name: "foo", email: "ghj@foo.invalid" },
+ ],
+ ],
+ [
+ "foo bar <foo@bar.invalid>",
+ [{ name: "foo bar", email: "foo@bar.invalid" }],
+ ],
+ [
+ "foo bar <foo@bar.invalid>, abc@foo.invalid, foo <ghj@foo.invalid>",
+ [
+ { name: "foo bar", email: "foo@bar.invalid" },
+ { name: "", email: "abc@foo.invalid" },
+ { name: "foo", email: "ghj@foo.invalid" },
+ ],
+ ],
+ [
+ "foo\u00D0 bar <foo@bar.invalid>, \u00F6foo <ghj@foo.invalid>",
+ [
+ { name: "foo\u00D0 bar", email: "foo@bar.invalid" },
+ { name: "\u00F6foo", email: "ghj@foo.invalid" },
+ ],
+ ],
+ [
+ "Undisclosed recipients:;",
+ [{ name: "Undisclosed recipients", group: [] }],
+ ],
+ [
+ '" "@a a;b',
+ [
+ { name: "", email: '" "@a a' },
+ { name: "b", email: "" },
+ ],
+ ],
+ [
+ "Undisclosed recipients:;\0:; foo <ghj@veryveryveryverylongveryveryver" +
+ "yveryinvalidaddress.invalid>",
+ [
+ { name: "Undisclosed recipients", group: [] },
+ {
+ name: "foo",
+ email:
+ "ghj@veryveryveryverylongveryveryveryveryinvali" +
+ "daddress.invalid",
+ },
+ ],
+ ],
+ // XXX: test_nsIMsgHeaderParser2 has an empty one here...
+ [
+ "<a;a@invalid",
+ [
+ { name: "", email: "a" },
+ { name: "", email: "a@invalid" },
+ ],
+ ],
+ ["me@foo.invalid", [{ name: "", email: "me@foo.invalid" }]],
+ [
+ "me@foo.invalid, me2@foo.invalid",
+ [
+ { name: "", email: "me@foo.invalid" },
+ { name: "", email: "me2@foo.invalid" },
+ ],
+ ],
+ [
+ '"foo bar" <me@foo.invalid>',
+ [{ name: "foo bar", email: "me@foo.invalid" }],
+ ],
+ [
+ '"foo bar" <me@foo.invalid>, "bar foo" <me2@foo.invalid>',
+ [
+ { name: "foo bar", email: "me@foo.invalid" },
+ { name: "bar foo", email: "me2@foo.invalid" },
+ ],
+ ],
+ [
+ "A Group:Ed Jones <c@a.invalid>,joe@where.invalid,John <jdoe@one.invalid>;",
+ [
+ {
+ name: "A Group",
+ group: [
+ { name: "Ed Jones", email: "c@a.invalid" },
+ { name: "", email: "joe@where.invalid" },
+ { name: "John", email: "jdoe@one.invalid" },
+ ],
+ },
+ ],
+ ],
+ [
+ "mygroup:;, empty:;, foo@foo.invalid, othergroup:bar@foo.invalid, bar2" +
+ "@foo.invalid;, y@y.invalid, empty:;",
+ [
+ { name: "mygroup", group: [] },
+ { name: "empty", group: [] },
+ { name: "", email: "foo@foo.invalid" },
+ {
+ name: "othergroup",
+ group: [
+ { name: "", email: "bar@foo.invalid" },
+ { name: "", email: "bar2@foo.invalid" },
+ ],
+ },
+ { name: "", email: "y@y.invalid" },
+ { name: "empty", group: [] },
+ ],
+ ],
+ [
+ "Undisclosed recipients:;;;;;;;;;;;;;;;;,,,,,,,,,,,,,,,,",
+ [{ name: "Undisclosed recipients", group: [] }],
+ ],
+ [
+ "a@xxx.invalid; b@xxx.invalid",
+ [
+ { name: "", email: "a@xxx.invalid" },
+ { name: "", email: "b@xxx.invalid" },
+ ],
+ ],
+ [
+ "a@xxx.invalid; B <b@xxx.invalid>",
+ [
+ { name: "", email: "a@xxx.invalid" },
+ { name: "B", email: "b@xxx.invalid" },
+ ],
+ ],
+ [
+ '"A " <a@xxx.invalid>; b@xxx.invalid',
+ [
+ { name: "A", email: "a@xxx.invalid" },
+ { name: "", email: "b@xxx.invalid" },
+ ],
+ ],
+ [
+ "A <a@xxx.invalid>; B <b@xxx.invalid>",
+ [
+ { name: "A", email: "a@xxx.invalid" },
+ { name: "B", email: "b@xxx.invalid" },
+ ],
+ ],
+ [
+ "A (this: is, a comment;) <a.invalid>; g: (this: is, <a> comment;) C" +
+ "<c.invalid>, d.invalid;",
+ [
+ { name: "A (this: is, a comment;)", email: "a.invalid" },
+ {
+ name: "g",
+ group: [
+ { name: "(this: is, <a> comment;) C", email: "c.invalid" },
+ { name: "d.invalid", email: "" },
+ ],
+ },
+ ],
+ ],
+ [
+ "Mary Smith <mary@x.invalid>, extra:;, group:jdoe@example.invalid; Who" +
+ '? <one@y.invalid>; <boss@nil.invalid>, "Giant; \\"Big\\" Box" <sysse' +
+ "rvices@example.invalid>, ",
+ [
+ { name: "Mary Smith", email: "mary@x.invalid" },
+ { name: "extra", group: [] },
+ {
+ name: "group",
+ group: [{ name: "", email: "jdoe@example.invalid" }],
+ },
+ { name: "Who?", email: "one@y.invalid" },
+ { name: "", email: "boss@nil.invalid" },
+ { name: 'Giant; "Big" Box', email: "sysservices@example.invalid" },
+ ],
+ ],
+ [
+ "Undisclosed recipients: a@foo.invalid ;;extra:;",
+ [
+ {
+ name: "Undisclosed recipients",
+ group: [{ name: "", email: "a@foo.invalid" }],
+ },
+ { name: "extra", group: [] },
+ ],
+ ],
+ [
+ "Undisclosed recipients:;;extra:a@foo.invalid;",
+ [
+ { name: "Undisclosed recipients", group: [] },
+ { name: "extra", group: [{ name: "", email: "a@foo.invalid" }] },
+ ],
+ ],
+ ["a < <a@b.c>", [{ name: "a", email: "a@b.c" }]],
+ [
+ "Name <incomplete@email",
+ [{ name: "Name", email: "incomplete@email" }],
+ ],
+ [
+ "Name <space here@email.invalid>",
+ [{ name: "Name", email: '"space here"@email.invalid' }],
+ ],
+ ["Name <not an email>", [{ name: "Name", email: "not an email" }]],
+ [
+ "=?UTF-8?Q?Simple?= <a@b.c>",
+ [{ name: "=?UTF-8?Q?Simple?=", email: "a@b.c" }],
+ ],
+ ["No email address", [{ name: "No email address", email: "" }]],
+ // Thought we were parsing an address, but it was a name.
+ [
+ "name@example.com <receiver@example.com>",
+ [{ name: "name@example.com", email: "receiver@example.com" }],
+ ],
+ [
+ "name@huhu.com <receiver@example.com>",
+ [{ name: "name@huhu.com", email: "receiver@example.com" }],
+ ],
+ // Some names with quotes.
+ [
+ '"name@huhu.com" <receiver@example.com>',
+ [{ name: "name@huhu.com", email: "receiver@example.com" }],
+ ],
+ [
+ '"Chaplin, Charlie" <receiver@example.com>',
+ [{ name: "Chaplin, Charlie", email: "receiver@example.com" }],
+ ],
+ [
+ '"name@huhu.com and name@haha.com" <receiver@example.com>',
+ [
+ {
+ name: "name@huhu.com and name@haha.com",
+ email: "receiver@example.com",
+ },
+ ],
+ ],
+ // Handling of comments and legacy display-names as per RFC 5322 §3.4
+ [
+ "(c1)n(c2) <(c3)a(c4)@(c5)b(c6).(c7)d(c8)> (c9(c10)c11)",
+ [{ name: "(c1) n (c2) (c9(c10)c11)", email: "a@b.d" }],
+ ],
+ [
+ "<(c3)a(c4)@(c5)b(c6).(c7)d(c8)> (c9(c10)c11)",
+ [{ name: "(c9(c10)c11)", email: "a@b.d" }],
+ ],
+ [
+ "(c3)a(c4)@(c5)b(c6).(c7)d(c8)(c9(c10)c11)",
+ [{ name: "c9(c10)c11", email: "a@b.d" }],
+ ],
+ [
+ "(c1)n(c2) <(c3)a(c4)@(c5)b(c6).(c7)d(c8)> (c9(c10)c11)(c12)",
+ [{ name: "(c1) n (c2) (c9(c10)c11) (c12)", email: "a@b.d" }],
+ ],
+ [
+ "<(c3)a(c4)@(c5)b(c6).(c7)d(c8)> (c9(c10)c11)(c12)",
+ [{ name: "(c9(c10)c11) (c12)", email: "a@b.d" }],
+ ],
+ [
+ "(c3)a(c4)@(c5)b(c6).(c7)d(c8)(c9(c10)c11)(c12)",
+ [{ name: "c12", email: "a@b.d" }],
+ ],
+ // Collapse extraneous whitespace and make sure unexpected characters aren't there.
+ [
+ 'Friend "<friend@huhu.com>" \t \t \u00A0\u00A0\u2003 \u00AD \x20\u200B\x20\u200B\x20 \t \u034F \u2028 \uDB40\uDD01 \u2800 \t <ws@example.com>',
+ [{ name: "Friend <friend@huhu.com>", email: "ws@example.com" }],
+ ],
+ // Collapse multiple "special" spaces like NBSP (\u00A0), EM space (\u2003), etc.
+ [
+ "Foe \u00A0\u00A0\u2003 A <foe@example.com>",
+ [{ name: "Foe A", email: "foe@example.com" }],
+ ],
+ // Remove tabs.
+ [
+ "Tabby \t \t A\t\tB <tab@example.com>",
+ [{ name: "Tabby A B", email: "tab@example.com" }],
+ ],
+ // Ensure it's the address in angles that is parsed as address.
+ [
+ "<attacker@example.com>friend@example.com\t, bystander@example.com,Someone (some@invalid) <someone@example.com>,",
+ [
+ { name: "", email: "attacker@example.com" },
+ { name: "", email: "bystander@example.com" },
+ { name: "Someone (some@invalid)", email: "someone@example.com" },
+ ],
+ ],
+ [
+ "me (via foo@example.com) <attacker2@example.com>friend2@example.com ",
+ [
+ {
+ name: "me (via foo@example.com)",
+ email: "attacker2@example.com",
+ },
+ ],
+ ],
+ [
+ "<attacker@example.com> <friend@example.com>,friend2@example.com<attacker2@example.com>",
+ [
+ {
+ name: "",
+ email: "attacker@example.com",
+ },
+ {
+ name: "friend2@example.com",
+ email: "attacker2@example.com",
+ },
+ ],
+ ],
+ [
+ 'My "XX ><<friend2@example.com>" YY <attacker@example.com> <friend@example.com> ("mr ><x")',
+ [
+ {
+ name: "My XX ><<friend2@example.com> YY (mr ><x)",
+ email: "attacker@example.com",
+ },
+ ],
+ ],
+ ];
+ header_tests.forEach(function (data) {
+ arrayTest(data, function () {
+ assert.deepEqual(
+ headerparser.parseAddressingHeader(data[0], false),
+ data[1]
+ );
+ });
+ });
+ });
+ suite("parseAddressingHeader (RFC 2047 support)", function () {
+ let header_tests = [
+ ["Simple <a@b.c>", [{ name: "Simple", email: "a@b.c" }]],
+ ["=?UTF-8?Q?Simple?= <a@b.c>", [{ name: "Simple", email: "a@b.c" }]],
+ ["=?UTF-8?Q?=3C@b.c?= <a@b.c>", [{ name: "<@b.c", email: "a@b.c" }]],
+
+ // RFC 2047 tokens should not interfere with lexical processing
+ ["=?UTF-8?Q?a@b.c,?= <b@b.c>", [{ name: "a@b.c,", email: "b@b.c" }]],
+ ["=?UTF-8?Q?a@b.c=2C?= <b@b.c>", [{ name: "a@b.c,", email: "b@b.c" }]],
+ ["=?UTF-8?Q?<?= <a@b.c>", [{ name: "<", email: "a@b.c" }]],
+ [
+ "Simple =?UTF-8?Q?<?= a@b.c>",
+ [{ name: "", email: '"Simple < a"@b.c' }],
+ ],
+ ["Tag <=?UTF-8?Q?email?=@b.c>", [{ name: "Tag", email: "email@b.c" }]],
+ // handling of comments and legacy display-names as per RFC 5322 §3.4
+ [
+ "jl1@b.c (=?ISO-8859-1?Q?Joe_L=F6we?=)",
+ [{ name: "Joe Löwe", email: "jl1@b.c" }],
+ ],
+ [
+ "(=?ISO-8859-1?Q?Joe_L=F6we?=) jl2@b.c",
+ [{ name: "Joe Löwe", email: "jl2@b.c" }],
+ ],
+ [
+ "(=?ISO-8859-1?Q?Joe_L=F6we?=) jl3@b.c (c2)",
+ [{ name: "c2", email: "jl3@b.c" }],
+ ],
+ [
+ "=?ISO-8859-1?Q?Joe_L=F6we?= <jl3@b.c> (c2)",
+ [{ name: "Joe Löwe (c2)", email: "jl3@b.c" }],
+ ],
+ [
+ "(=?ISO-8859-1?Q?Joe_L=F6we?=) <jl3@b.c> (c2)",
+ [{ name: "(Joe Löwe) (c2)", email: "jl3@b.c" }],
+ ],
+ // Bug 1141446: Malformed From addresses with erroneous quotes,
+ // note: acute accents: a \u00E1, e \u00E9, i \u00ED, o \u00F3, u \u00FA.
+ [
+ '"=?UTF-8?Q?Jazzy_Fern=C3=A1ndez_Nunoz?= jazzy.f.nunoz@example.com ' +
+ '[BCN-FC]" <Barcelona-Freecycle-noreply@yahoogroups.com>',
+ [
+ {
+ name: "Jazzy Fern\u00E1ndez Nunoz jazzy.f.nunoz@example.com [BCN-FC]",
+ email: "Barcelona-Freecycle-noreply@yahoogroups.com",
+ },
+ ],
+ ],
+ [
+ '"=?UTF-8?B?TWlyaWFtIEJlcm5hYsOpIFBlcmVsbMOz?= miriam@example.com ' +
+ '[BCN-FC]" <Barcelona-Freecycle-noreply@yahoogroups.com>',
+ [
+ {
+ name: "Miriam Bernab\u00E9 Perell\u00F3 miriam@example.com [BCN-FC]",
+ email: "Barcelona-Freecycle-noreply@yahoogroups.com",
+ },
+ ],
+ ],
+ [
+ '"=?iso-8859-1?Q?First_Mar=EDa_Furi=F3_Gancho?= mail@yahoo.es ' +
+ '[BCN-FC]" <Barcelona-Freecycle-noreply@yahoogroups.com>',
+ [
+ {
+ name: "First Mar\u00EDa Furi\u00F3 Gancho mail@yahoo.es [BCN-FC]",
+ email: "Barcelona-Freecycle-noreply@yahoogroups.com",
+ },
+ ],
+ ],
+ [
+ '"=?iso-8859-1?B?U29maWEgQ2FzdGVsbPMgUm9tZXJv?= sonia@example.com ' +
+ '[BCN-FC]" <Barcelona-Freecycle-noreply@yahoogroups.com>',
+ [
+ {
+ name: "Sofia Castell\u00F3 Romero sonia@example.com [BCN-FC]",
+ email: "Barcelona-Freecycle-noreply@yahoogroups.com",
+ },
+ ],
+ ],
+ [
+ "=?iso-8859-1?Q?Klaus_Eisschl=E4ger_=28k=2Eeisschlaeger=40t-onli?=" +
+ "=?iso-8859-1?Q?ne=2Ede=29?= <k.eisschlaeger@t-online.de>",
+ [
+ {
+ name: "Klaus Eisschläger (k.eisschlaeger@t-online.de)",
+ email: "k.eisschlaeger@t-online.de",
+ },
+ ],
+ ],
+ [
+ '"=?UTF-8?Q?=22Claudia_R=C3=B6hschicht=22?= Claudia_Roehschicht@web.de [freecycle-berlin]" ' +
+ "<freecycle-berlin-noreply@yahoogroups.de>",
+ [
+ {
+ name: '"Claudia Röhschicht" Claudia_Roehschicht@web.de [freecycle-berlin]',
+ email: "freecycle-berlin-noreply@yahoogroups.de",
+ },
+ ],
+ ],
+ // Collapse multiple consecutive "special" spaces, like zero width space
+ // etc.
+ // \u00AD is soft hyphen. \u200B is zero width space.
+ [
+ `invisiblespaceA@friend.example.com B \u200B\u00AD \u200B \u200B A. <foeA@example.com>`,
+ [
+ {
+ name: "invisiblespaceA@friend.example.com B A.",
+ email: "foeA@example.com",
+ },
+ ],
+ ],
+
+ // Collapse multiple consecutive "special" spaces, like zero width space
+ // etc. also when encoded.
+ // \u00AD is soft hyphen. \u200B is zero width space.
+ // unescape(encodeURIComponent(source)) encodes the JavaScript UTF-16 representation
+ // of the string into UTF-8. Example: encodeURIComponent("ö") returns "%C3%B6",
+ // unescape("%C3%B6") returns the bytes 0xC3B6 which is the UTF-8 encoding of "ö".
+ // See bug 1551746 for other ways.
+ [
+ //"=?UTF-8?B?IMKgIGJsw7YgPGludmlzaWJsZXNwYWNlQGZyaWVuZC5leGFtcGxlLmNvbT4g4oCLIOKAiyDigIsu=?= <foe@example.com>"
+ `=?UTF-8?B?${btoa(
+ unescape(
+ encodeURIComponent(
+ " \u00AD blö <invisiblespace@friend.example.com> \u200B \u200B \u200B."
+ )
+ )
+ )}?= <foe@example.com>`,
+ [
+ {
+ name: "blö <invisiblespace@friend.example.com> .",
+ email: "foe@example.com",
+ },
+ ],
+ ],
+ ];
+ header_tests.forEach(function (data) {
+ arrayTest(data, function () {
+ assert.deepEqual(
+ headerparser.parseAddressingHeader(data[0], true),
+ data[1]
+ );
+ });
+ });
+ });
+ suite("parseDateHeader", function () {
+ let header_tests = [
+ // Some basic tests, derived from searching for Date headers in a mailing
+ // list archive.
+ ["Thu, 06 Sep 2012 08:08:21 -0700", "2012-09-06T08:08:21-0700"],
+ ["Thu, 6 Sep 2012 14:49:05 -0400", "2012-09-06T14:49:05-0400"],
+ ["Fri, 07 Sep 2012 07:30:11 -0700 (PDT)", "2012-09-07T07:30:11-0700"],
+ ["9 Sep 2012 21:03:59 -0000", "2012-09-09T21:03:59Z"],
+ ["Sun, 09 Sep 2012 19:10:59 -0400", "2012-09-09T19:10:59-0400"],
+ ["Wed, 17 Jun 2009 10:12:25 +0530", "2009-06-17T10:12:25+0530"],
+
+ // Exercise all the months.
+ ["Mon, 28 Jan 2013 13:35:05 -0500", "2013-01-28T13:35:05-0500"],
+ ["Wed, 29 Feb 2012 23:43:26 +0000", "2012-02-29T23:43:26+0000"],
+ ["Sat, 09 Mar 2013 18:24:47 -0500", "2013-03-09T18:24:47-0500"],
+ ["Sat, 27 Apr 2013 12:51:48 -0400", "2013-04-27T12:51:48-0400"],
+ ["Tue, 28 May 2013 17:21:13 +0800", "2013-05-28T17:21:13+0800"],
+ ["Mon, 17 Jun 2013 22:15:41 +0200", "2013-06-17T22:15:41+0200"],
+ ["Wed, 18 Jul 2012 13:50:47 +0900", "2012-07-18T13:50:47+0900"],
+ ["Mon, 13 Aug 2012 13:55:16 +0200", "2012-08-13T13:55:16+0200"],
+ ["Thu, 06 Sep 2012 19:49:47 -0400", "2012-09-06T19:49:47-0400"],
+ ["Mon, 22 Oct 2012 02:27:23 -0700", "2012-10-22T02:27:23-0700"],
+ ["Thu, 22 Nov 2012 09:04:24 +0800", "2012-11-22T09:04:24+0800"],
+ ["Sun, 25 Dec 2011 12:27:13 +0000", "2011-12-25T12:27:13+0000"],
+
+ // Try out less common timezone offsets.
+ ["Sun, 25 Dec 2011 12:27:13 +1337", "2011-12-25T12:27:13+1337"],
+ ["Sun, 25 Dec 2011 12:27:13 -1337", "2011-12-25T12:27:13-1337"],
+
+ // Leap seconds! Except that since dates in JS don't believe they exist,
+ // they get shoved to the next second.
+ ["30 Jun 2012 23:59:60 +0000", "2012-07-01T00:00:00Z"],
+ ["31 Dec 2008 23:59:60 +0000", "2009-01-01T00:00:00Z"],
+ // This one doesn't exist (they are added only as needed on an irregular
+ // basis), but it's plausible...
+ ["30 Jun 2030 23:59:60 +0000", "2030-07-01T00:00:00Z"],
+ // ... and this one isn't.
+ ["10 Jun 2030 13:39:60 +0000", "2030-06-10T13:40:00Z"],
+ // How about leap seconds in other timezones?
+ ["30 Jun 2012 18:59:60 -0500", "2012-07-01T00:00:00Z"],
+
+ // RFC 5322 obsolete date productions
+ ["Sun, 26 Jan 14 17:14:22 -0600", "2014-01-26T17:14:22-0600"],
+ ["Tue, 26 Jan 49 17:14:22 -0600", "2049-01-26T17:14:22-0600"],
+ ["Thu, 26 Jan 50 17:14:22 -0600", "1950-01-26T17:14:22-0600"],
+ ["Sun, 26 Jan 2014 17:14:22 EST", "2014-01-26T17:14:22-0500"],
+ ["Sun, 26 Jan 2014 17:14:22 CST", "2014-01-26T17:14:22-0600"],
+ ["Sun, 26 Jan 2014 17:14:22 MST", "2014-01-26T17:14:22-0700"],
+ ["Sun, 26 Jan 2014 17:14:22 PST", "2014-01-26T17:14:22-0800"],
+ ["Sun, 26 Jan 2014 17:14:22 AST", "2014-01-26T17:14:22-0400"],
+ ["Sun, 26 Jan 2014 17:14:22 NST", "2014-01-26T17:14:22-0330"],
+ ["Sun, 26 Jan 2014 17:14:22 MET", "2014-01-26T17:14:22+0100"],
+ ["Sun, 26 Jan 2014 17:14:22 EET", "2014-01-26T17:14:22+0200"],
+ ["Sun, 26 Jan 2014 17:14:22 JST", "2014-01-26T17:14:22+0900"],
+ ["Sun, 26 Jan 2014 17:14:22 GMT", "2014-01-26T17:14:22+0000"],
+ ["Sun, 26 Jan 2014 17:14:22 UT", "2014-01-26T17:14:22+0000"],
+ // Daylight savings timezones, even though these aren't actually during
+ // daylight savings time for the relevant jurisdictions.
+ ["Sun, 26 Jan 2014 17:14:22 EDT", "2014-01-26T17:14:22-0400"],
+ ["Sun, 26 Jan 2014 17:14:22 CDT", "2014-01-26T17:14:22-0500"],
+ ["Sun, 26 Jan 2014 17:14:22 MDT", "2014-01-26T17:14:22-0600"],
+ ["Sun, 26 Jan 2014 17:14:22 PDT", "2014-01-26T17:14:22-0700"],
+ ["Sun, 26 Jan 2014 17:14:22 BST", "2014-01-26T17:14:22+0100"],
+ // Unknown time zone--assume UTC
+ ["Sun, 26 Jan 2014 17:14:22 QMT", "2014-01-26T17:14:22+0000"],
+
+ // The following days of the week are incorrect.
+ ["Tue, 28 Jan 2013 13:35:05 -0500", "2013-01-28T13:35:05-0500"],
+ ["Thu, 26 Jan 14 17:14:22 -0600", "2014-01-26T17:14:22-0600"],
+ ["Fri, 26 Jan 49 17:14:22 -0600", "2049-01-26T17:14:22-0600"],
+ ["Mon, 26 Jan 50 17:14:22 -0600", "1950-01-26T17:14:22-0600"],
+ // And for these 2 digit years, they are correct for the other century.
+ ["Mon, 26 Jan 14 17:14:22 -0600", "2014-01-26T17:14:22-0600"],
+ ["Wed, 26 Jan 49 17:14:22 -0600", "2049-01-26T17:14:22-0600"],
+ ["Wed, 26 Jan 50 17:14:22 -0600", "1950-01-26T17:14:22-0600"],
+
+ // Try with some illegal names for days of the week or months of the year.
+ ["Sam, 05 Apr 2014 15:04:13 -0500", "2014-04-05T15:04:13-0500"],
+ ["Lun, 01 Apr 2014 15:04:13 -0500", "2014-04-01T15:04:13-0500"],
+ ["Mar, 02 Apr 2014 15:04:13 -0500", "2014-04-02T15:04:13-0500"],
+ ["Mar, 02 April 2014 15:04:13 -0500", "2014-04-02T15:04:13-0500"],
+ ["Mar, 02 Avr 2014 15:04:13 -0500", NaN],
+ ["Tue, 02 A 2014 15:04:13 -0500", NaN],
+
+ // A truly invalid date
+ ["Coincident with the rapture", NaN],
+ ];
+ header_tests.forEach(function (data) {
+ arrayTest(data, function () {
+ assert.equal(
+ headerparser.parseDateHeader(data[0]).toString(),
+ new Date(data[1]).toString()
+ );
+ });
+ });
+ });
+
+ suite("decodeRFC2047Words", function () {
+ let header_tests = [
+ // Some basic sanity tests for the test process
+ ["Test", "Test"],
+ ["Test 2", "Test 2"],
+ ["Basic words", "Basic words"],
+ ["Not a =? word", "Not a =? word"],
+
+ // Simple 2047 decodings
+ ["=?UTF-8?Q?Encoded?=", "Encoded"],
+ ["=?UTF-8?q?Encoded?=", "Encoded"],
+ ["=?ISO-8859-1?Q?oxyg=e8ne?=", "oxyg\u00e8ne"],
+ ["=?UTF-8?B?QmFzZTY0?=", "Base64"],
+ ["=?UTF-8?b?QmFzZTY0?=", "Base64"],
+ ["=?UTF-8?Q?A_space?=", "A space"],
+ ["=?UTF-8?Q?A space?=", "A space"],
+ ["A =?UTF-8?Q?B?= C", "A B C"],
+ ["=?UTF-8?Q?A?= =?UTF-8?Q?B?=", "AB"],
+ ["=?UTF-8?Q?oxyg=c3=a8ne?=", "oxyg\u00e8ne"],
+ ["=?utf-8?Q?oxyg=C3=A8ne?=", "oxyg\u00e8ne"],
+ ["=?UTF-8?B?b3h5Z8OobmU=?=", "oxyg\u00e8ne"],
+ ["=?UTF-8*fr?B?b3h5Z8OobmU=?=", "oxyg\u00e8ne"],
+ [
+ "=?BIG5?Q?=B9=CF=AE=D1=C0]SSCI=A4=CEJCR=B8=EA=AE=C6=AEw=C1=BF=B2=DF=A1A=A8" +
+ "=F3=A7U=B1z=A1u=B4=A3=A4=C9=AC=E3=A8s=AF=C0=BD=E8=BBP=AE=C4=B2v=A5H=A4=CE" +
+ "=A7=EB=BDZ=B5=A6=B2=A4=AA=BA=B9B=A5=CE=A1v=A1A=C5w=AA=EF=B3=F8=A6W=B0=D1" +
+ "=A5[=A1C?=",
+ "\u5716\u66F8\u9928SSCI\u53CAJCR\u8CC7\u6599\u5EAB\u8B1B" +
+ "\u7FD2\uFF0C\u5354\u52A9\u60A8\u300C\u63D0\u5347\u7814\u7A76\u7D20\u8CEA" +
+ "\u8207\u6548\u7387\u4EE5\u53CA\u6295\u7A3F\u7B56\u7565\u7684\u904B\u7528" +
+ "\u300D\uFF0C\u6B61\u8FCE\u5831\u540D\u53C3\u52A0\u3002",
+ ],
+
+ // Invalid decodings
+ ["=?UTF-8?Q?=f0ab?=", "\ufffdab"],
+ ["=?UTF-8?Q?=f0?= ab", "\ufffd ab"],
+ [
+ "=?UTF-8?Q?=ed=a0=bd=ed=b2=a9?=",
+ "\ufffd\ufffd\ufffd\ufffd\ufffd\ufffd",
+ ],
+ ["=?NoSuchCharset?Q?ab?=", "=?NoSuchCharset?Q?ab?="],
+ ["=?UTF-8?U?Encoded?=", "=?UTF-8?U?Encoded?="],
+ ["=?UTF-8?Q?Almost", "=?UTF-8?Q?Almost"],
+
+ // Try some non-BMP characters in various charsets
+ ["=?UTF-8?B?8J+SqQ==?=", "\ud83d\udca9"],
+ // The goal for the next one is to be a non-BMP in a non-full-Unicode
+ // charset. The only category where this exists is a small set of
+ // characters in Big5, which were previously mapped to a PUA in an older
+ // version but then reassigned to Plane 1. However, Big5 is really a set
+ // of slightly different, slightly incompatible charsets.
+ // TODO: This requires future investigation. Bug 912470 discusses the
+ // changes to Big5 proposed within Mozilla.
+ // ["=?Big5?Q?=87E?=", "\ud85c\ude67"],
+ ["=?GB18030?B?lDnaMw==?=", "\ud83d\udca9"],
+
+ // How to handle breaks in multi-byte encoding
+ ["=?UTF-8?Q?=f0=9f?= =?UTF-8?Q?=92=a9?=", "\ud83d\udca9"],
+ ["=?UTF-8?B?8J+S?= =?UTF-8?B?qQ==?=", "\ud83d\udca9"],
+ ["=?UTF-8?B?8J+S?= =?UTF-8?Q?=a9?=", "\ud83d\udca9"],
+ ["=?UTF-8?B?8J+S?= =?ISO-8859-1?B?qQ==?=", "\ufffd\u00a9"],
+ ["=?UTF-8?Q?=f0?= =?UTF-8?Q?ab?=", "\ufffdab"],
+
+ // This is a split non-BMP character.
+ [
+ "=?UTF-8?B?YfCfkqnwn5Kp8J+SqfCfkqnwn5Kp8J+SqfCfkqnvv70=?= =?UTF-8?B?77+9?=",
+ "a\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D\uDCA9\uD83D" +
+ "\uDCA9\uD83D\uDCA9\uFFFD\uFFFD",
+ ],
+
+ // Spaces in RFC 2047 tokens
+ ["=?UTF-8?Q?Invalid token?=", "Invalid token"],
+
+ // More tests from bug 493544
+ [
+ "AAA =?UTF-8?Q?bbb?= CCC =?UTF-8?Q?ddd?= EEE =?UTF-8?Q?fff?= GGG",
+ "AAA bbb CCC ddd EEE fff GGG",
+ ],
+ [
+ "=?UTF-8?B?4oiAICDiiIEgIOKIgiAg4oiDICDiiIQgIOKIhSAg4oiGICDiiIcgIOKIiC" +
+ "Ag?=\n =?UTF-8?B?4oiJICDiiIogIOKIiyAg4oiMICDiiI0gIOKIjiAg4oiP?=",
+ "\u2200 \u2201 \u2202 \u2203 \u2204 \u2205 \u2206 \u2207 " +
+ "\u2208 \u2209 \u220a \u220b \u220c \u220d \u220e \u220f",
+ ],
+ [
+ "=?utf-8?Q?=E2=88=80__=E2=88=81__=E2=88=82__=E2=88=83__=E2=88=84__=E2" +
+ "?=\n =?utf-8?Q?=88=85__=E2=88=86__=E2=88=87__=E2=88=88__=E2=88=89__" +
+ "=E2=88?=\n =?utf-8?Q?=8A__=E2=88=8B__=E2=88=8C__=E2=88=8D__=E2=88=8" +
+ "E__=E2=88=8F?=",
+ "\u2200 \u2201 \u2202 \u2203 \u2204 \u2205 \u2206 \u2207 " +
+ "\u2208 \u2209 \u220a \u220b \u220c \u220d \u220e \u220f",
+ ],
+ [
+ "=?UTF-8?B?4oiAICDiiIEgIOKIgiAg4oiDICDiiIQgIOKIhSAg4oiGICDiiIcgIOKIiA" +
+ "==?=\n =?UTF-8?B?ICDiiIkgIOKIiiAg4oiLICDiiIwgIOKIjSAg4oiOICDiiI8=?=",
+ "\u2200 \u2201 \u2202 \u2203 \u2204 \u2205 \u2206 \u2207 " +
+ "\u2208 \u2209 \u220a \u220b \u220c \u220d \u220e \u220f",
+ ],
+ [
+ "=?UTF-8?b?4oiAICDiiIEgIOKIgiAg4oiDICDiiIQgIOKIhSAg4oiGICDiiIcgIOKIiA" +
+ "==?=\n =?UTF-8?b?ICDiiIkgIOKIiiAg4oiLICDiiIwgIOKIjSAg4oiOICDiiI8=?=",
+ "\u2200 \u2201 \u2202 \u2203 \u2204 \u2205 \u2206 \u2207 " +
+ "\u2208 \u2209 \u220a \u220b \u220c \u220d \u220e \u220f",
+ ],
+ [
+ "=?utf-8?Q?=E2=88=80__=E2=88=81__=E2=88=82__=E2=88=83__=E2=88=84__?=\n" +
+ " =?utf-8?Q?=E2=88=85__=E2=88=86__=E2=88=87__=E2=88=88__=E2=88=89__?=\n" +
+ " =?utf-8?Q?=E2=88=8A__=E2=88=8B__=E2=88=8C__=E2=88=8D__=E2=88=8E__?=\n" +
+ " =?utf-8?Q?=E2=88=8F?=",
+ "\u2200 \u2201 \u2202 \u2203 \u2204 \u2205 \u2206 \u2207 " +
+ "\u2208 \u2209 \u220a \u220b \u220c \u220d \u220e \u220f",
+ ],
+ [
+ "=?utf-8?q?=E2=88=80__=E2=88=81__=E2=88=82__=E2=88=83__=E2=88=84__?=\n" +
+ " =?utf-8?q?=E2=88=85__=E2=88=86__=E2=88=87__=E2=88=88__=E2=88=89__?=\n" +
+ " =?utf-8?q?=E2=88=8A__=E2=88=8B__=E2=88=8C__=E2=88=8D__=E2=88=8E__?=\n" +
+ " =?utf-8?q?=E2=88=8F?=",
+ "\u2200 \u2201 \u2202 \u2203 \u2204 \u2205 \u2206 \u2207 " +
+ "\u2208 \u2209 \u220a \u220b \u220c \u220d \u220e \u220f",
+ ],
+ [
+ "=?UTF-8?B?4oiAICDiiIEgIOKIgiAg4oiDICDiiIQgIOKIhSAg4oiGICDiiIcgIOKIiA=" +
+ "==?=\n =?UTF-8?B?ICDiiIkgIOKIiiAg4oiLICDiiIwgIOKIjSAg4oiOICDiiI8=?=",
+ "\u2200 \u2201 \u2202 \u2203 \u2204 \u2205 \u2206 \u2207 " +
+ "\u2208 \u2209 \u220a \u220b \u220c \u220d \u220e \u220f",
+ ],
+
+ // Some interesting headers found in the wild:
+ // Bug 1498795: Tolerate spaces in base64-encoded RFC 2047 tokens.
+ [
+ "=?UTF-8?B?Q29uc2lndW Ug dG9kbyBlbCBmw7p0Ym9sIA==?= =?iso-8859-1?" +
+ "B?Y2 9uI EZVU0nTTiBjb24g?= =?windows-1252?B?ZX N 0ZSBvZmVydPNu?=",
+ "Consigue todo el f\u00fatbol con FUSI\u00d3N con este ofert\u00f3n",
+ ],
+ ["=?us-ascii?Q?=09Edward_Rosten?=", "\tEdward Rosten"],
+ [
+ "=?us-ascii?Q?=3D=3FUTF-8=3FQ=3Ff=3DC3=3DBCr=3F=3D?=",
+ "=?UTF-8?Q?f=C3=BCr?=",
+ ],
+ // We don't decode unrecognized charsets (This one is actually UTF-8).
+ ["=??B?Sy4gSC4gdm9uIFLDvGRlbg==?=", "=??B?Sy4gSC4gdm9uIFLDvGRlbg==?="],
+
+ // Test for bug 1374149 with ISO-2022-JP where we shouldn't stream
+ // if the first token ends in ESC(B.
+ // GyRCJCIbKEI= is the base64 encoding of ESC$B$"ESC(B.
+ [
+ "=?ISO-2022-JP?B?GyRCJCIbKEI=?==?ISO-2022-JP?B?GyRCJCIbKEI=?=",
+ "ああ",
+ ],
+
+ // Tolerate invalid split of character, € = 0xE2 0x82 0xAC in UTF-8.
+ [
+ "Split =?UTF-8?Q?=E2?= =?UTF-8?Q?=82=AC?= after first byte",
+ "Split € after first byte",
+ ],
+ [
+ "Split =?UTF-8?Q?=E2=82?= =?UTF-8?Q?=AC?= after second byte",
+ "Split € after second byte",
+ ],
+ ["Byte missing =?UTF-8?Q?=E2=82?=", "Byte missing \ufffd"], // Replacement character for invalid input.
+
+ // Test for bug 1301989: Tolerate invalid base64 encoding.
+ ["=?us-ascii?B?YWJjZA==?=", "abcd"], // correct
+ ["=?us-ascii?B?YWJjZA=?=", "abc"], // not a multiple of 4
+ ["=?us-ascii?B?Y=WJjZA==?=", "abcd"], // invalid =
+ ["=?us-ascii?B?Y=WJj==ZA==?=", "abcd"], // invalid =
+ ["=?us-ascii?B?YWJjZA===?=", "abcd"], // excess = at the end, see bug 227290.
+
+ // Test for bug 1437282: Tolerate extra = padding at the end.
+ ["=?us-ascii?B?VGVzdA==?=", "Test"], // This is correct.
+ ["=?us-ascii?B?VGVzdA===?=", "Test"],
+ ["=?us-ascii?B?VGVzdA====?=", "Test"],
+ ["=?us-ascii?B?VGVzdA=====?=", "Test"],
+ ["=?us-ascii?B?VGVzdA======?=", "Test"],
+ ["=?us-ascii?B?VGVzdA========?=", "Test"],
+ ["=?us-ascii?B?VGVzdA=========?=", "Test"],
+ ["=?us-ascii?B?VGVzdA==========?=", "Test"],
+ ["=?us-ascii?B?VGVzdA===========?=", "Test"],
+ ];
+ header_tests.forEach(function (data) {
+ arrayTest(data, function () {
+ assert.deepEqual(headerparser.decodeRFC2047Words(data[0]), data[1]);
+ });
+ });
+ });
+ suite("8-bit header processing", function () {
+ let header_tests = [
+ // Non-ASCII header values
+ ["oxyg\xc3\xa8ne", "oxyg\u00e8ne", "UTF-8"],
+ ["oxyg\xc3\xa8ne", "oxyg\u00e8ne", "ISO-8859-1"], // UTF-8 overrides
+ ["oxyg\xc3\xa8ne", "oxyg\u00e8ne"], // default to UTF-8 if no charset
+ ["oxyg\xe8ne", "oxyg\ufffdne", "UTF-8"],
+ ["oxyg\xe8ne", "oxyg\u00e8ne", "ISO-8859-1"],
+ ["\xc3\xa8\xe8", "\u00e8\ufffd", "UTF-8"],
+ ["\xc3\xa8\xe8", "\u00c3\u00a8\u00e8", "ISO-8859-1"],
+
+ // Don't fallback to UTF-16 or UTF-32
+ ["\xe8S!0", "\ufffdS!0", "UTF-16"],
+ ["\xe8S!0", "\ufffdS!0", "UTF-16be"],
+ ["\xe8S!0", "\ufffdS!0", "UTF-32"],
+ ["\xe8S!0", "\ufffdS!0", "utf-32"],
+
+ // Don't combine encoded-word and header charset decoding
+ ["=?UTF-8?Q?=c3?= \xa8", "\ufffd \ufffd", "UTF-8"],
+ ["=?UTF-8?Q?=c3?= \xa8", "\ufffd \u00a8", "ISO-8859-1"],
+ ["\xc3 =?UTF-8?Q?=a8?=", "\ufffd \ufffd", "UTF-8"],
+ ["\xc3 =?UTF-8?Q?=a8?=", "\u00c3 \ufffd", "ISO-8859-1"],
+ ];
+ header_tests.forEach(function (data) {
+ arrayTest(data, function () {
+ assert.deepEqual(
+ headerparser.decodeRFC2047Words(
+ headerparser.convert8BitHeader(
+ data[0],
+ data.length > 2 ? data[2] : null
+ )
+ ),
+ data[1]
+ );
+ });
+ });
+ });
+ });
+});