Revision control

Copy as Markdown

Other Tools

"use strict";
define(function (require) {
var assert = require("assert");
var jsmime = require("jsmime");
var fs = require("fs");
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);
}
// Returns and deletes object[field] if present, or undefined if not.
function extract_field(object, field) {
if (field in object) {
var result = object[field];
delete object[field];
return result;
}
return undefined;
}
// A file cache for read_file.
var file_cache = {};
/**
* Read a file into a string (all line endings become CRLF).
*
* @param {string} file - The name of the file to read, relative to the data/ directory.
* @param {integer} [start=0] - The first line of the file to return.
* @param {integer} [end] - The last line of the file to return,
* defaulting to the number of lines in the file.
* @returns {Promise<string>} the contents of the file as a binary string.
*/
function read_file(file, start, end) {
if (!(file in file_cache)) {
var realFile = new Promise(function (resolve, reject) {
fs.readFile("data/" + file, function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
var loader = realFile.then(function (contents) {
var inStrForm = "";
while (contents.length > 0) {
inStrForm += String.fromCharCode.apply(
null,
contents.subarray(0, 1024)
);
contents = contents.subarray(1024);
}
return inStrForm.split(/\r\n|[\r\n]/);
});
file_cache[file] = loader;
}
return file_cache[file].then(function (contents) {
if (start !== undefined) {
contents = contents.slice(start - 1, end - 1);
}
return contents.join("\r\n");
});
}
/**
* Helper for body tests.
*
* Some extra options are listed too:
* _split: The contents of the file will be passed in packets split by this
* regex. Be sure to include the split delimiter in a group so that they
* are included in the output packets!
* _eol: The CRLFs in the input file will be replaced with the given line
* ending instead.
*
* @param {string} test - The name of test.
* @param {string} file - The name of the file to read (relative to mailnews/data).
* @param {object} opts - Options for the mime parser, as well as a few extras
* detailed above.
* @param {Array} partspec - An array of [partnum, line start, line end]
* detailing the expected parts in the body. It will be expected that the
* accumulated body part data for partnum would be the contents
* of the file from [line start, line end) [1-based lines]
*/
function make_body_test(test, file, opts, partspec) {
var results = Promise.all(
partspec.map(p => Promise.all([p[0], read_file(file, p[1], p[2])]))
);
var eol = extract_field(opts, "_eol");
var msgtext = read_file(file).then(function (msgcontents) {
var packetize = extract_field(opts, "_split");
if (packetize !== undefined) {
msgcontents = msgcontents.split(packetize);
}
if (eol !== undefined) {
msgcontents = msgcontents.replace(/\r\n/g, eol);
}
return msgcontents;
});
if (eol !== undefined) {
results = results.then(function (results_) {
for (const part of results_) {
part[1] = part[1].replace(/\r\n/g, eol);
}
return results_;
});
}
return [test, msgtext, opts, results];
}
/**
* Execute a single MIME tree test.
*
* @param {string|string[]|Promise} message - Either the text of the message, an array of textual message
* part data (imagine coming on different TCP packets), or a
* promise that resolves to any of the above.
* @param {object} opts - A set of options for the parser and for the test.
* @param {object|Array} results - The expected results of the call.
* This may either be adictionary of part number -> header -> values
* (to check headers), or an array of [partnum, partdata] for expected
* results to deliverPartData, or a promise for the above.
* @returns {Promise} A promise containing the results of the test.
*/
function testParser(message, opts, results) {
var uncheckedValues;
var checkingHeaders;
var calls = 0;
var fusingParts = extract_field(opts, "_nofuseparts") === undefined;
var emitter = {
stack: [],
startMessage: function emitter_startMsg() {
assert.equal(this.stack.length, 0);
calls++;
this.partData = "";
},
endMessage: function emitter_endMsg() {
assert.equal(this.stack.length, 0);
calls++;
},
startPart: function emitter_startPart(partNum, headers) {
this.stack.push(partNum);
if (checkingHeaders) {
assert.ok(partNum in uncheckedValues);
// Headers is a map, convert it to an object.
var objmap = {};
for (const pair of headers) {
objmap[pair[0]] = pair[1];
}
var expected = uncheckedValues[partNum];
var convresults = {};
for (const key in expected) {
try {
convresults[key] = jsmime.headerparser.parseStructuredHeader(
key,
expected[key]
);
} catch (e) {
convresults[key] = expected[key];
}
}
assert.deepEqual(objmap, convresults);
if (fusingParts) {
assert.equal(this.partData, "");
}
delete uncheckedValues[partNum];
}
},
deliverPartData: function emitter_partData(partNum, data) {
assert.equal(this.stack[this.stack.length - 1], partNum);
if (!checkingHeaders) {
if (fusingParts) {
this.partData += data;
} else {
const check = uncheckedValues.shift();
assert.equal(partNum, check[0]);
assert.equal(data, check[1]);
}
}
},
endPart: function emitter_endPart(partNum) {
if (this.partData != "") {
const check = uncheckedValues.shift();
assert.equal(partNum, check[0]);
assert.equal(this.partData, check[1]);
this.partData = "";
}
assert.equal(this.stack.pop(), partNum);
},
};
opts.onerror = function (e) {
throw e;
};
return Promise.all([message, results]).then(function (vals) {
let [message_, results_] = vals;
// Clone the results array into uncheckedValues
if (Array.isArray(results_)) {
uncheckedValues = Array.from(results_);
checkingHeaders = false;
} else {
uncheckedValues = {};
for (const key in results_) {
uncheckedValues[key] = results_[key];
}
checkingHeaders = true;
}
if (!Array.isArray(message_)) {
message_ = [message_];
}
var parser = new jsmime.MimeParser(emitter, opts);
message_.forEach(function (packet) {
parser.deliverData(packet);
});
parser.deliverEOF();
assert.equal(calls, 2);
if (!checkingHeaders) {
assert.equal(0, uncheckedValues.length);
} else {
assert.deepEqual({}, uncheckedValues);
}
});
}
suite("MimeParser", function () {
// This is the expected part specifier for the multipart-complex1 test file,
// specified here because it is used in several cases.
const mpart_complex1 = [
["1", 8, 10],
["2", 14, 16],
["3.1", 22, 24],
["4", 29, 31],
["5", 33, 35],
];
suite("Simple tests", function () {
const parser_tests = [
// The following tests are either degenerate or error cases that should
// work
["Empty string", "", {}, { "": {} }],
["No value for header", "Header", {}, { "": { Header: [""] } }],
[
"No trailing newline",
"To: eof@example.net",
{},
{ "": { To: ["eof@example.net"] } },
],
[
"Header no val",
"To: eof@example.net\r\n",
{},
{ "": { To: ["eof@example.net"] } },
],
["No body no headers", "\r\n\r\n", {}, { "": {} }],
["Body no headers", "\r\n\r\nA", {}, { "": {} }],
// Basic cases for headers
[
"Multiparts get headers",
read_file("multipart-complex1"),
{},
{
"": { "Content-Type": ['multipart/mixed; boundary="boundary"'] },
1: {
"Content-Type": ["application/octet-stream"],
"Content-Transfer-Encoding": ["base64"],
},
2: {
"Content-Type": ["image/png"],
"Content-Transfer-Encoding": ["base64"],
},
3: {
"Content-Type": ['multipart/related; boundary="boundary2"'],
},
3.1: { "Content-Type": ["text/html"] },
4: { "Content-Type": ["text/plain"] },
5: {},
},
],
];
parser_tests.forEach(function (data) {
arrayTest(data, function () {
return testParser(data[1], data[2], data[3]);
});
});
});
suite("Body tests", function () {
const parser_tests = [
// Body tests from data
// (Note: line numbers are 1-based. Also, to capture trailing EOF, add 2
// to the last line number of the file).
make_body_test("Basic body", "basic1", {}, [["", 3, 5]]),
make_body_test("Basic multipart", "multipart1", {}, [["1", 10, 12]]),
make_body_test("Basic multipart", "multipart2", {}, [["1", 8, 11]]),
make_body_test(
"Complex multipart",
"multipart-complex1",
{},
mpart_complex1
),
make_body_test("Truncated multipart", "multipart-complex2", {}, [
["1.1.1.1", 21, 25],
["2", 27, 57],
["3", 60, 62],
]),
make_body_test("No LF multipart", "multipartmalt-detach", {}, [
["1", 20, 21],
["2.1", 27, 38],
["2.2", 42, 43],
["2.3", 47, 48],
["3", 53, 54],
]),
make_body_test("Raw body", "multipart1", { bodyformat: "raw" }, [
["", 4, 14],
]),
[
"Base64 decode 1",
read_file("base64-1"),
{ bodyformat: "decode" },
[
[
"",
"\r\nHello, world! (Again...)\r\n\r\nLet's see how well base64 " +
"text is handled. Yay, lots of space" +
"s! There's even a CRLF at the end and one at the beginning, bu" +
"t the output shouldn't have it.\r\n",
],
],
],
[
"Base64 decode 2",
read_file("base64-2"),
{ bodyformat: "decode" },
[
[
"",
"<html><body>This is base64 encoded HTML text, and the tags sho" +
"uldn't be stripped.\r\n<b>Bold text is bold!</b></body></html>" +
"\r\n",
],
],
],
[
"Base64 decode line issues",
read_file("base64-2").then(function (s) {
return s.split(/(\r\n)/);
}),
{ bodyformat: "decode" },
[
[
"",
"<html><body>This is base64 encoded HTML text, and the tags sho" +
"uldn't be stripped.\r\n<b>Bold text is bold!</b></body></html>" +
"\r\n",
],
],
],
make_body_test("Base64 nodecode", "base64-1", {}, [["", 4, 9]]),
[
"QP decode",
read_file("bug505221"),
{ pruneat: "1", bodyformat: "decode" },
[
[
"1",
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"' +
'>\r\n<HTML><HEAD>\r\n<META HTTP-EQUIV="Content-Type" CONTENT=' +
'"text/html; charset=us-ascii">\r\n\r\n\r\n<META content="MSHT' +
'ML 6.00.6000.16735" name=GENERATOR></HEAD>\r\n<BODY> bbb\r\n<' +
"/BODY></HTML>",
],
],
],
[
"Nested messages",
read_file("message-encoded"),
{ bodyformat: "decode" },
[
["1$", "This is a plain-text message."],
["2$", "I am a plain-text message."],
["3$", "I am an encoded plain-text message."],
],
],
[
"Nested message headers",
read_file("message-encoded"),
{},
{
"": {
"Content-Type": ['multipart/mixed; boundary="iamaboundary"'],
},
1: { "Content-Type": ["message/rfc822"] },
"1$": { Subject: ["I am a subject"] },
2: {
"Content-Type": ["message/global"],
"Content-Transfer-Encoding": ["base64"],
},
"2$": { Subject: ["\u79c1\u306f\u3001\u4ef6\u540d\u5348\u524d"] },
3: {
"Content-Type": ["message/news"],
"Content-Transfer-Encoding": ["quoted-printable"],
},
"3$": { Subject: ["\u79c1\u306f\u3001\u4ef6\u540d\u5348\u524d"] },
},
],
];
parser_tests.forEach(function (data) {
arrayTest(data, function () {
return testParser(data[1], data[2], data[3]);
});
});
});
suite("Torture tests", function () {
// Generate a very long message for tests
let teststr = "a";
for (let i = 0; i < 16; i++) {
teststr += teststr;
}
const parser_tests = [
[
"Base64 very long decode",
"Content-Transfer-Encoding: base64\r\n\r\n" + btoa(teststr) + "\r\n",
{ bodyformat: "decode" },
[["", teststr]],
],
make_body_test("Torture regular body", "mime-torture", {}, [
["1", 17, 21],
["2$.1", 58, 75],
["2$.2.1", 83, 97],
["2$.3", 102, 130],
["3$", 155, 7742],
["4", 7747, 8213],
["5", 8218, 8242],
["6$.1.1", 8284, 8301],
["6$.1.2", 8306, 8733],
["6$.2.1", 8742, 9095],
["6$.2.2", 9100, 9354],
["6$.2.3", 9357, 11794],
["6$.2.4", 11797, 12155],
["6$.3", 12161, 12809],
["7$.1", 12844, 12845],
["7$.2", 12852, 13286],
["7$.3", 13288, 13297],
["8$.1", 13331, 13358],
["8$.2", 13364, 13734],
["9$", 13757, 20179],
["10", 20184, 21200],
["11$.1", 21223, 22031],
["11$.2", 22036, 22586],
["12$.1", 22607, 23469],
["12$.2", 23474, 23774],
["12$.3$.1", 23787, 23795],
["12$.3$.2.1", 23803, 23820],
["12$.3$.2.2", 23825, 24633],
["12$.3$.3", 24640, 24836],
["12$.3$.4$", 24848, 25872],
]),
make_body_test("Torture pruneat", "mime-torture", { pruneat: "4" }, [
["4", 7747, 8213],
]),
// Test packetization problems
make_body_test(
"Large packets",
"multipart-complex1",
{ _split: /(.{30})/ },
mpart_complex1
),
make_body_test(
"Split on newline",
"multipart-complex1",
{ _split: /(\r\n)/ },
mpart_complex1
),
make_body_test(
"Pathological splitting",
"multipart-complex1",
{ _split: "" },
mpart_complex1
),
// Non-CLRF line endings?
make_body_test(
"LF-based messages",
"multipart-complex1",
{ _eol: "\n" },
mpart_complex1
),
make_body_test(
"CR-based messages",
"multipart-complex1",
{ _eol: "\r" },
mpart_complex1
),
];
parser_tests.forEach(function (data) {
arrayTest(data, function () {
return testParser(data[1], data[2], data[3]);
});
});
});
suite("Header tests", function () {
const parser_tests = [
// Basic cases for headers
[
"Multiparts get headers",
read_file("multipart-complex1"),
{},
{
"": { "Content-Type": ['multipart/mixed; boundary="boundary"'] },
1: {
"Content-Type": ["application/octet-stream"],
"Content-Transfer-Encoding": ["base64"],
},
2: {
"Content-Type": ["image/png"],
"Content-Transfer-Encoding": ["base64"],
},
3: {
"Content-Type": ['multipart/related; boundary="boundary2"'],
},
3.1: { "Content-Type": ["text/html"] },
4: { "Content-Type": ["text/plain"] },
5: {},
},
],
// 'From ' is not an [iterable] header
[
"Exclude mbox delimiter",
read_file("bugmail11"),
{},
{
"": {
"X-Mozilla-Status": ["0001"],
"X-Mozilla-Status2": ["00000000"],
"X-Mozilla-Keys": [""],
"Return-Path": [
"<example@example.com>",
"<bugzilla-daemon@mozilla.org>",
],
"Delivered-To": ["bugmail@example.org"],
Received: [
"by 10.114.166.12 with SMTP id o12cs163262wae;" +
" Fri, 11 Apr 2008 07:17:31 -0700 (PDT)",
"by 10.115.60.1 with SMTP id n1mr214763wak.181.1207923450166;" +
" Fri, 11 Apr 2008 07:17:30 -0700 (PDT)",
"from webapp-out.mozilla.org (webapp01.sj.mozilla.com [63.245.208.1" +
"46]) by mx.google.com with ESMTP id n38si6807242wag.2.2008." +
"04.11.07.17.29; Fri, 11 Apr 2008 07:17:30 -0700 (PDT)",
"from mrapp51.mozilla.org (mrapp51.mozilla.org [127.0.0.1])" +
"\tby webapp-out.mozilla.org (8.13.8/8.13.8) with ESMTP id m3BEHTGU" +
"030132\tfor <bugmail@example.org>; Fri, 11 Apr 2008 07:17:29 -0700",
"(from root@localhost)" +
"\tby mrapp51.mozilla.org (8.13.8/8.13.8/Submit) id m3BEHTk4030129;" +
"\tFri, 11 Apr 2008 07:17:29 -0700",
],
"Received-Spf": [
"neutral (google.com: 63.245.208.146 is neither perm" +
"itted nor denied by best guess record for domain of bugzilla-daemo" +
"n@mozilla.org) client-ip=63.245.208.146;",
],
"Authentication-Results": [
"mx.google.com; spf=neutral (google.com: 6" +
"3.245.208.146 is neither permitted nor denied by best guess record" +
" for domain of bugzilla-daemon@mozilla.org) smtp.mail=bugzilla-dae" +
"mon@mozilla.org",
],
Date: ["Fri, 11 Apr 2008 07:17:29 -0700"],
"Message-ID": [
"<200804111417.m3BEHTk4030129@mrapp51.mozilla.org>",
],
From: ["bugzilla-daemon@mozilla.org"],
To: ["bugmail@example.org"],
Subject: ["Bugzilla: confirm account creation"],
"X-Bugzilla-Type": ["admin"],
"Content-Type": ['text/plain; charset="UTF-8"'],
"MIME-Version": ["1.0"],
},
},
],
];
parser_tests.forEach(function (data) {
arrayTest(data, function () {
return testParser(data[1], data[2], data[3]);
});
});
});
suite("Charset tests", function () {
function buildTree(file, options) {
var tree = new Map();
var emitter = {
startPart(part, headers) {
tree.set(part, { headers, body: null });
},
deliverPartData(part, data) {
var obj = tree.get(part);
if (obj.body === null) {
obj.body = data;
} else if (typeof obj.body === "string") {
obj.body += data;
} else {
var newData = new Uint8Array(obj.body.length + data.length);
newData.set(obj.body);
newData.subarray(obj.body.length).set(data);
obj.body = newData;
}
},
};
return file.then(function (data) {
var parser = new jsmime.MimeParser(emitter, options);
parser.deliverData(data);
parser.deliverEOF();
return tree;
});
}
test("Unicode decoding", function () {
return buildTree(read_file("shift-jis-image"), {
strformat: "unicode",
bodyformat: "decode",
}).then(function (tree) {
// text/plain should be transcoded...
assert.equal(
tree.get("1").headers.get("Content-Type").get("charset"),
"Shift-JIS"
);
assert.equal(tree.get("1").headers.charset, "Shift-JIS");
assert.equal(
tree.get("1").headers.get("Content-Description"),
"\u30b1\u30c4\u30a1\u30eb\u30b3\u30a2\u30c8\u30eb"
);
assert.equal(
tree.get("1").body,
"Portable Network Graphics\uff08" +
"\u30dd\u30fc\u30bf\u30d6\u30eb\u30fb\u30cd\u30c3\u30c8\u30ef\u30fc" +
"\u30af\u30fb\u30b0\u30e9\u30d5\u30a3\u30c3\u30af\u30b9\u3001PNG" +
"\uff09\u306f\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u3067\u30d3\u30c3" +
"\u30c8\u30de\u30c3\u30d7\u753b\u50cf\u3092\u6271\u3046\u30d5\u30a1" +
"\u30a4\u30eb\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u3042\u308b" +
"\u3002\u5727\u7e2e\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0\u3068\u3057" +
"\u3066Deflate\u3092\u63a1\u7528\u3057\u3066\u3044\u308b\u3001" +
"\u5727\u7e2e\u306b\u3088\u308b\u753b\u8cea\u306e\u52a3\u5316\u306e" +
"\u306a\u3044\u53ef\u9006\u5727\u7e2e\u306e\u753b\u50cf\u30d5\u30a1" +
"\u30a4\u30eb\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u3042\u308b" +
"\u3002\r\n"
);
// ... but not image/png
assert.ok(!tree.get("2").headers.get("Content-Type").has("charset"));
assert.equal(tree.get("2").headers.charset, "");
assert.equal(
tree.get("2").headers.get("Content-Description"),
"\ufffdP\ufffdc\ufffd@\ufffd\ufffd\ufffdR\ufffdA\ufffdg\ufffd\ufffd"
);
assert.equal(
tree.get("2").headers.getRawHeader("Content-Description"),
"\x83\x50\x83\x63\x83\x40\x83\x8b\x83\x52\x83\x41\x83\x67\x83\x8b"
);
var imageData =
"iVBORw0KGgoAAAANSUhEUgAAAIAAAABECAIAAADGJao+AAAAwklE" +
"QVR4Xu3UgQbDMBRA0bc03f//b7N0VuqJEmwoc+KqNEkDh9b+2HuJu1KNO4f+AQCAAA" +
"AQAAACAEAAAAgAAAEAIAAABACAAAAQAAACAEAAAAgAAAEAIAAAANReamRLlPWYfNH0" +
"klxcPs+cP3NxWF+vi3lb7pa2R+vx6tHOtuN1O+a5lY3HzgM5ya/GM5N7ZjfPq7/5yS" +
"8IgAAAEAAAAgBAAAAIAAABACAAAAQAgAAAEAAAAgBAAAAIAAABACAAAIw322gDIPvt" +
"lmUAAAAASUVORK5CYII=";
imageData = atob(imageData);
var asArray = new Uint8Array(imageData.length);
for (var i = 0; i < asArray.length; i++) {
asArray[i] = imageData.charCodeAt(i);
}
assert.deepEqual(tree.get("2").body, asArray);
// Touching the header charset should change the interpretation.
tree.get("1").headers.charset = "Shift-JIS";
assert.equal(tree.get("1").headers.charset, "Shift-JIS");
assert.equal(
tree.get("1").headers.get("Content-Description"),
"\u30b1\u30c4\u30a1\u30eb\u30b3\u30a2\u30c8\u30eb"
);
});
});
test("Fallback charset decoding", function () {
return buildTree(read_file("shift-jis-image"), {
strformat: "unicode",
charset: "ISO-8859-1",
bodyformat: "decode",
}).then(function (tree) {
// text/plain should be transcoded...
assert.equal(
tree.get("1").headers.get("Content-Type").get("charset"),
"Shift-JIS"
);
assert.equal(tree.get("1").headers.charset, "Shift-JIS");
assert.equal(
tree.get("1").headers.get("Content-Description"),
"\u30b1\u30c4\u30a1\u30eb\u30b3\u30a2\u30c8\u30eb"
);
assert.equal(
tree.get("1").body,
"Portable Network Graphics\uff08" +
"\u30dd\u30fc\u30bf\u30d6\u30eb\u30fb\u30cd\u30c3\u30c8\u30ef\u30fc" +
"\u30af\u30fb\u30b0\u30e9\u30d5\u30a3\u30c3\u30af\u30b9\u3001PNG" +
"\uff09\u306f\u30b3\u30f3\u30d4\u30e5\u30fc\u30bf\u3067\u30d3\u30c3" +
"\u30c8\u30de\u30c3\u30d7\u753b\u50cf\u3092\u6271\u3046\u30d5\u30a1" +
"\u30a4\u30eb\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u3042\u308b" +
"\u3002\u5727\u7e2e\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0\u3068\u3057" +
"\u3066Deflate\u3092\u63a1\u7528\u3057\u3066\u3044\u308b\u3001" +
"\u5727\u7e2e\u306b\u3088\u308b\u753b\u8cea\u306e\u52a3\u5316\u306e" +
"\u306a\u3044\u53ef\u9006\u5727\u7e2e\u306e\u753b\u50cf\u30d5\u30a1" +
"\u30a4\u30eb\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u3042\u308b" +
"\u3002\r\n"
);
// ... but not image/png
assert.ok(!tree.get("2").headers.get("Content-Type").has("charset"));
assert.equal(tree.get("2").headers.charset, "ISO-8859-1");
assert.equal(
tree.get("2").headers.get("Content-Description"),
"\u0192P\u0192c\u0192@\u0192\u2039\u0192R\u0192A\u0192g\u0192\u2039"
);
assert.equal(
tree.get("2").headers.getRawHeader("Content-Description"),
"\x83\x50\x83\x63\x83\x40\x83\x8b\x83\x52\x83\x41\x83\x67\x83\x8b"
);
var imageData =
"iVBORw0KGgoAAAANSUhEUgAAAIAAAABECAIAAADGJao+AAAAwklE" +
"QVR4Xu3UgQbDMBRA0bc03f//b7N0VuqJEmwoc+KqNEkDh9b+2HuJu1KNO4f+AQCAAA" +
"AQAAACAEAAAAgAAAEAIAAABACAAAAQAAACAEAAAAgAAAEAIAAAANReamRLlPWYfNH0" +
"klxcPs+cP3NxWF+vi3lb7pa2R+vx6tHOtuN1O+a5lY3HzgM5ya/GM5N7ZjfPq7/5yS" +
"8IgAAAEAAAAgBAAAAIAAABACAAAAQAgAAAEAAAAgBAAAAIAAABACAAAIw322gDIPvt" +
"lmUAAAAASUVORK5CYII=";
imageData = atob(imageData);
var asArray = new Uint8Array(imageData.length);
for (var i = 0; i < asArray.length; i++) {
asArray[i] = imageData.charCodeAt(i);
}
assert.deepEqual(tree.get("2").body, asArray);
// Touching the header charset should change the interpretation.
tree.get("1").headers.charset = "Shift-JIS";
assert.equal(tree.get("1").headers.charset, "Shift-JIS");
assert.equal(
tree.get("1").headers.get("Content-Description"),
"\u30b1\u30c4\u30a1\u30eb\u30b3\u30a2\u30c8\u30eb"
);
});
});
test("Forced charset decoding", function () {
return buildTree(read_file("shift-jis-image"), {
strformat: "unicode",
charset: "ISO-8859-1",
"force-charset": true,
bodyformat: "decode",
}).then(function (tree) {
// text/plain should be transcoded...
assert.equal(
tree.get("1").headers.get("Content-Type").get("charset"),
"Shift-JIS"
);
assert.equal(tree.get("1").headers.charset, "ISO-8859-1");
assert.equal(
tree.get("1").headers.get("Content-Description"),
"\u0192P\u0192c\u0192@\u0192\u2039\u0192R\u0192A\u0192g\u0192\u2039"
);
assert.equal(
tree.get("1").body,
"Portable Network Graphics\u0081i" +
"\u0192|\u0081[\u0192^\u0192u\u0192\u2039\u0081E\u0192l\u0192b" +
"\u0192g\u0192\u008f\u0081[\u0192N\u0081E\u0192O\u0192\u2030\u0192t" +
"\u0192B\u0192b\u0192N\u0192X\u0081APNG\u0081j\u201a\u00cd\u0192R" +
"\u0192\u201c\u0192s\u0192\u2026\u0081[\u0192^\u201a\u00c5\u0192r" +
"\u0192b\u0192g\u0192}\u0192b\u0192v\u2030\u00e6\u2018\u0153\u201a" +
"\u00f0\u02c6\u00b5\u201a\u00a4\u0192t\u0192@\u0192C\u0192\u2039" +
"\u0192t\u0192H\u0081[\u0192}\u0192b\u0192g\u201a\u00c5\u201a\u00a0" +
"\u201a\u00e9\u0081B\u02c6\u00b3\u008fk\u0192A\u0192\u2039\u0192S" +
"\u0192\u0160\u0192Y\u0192\u20ac\u201a\u00c6\u201a\u00b5\u201a" +
"\u00c4Deflate\u201a\u00f0\u008d\u00cc\u2014p\u201a\u00b5\u201a" +
"\u00c4\u201a\u00a2\u201a\u00e9\u0081A\u02c6\u00b3\u008fk\u201a" +
"\u00c9\u201a\u00e6\u201a\u00e9\u2030\u00e6\u017d\u00bf\u201a\u00cc" +
"\u2014\u00f2\u2030\u00bb\u201a\u00cc\u201a\u00c8\u201a\u00a2\u2030" +
"\u00c2\u2039t\u02c6\u00b3\u008fk\u201a\u00cc\u2030\u00e6\u2018" +
"\u0153\u0192t\u0192@\u0192C\u0192\u2039\u0192t\u0192H\u0081[\u0192" +
"}\u0192b\u0192g\u201a\u00c5\u201a\u00a0\u201a\u00e9\u0081B\r\n"
);
// ... but not image/png
assert.ok(!tree.get("2").headers.get("Content-Type").has("charset"));
assert.equal(tree.get("2").headers.charset, "ISO-8859-1");
assert.equal(
tree.get("2").headers.get("Content-Description"),
"\u0192P\u0192c\u0192@\u0192\u2039\u0192R\u0192A\u0192g\u0192\u2039"
);
assert.equal(
tree.get("2").headers.getRawHeader("Content-Description"),
"\x83\x50\x83\x63\x83\x40\x83\x8b\x83\x52\x83\x41\x83\x67\x83\x8b"
);
var imageData =
"iVBORw0KGgoAAAANSUhEUgAAAIAAAABECAIAAADGJao+AAAAwklE" +
"QVR4Xu3UgQbDMBRA0bc03f//b7N0VuqJEmwoc+KqNEkDh9b+2HuJu1KNO4f+AQCAAA" +
"AQAAACAEAAAAgAAAEAIAAABACAAAAQAAACAEAAAAgAAAEAIAAAANReamRLlPWYfNH0" +
"klxcPs+cP3NxWF+vi3lb7pa2R+vx6tHOtuN1O+a5lY3HzgM5ya/GM5N7ZjfPq7/5yS" +
"8IgAAAEAAAAgBAAAAIAAABACAAAAQAgAAAEAAAAgBAAAAIAAABACAAAIw322gDIPvt" +
"lmUAAAAASUVORK5CYII=";
imageData = atob(imageData);
var asArray = new Uint8Array(imageData.length);
for (var i = 0; i < asArray.length; i++) {
asArray[i] = imageData.charCodeAt(i);
}
assert.deepEqual(tree.get("2").body, asArray);
// Touching the header charset should change the interpretation.
tree.get("1").headers.charset = "Shift-JIS";
assert.equal(tree.get("1").headers.charset, "Shift-JIS");
assert.equal(
tree.get("1").headers.get("Content-Description"),
"\u30b1\u30c4\u30a1\u30eb\u30b3\u30a2\u30c8\u30eb"
);
});
});
test("Charset conversion", function () {
return buildTree(read_file("charsets"), {
strformat: "unicode",
bodyformat: "decode",
}).then(function (tree) {
var numParts = 12;
for (let i = 1; i < numParts; i += 2) {
assert.equal(tree.get("" + i).body, tree.get("" + (i + 1)).body);
}
assert.ok(!tree.has("" + (numParts + 1)));
});
});
});
});
});