@vortiq/eml-parse-js
Version:
Format EML file in browser environment
931 lines (930 loc) • 29.3 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const jsBase64 = require("js-base64");
const textEncoding = require("@sinonjs/text-encoding");
function _handleAddress(tokens) {
let token;
let isGroup = false;
let state = "text";
let address;
let addresses = [];
let data = {
address: [],
comment: [],
group: [],
text: []
};
let i2;
let len;
for (i2 = 0, len = tokens.length; i2 < len; i2++) {
token = tokens[i2];
if (token.type === "operator") {
switch (token.value) {
case "<":
state = "address";
break;
case "(":
state = "comment";
break;
case ":":
state = "group";
isGroup = true;
break;
default:
state = "text";
}
} else if (token.value) {
if (state === "address") {
token.value = token.value.replace(/^[^<]*<\s*/, "");
}
data[state].push(token.value);
}
}
if (!data.text.length && data.comment.length) {
data.text = data.comment;
data.comment = [];
}
if (isGroup) {
data.text = data.text.join(" ");
addresses.push({
name: data.text || address && address.name,
group: data.group.length ? addressparser(data.group.join(",")) : []
});
} else {
if (!data.address.length && data.text.length) {
for (i2 = data.text.length - 1; i2 >= 0; i2--) {
if (data.text[i2].match(/^[^@\s]+@[^@\s]+$/)) {
data.address = data.text.splice(i2, 1);
break;
}
}
let _regexHandler = function(address2) {
if (!data.address.length) {
data.address = [address2.trim()];
return " ";
} else {
return address2;
}
};
if (!data.address.length) {
for (i2 = data.text.length - 1; i2 >= 0; i2--) {
data.text[i2] = data.text[i2].replace(/\s*\b[^@\s]+@[^\s]+\b\s*/, _regexHandler).trim();
if (data.address.length) {
break;
}
}
}
}
if (!data.text.length && data.comment.length) {
data.text = data.comment;
data.comment = [];
}
if (data.address.length > 1) {
data.text = data.text.concat(data.address.splice(1));
}
data.text = data.text.join(" ");
data.address = data.address.join(" ");
if (!data.address && isGroup) {
return [];
} else {
address = {
address: data.address || data.text || "",
name: data.text || data.address || ""
};
if (address.address === address.name) {
if ((address.address || "").match(/@/)) {
address.name = "";
} else {
address.address = "";
}
}
addresses.push(address);
}
}
return addresses;
}
class Tokenizer {
constructor(str) {
this.str = (str || "").toString();
this.operatorCurrent = "";
this.operatorExpecting = "";
this.node = null;
this.escaped = false;
this.list = [];
this.operators = {
'"': '"',
"(": ")",
"<": ">",
",": "",
":": ";",
// Semicolons are not a legal delimiter per the RFC2822 grammar other
// than for terminating a group, but they are also not valid for any
// other use in this context. Given that some mail clients have
// historically allowed the semicolon as a delimiter equivalent to the
// comma in their UI, it makes sense to treat them the same as a comma
// when used outside of a group.
";": ""
};
}
/**
* Tokenizes the original input string
*
* @return {Array} An array of operator|text tokens
*/
tokenize() {
let chr, list = [];
for (let i2 = 0, len = this.str.length; i2 < len; i2++) {
chr = this.str.charAt(i2);
this.checkChar(chr);
}
this.list.forEach((node) => {
node.value = (node.value || "").toString().trim();
if (node.value) {
list.push(node);
}
});
return list;
}
/**
* Checks if a character is an operator or text and acts accordingly
*
* @param {String} chr Character from the address field
*/
checkChar(chr) {
if (this.escaped) ;
else if (chr === this.operatorExpecting) {
this.node = {
type: "operator",
value: chr
};
this.list.push(this.node);
this.node = null;
this.operatorExpecting = "";
this.escaped = false;
return;
} else if (!this.operatorExpecting && chr in this.operators) {
this.node = {
type: "operator",
value: chr
};
this.list.push(this.node);
this.node = null;
this.operatorExpecting = this.operators[chr];
this.escaped = false;
return;
} else if (['"', "'"].includes(this.operatorExpecting) && chr === "\\") {
this.escaped = true;
return;
}
if (!this.node) {
this.node = {
type: "text",
value: ""
};
this.list.push(this.node);
}
if (chr === "\n") {
chr = " ";
}
if (chr.charCodeAt(0) >= 33 || [" ", " "].includes(chr)) {
this.node.value += chr;
}
this.escaped = false;
}
}
function addressparser(str, options) {
options = options || {};
let tokenizer = new Tokenizer(str);
let tokens = tokenizer.tokenize();
let addresses = [];
let address = [];
let parsedAddresses = [];
tokens.forEach((token) => {
if (token.type === "operator" && (token.value === "," || token.value === ";")) {
if (address.length) {
addresses.push(address);
}
address = [];
} else {
address.push(token);
}
});
if (address.length) {
addresses.push(address);
}
addresses.forEach((address2) => {
address2 = _handleAddress(address2);
if (address2.length) {
parsedAddresses = parsedAddresses.concat(address2);
}
});
if (options.flatten) {
let addresses2 = [];
let walkAddressList = (list) => {
list.forEach((address2) => {
if (address2.group) {
return walkAddressList(address2.group);
} else {
addresses2.push(address2);
}
});
};
walkAddressList(parsedAddresses);
return addresses2;
}
return parsedAddresses;
}
const getCharset = (contentType) => {
const match = /charset\s*=\W*([\w\-]+)/g.exec(contentType);
return match ? match[1] : void 0;
};
const encode = (str, fromCharset = "utf-8") => new textEncoding.TextEncoder(fromCharset).encode(str);
const arr2str = (arr) => {
const CHUNK_SZ = 32768;
const strs = [];
for (let i2 = 0; i2 < arr.length; i2 += CHUNK_SZ) {
strs.push(String.fromCharCode.apply(null, arr.subarray(i2, i2 + CHUNK_SZ)));
}
return strs.join("");
};
function decode(buf, fromCharset = "utf-8") {
const normalizedFromCharset = normalizeCharset(fromCharset);
const attempts = [];
if (normalizedFromCharset === "iso-8859-15") {
attempts.push({ charset: normalizedFromCharset, fatal: true });
attempts.push({ charset: normalizedFromCharset, fatal: false });
} else {
attempts.push({ charset: normalizedFromCharset, fatal: false });
}
if (normalizedFromCharset !== "utf-8") {
attempts.push({ charset: "utf-8", fatal: true });
}
if (normalizedFromCharset !== "iso-8859-15") {
attempts.push({ charset: "iso-8859-15", fatal: false });
}
for (const { charset, fatal } of attempts) {
try {
return new textEncoding.TextDecoder(charset, { fatal }).decode(buf);
} catch (e) {
}
}
return arr2str(buf);
}
function normalizeCharset(charset = "utf-8") {
let match;
if (match = charset.match(/^utf[-_]?(\d+)$/i)) {
return "UTF-" + match[1];
}
if (match = charset.match(/^win[-_]?(\d+)$/i)) {
return "WINDOWS-" + match[1];
}
if (match = charset.match(/^latin[-_]?(\d+)$/i)) {
return "ISO-8859-" + match[1];
}
return charset;
}
function getBoundary(contentType) {
const match = /(?:B|b)oundary=(?:\s*(?:'|")(.*?)(?:'|")|([^;\s\r\n"']{1,70}))(?:\s*;.*)?$/.exec(contentType);
return match ? match[1] || match[2] : void 0;
}
function getCharsetName(charset) {
return charset.toLowerCase().replace(/[^0-9a-z]/g, "");
}
function mimeDecode(str = "", fromCharset = "UTF-8") {
const encodedBytesCount = (str.match(/=[\da-fA-F]{2}/g) || []).length;
let buffer = new Uint8Array(str.length - encodedBytesCount * 2);
for (let i2 = 0, len = str.length, bufferPos = 0; i2 < len; i2++) {
let hex = str.substr(i2 + 1, 2);
const chr = str.charAt(i2);
if (chr === "=" && hex && /[\da-fA-F]{2}/.test(hex)) {
buffer[bufferPos++] = parseInt(hex, 16);
i2 += 2;
} else {
buffer[bufferPos++] = chr.charCodeAt(0);
}
}
return decode(buffer, fromCharset);
}
const DEFAULT_CHARSET = "utf-8";
const toEmailAddress = (data) => {
let email = "";
if (typeof data === "undefined") ;
else if (typeof data === "string") {
email = data;
} else if (typeof data === "object") {
if (Array.isArray(data)) {
email += data.map((item) => {
let str = "";
if (item.name) {
str += '"' + item.name.replace(/^"|"\s*$/g, "") + '" ';
}
if (item.email) {
str += "<" + item.email + ">";
}
return str;
}).filter((a) => a).join(", ");
} else {
if (data) {
if (data.name) {
email += '"' + data.name.replace(/^"|"\s*$/g, "") + '" ';
}
if (data.email) {
email += "<" + data.email + ">";
}
}
}
}
return email;
};
const decodeJoint = (str) => {
const match = /=\?([^?]+)\?(B|Q)\?([^?]+)\?=/gi.exec(str);
if (match) {
const charset = getCharsetName(match[1] || DEFAULT_CHARSET);
const type = match[2].toUpperCase();
const value = match[3];
if (type === "B") {
if (charset === "utf8") {
return decode(encode(jsBase64.Base64.fromBase64(value.replace(/\r?\n/g, ""))), "utf8");
} else {
return decode(jsBase64.Base64.toUint8Array(value.replace(/\r?\n/g, "")), charset);
}
} else if (type === "Q") {
return unquotePrintable(value, charset, true);
}
}
return str;
};
const unquotePrintable = (value, charset, qEncoding = false) => {
let rawString = value.replace(/[ \t]+$/gm, "").replace(/=(?:\r?\n|$)/g, "");
if (qEncoding) {
rawString = rawString.replace(/_/g, decode(new Uint8Array([32]), charset));
}
return mimeDecode(rawString, charset);
};
const unquoteString = (str) => {
const regex = /=\?([^?]+)\?(B|Q)\?([^?]+)\?=/gi;
let decodedString = str || "";
const spinOffMatch = decodedString.match(regex);
if (spinOffMatch) {
spinOffMatch.forEach((spin) => {
decodedString = decodedString.replace(spin, decodeJoint(spin));
});
}
return decodedString.replace(/\r?\n/g, "");
};
const getEmailAddress = (rawStr) => {
const addresses = [];
const raw = unquoteString(rawStr);
const parseList = addressparser(raw);
for (const v of parseList) {
addresses.push({ name: v.name, email: v.address });
}
return addresses;
};
function guid() {
return "xxxxxxxxxxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c2) {
const r = Math.random() * 16 | 0, v = c2 == "x" ? r : r & 3 | 8;
return v.toString(16);
}).replace("-", "");
}
function wrap(s2, i2) {
const a = [];
do {
a.push(s2.substring(0, i2));
} while ((s2 = s2.substring(i2, s2.length)) != "");
return a.join("\r\n");
}
const createBoundary = () => {
return "----=" + guid();
};
const build = (data, options) => {
let eml = "";
const EOL = "\r\n";
try {
if (!data) {
throw new Error('Argument "data" expected to be non-null');
}
if (!data.headers) {
throw new Error('Argument "data" expected to be has headers');
}
if (typeof data.subject === "string") {
data.headers["Subject"] = data.subject;
}
if (typeof data.from !== "undefined") {
data.headers["From"] = toEmailAddress(data.from);
}
if (typeof data.to !== "undefined") {
data.headers["To"] = toEmailAddress(data.to);
}
if (typeof data.cc !== "undefined") {
data.headers["Cc"] = toEmailAddress(data.cc);
}
const emlBoundary = getBoundary(data.headers["Content-Type"] || data.headers["Content-type"] || "");
let hasBoundary = false;
let boundary = createBoundary();
let multipartBoundary = "";
if (data.multipartAlternative) {
multipartBoundary = "" + (getBoundary(data.multipartAlternative["Content-Type"]) || "");
hasBoundary = true;
}
if (emlBoundary) {
boundary = emlBoundary;
hasBoundary = true;
} else {
data.headers["Content-Type"] = data.headers["Content-type"] || "multipart/mixed;" + EOL + 'boundary="' + boundary + '"';
}
const keys = Object.keys(data.headers);
for (let i2 = 0; i2 < keys.length; i2++) {
const key = keys[i2];
const value = data.headers[key];
if (typeof value === "undefined") {
continue;
} else if (typeof value === "string") {
eml += key + ": " + value.replace(/\r?\n/g, EOL + " ") + EOL;
} else {
for (let j = 0; j < value.length; j++) {
eml += key + ": " + value[j].replace(/\r?\n/g, EOL + " ") + EOL;
}
}
}
if (data.multipartAlternative) {
eml += EOL;
eml += "--" + emlBoundary + EOL;
eml += "Content-Type: " + data.multipartAlternative["Content-Type"].replace(/\r?\n/g, EOL + " ") + EOL;
}
eml += EOL;
if (data.text) {
if (typeof options === "object" && !!options && options.encode && data.textheaders) {
eml += "--" + boundary + EOL;
for (const key in data.textheaders) {
if (data.textheaders.hasOwnProperty(key)) {
eml += `${key}: ${data.textheaders[key].replace(/\r?\n/g, EOL + " ")}`;
}
}
} else if (hasBoundary) {
eml += "--" + (multipartBoundary ? multipartBoundary : boundary) + EOL;
eml += 'Content-Type: text/plain; charset="utf-8"' + EOL;
}
eml += EOL + data.text;
eml += EOL;
}
if (data.html) {
if (typeof options === "object" && !!options && options.encode && data.textheaders) {
eml += "--" + boundary + EOL;
for (const key in data.textheaders) {
if (data.textheaders.hasOwnProperty(key)) {
eml += `${key}: ${data.textheaders[key].replace(/\r?\n/g, EOL + " ")}`;
}
}
} else if (hasBoundary) {
eml += "--" + (multipartBoundary ? multipartBoundary : boundary) + EOL;
eml += 'Content-Type: text/html; charset="utf-8"' + EOL;
}
eml += EOL + data.html;
eml += EOL;
}
if (data.attachments) {
for (let i2 = 0; i2 < data.attachments.length; i2++) {
const attachment = data.attachments[i2];
eml += "--" + boundary + EOL;
eml += "Content-Type: " + (attachment.contentType.replace(/\r?\n/g, EOL + " ") || "application/octet-stream") + EOL;
eml += "Content-Transfer-Encoding: base64" + EOL;
eml += "Content-Disposition: " + (attachment.inline ? "inline" : "attachment") + '; filename="' + (attachment.filename || attachment.name || "attachment_" + (i2 + 1)) + '"' + EOL;
if (attachment.cid) {
eml += "Content-ID: <" + attachment.cid + ">" + EOL;
}
eml += EOL;
if (typeof attachment.data === "string") {
const content = jsBase64.Base64.toBase64(attachment.data);
eml += wrap(content, 72) + EOL;
} else {
const content = decode(attachment.data);
eml += wrap(content, 72) + EOL;
}
eml += EOL;
}
}
if (hasBoundary) {
eml += "--" + boundary + "--" + EOL;
}
} catch (e) {
const error2 = new Error("Error building EML");
error2.cause = e;
throw error2;
}
return eml;
};
const parse = (eml, options) => {
try {
if (typeof eml !== "string") {
throw new Error('Argument "eml" expected to be string!');
}
const lines = eml.split(/\r?\n/);
const result = parseRecursive(lines, 0, {}, options);
return result;
} catch (e) {
const error = new Error("Error parsing EML data");
error.cause = e;
throw error;
}
};
const parseRecursive = (lines, start, parent, options) => {
let boundary = null;
let lastHeaderName = "";
let findBoundary = "";
let insideBody = false;
let insideBoundary = false;
let isMultiHeader = false;
let isMultipart = false;
let checkedForCt = false;
let ctInBody = false;
parent.headers = {};
function complete(boundary2) {
boundary2.part = {};
parseRecursive(boundary2.lines, 0, boundary2.part, options);
delete boundary2.lines;
}
for (let i2 = start; i2 < lines.length; i2++) {
let line = lines[i2];
if (!insideBody) {
if (line == "") {
insideBody = true;
if (options && options.headersOnly) {
break;
}
let ct = parent.headers["Content-Type"] || parent.headers["Content-type"];
if (!ct) {
if (checkedForCt) {
insideBody = !ctInBody;
} else {
checkedForCt = true;
const lineClone = Array.from(lines);
const string = lineClone.splice(i2).join("\r\n");
const trimmedStrin = string.trim();
if (trimmedStrin.indexOf("Content-Type") === 0 || trimmedStrin.indexOf("Content-type") === 0) {
insideBody = false;
ctInBody = true;
} else {
console.warn("Warning: undefined Content-Type");
}
}
} else if (/^multipart\//g.test(ct)) {
let b = getBoundary(ct);
if (b && b.length) {
findBoundary = b;
isMultipart = true;
parent.body = [];
} else {
console.warn("Multipart without boundary! " + ct.replace(/\r?\n/g, " "));
}
}
continue;
}
let match = /^\s+([^\r\n]+)/g.exec(line);
if (match) {
if (isMultiHeader) {
parent.headers[lastHeaderName][parent.headers[lastHeaderName].length - 1] += "\r\n" + match[1];
} else {
parent.headers[lastHeaderName] += "\r\n" + match[1];
}
continue;
}
match = /^([\w\d\-]+):\s*([^\r\n]*)/gi.exec(line);
if (match) {
lastHeaderName = match[1];
if (parent.headers[lastHeaderName]) {
isMultiHeader = true;
if (typeof parent.headers[lastHeaderName] == "string") {
parent.headers[lastHeaderName] = [parent.headers[lastHeaderName]];
}
parent.headers[lastHeaderName].push(match[2]);
} else {
isMultiHeader = false;
parent.headers[lastHeaderName] = match[2];
}
continue;
}
} else {
if (isMultipart) {
if (line.indexOf("--" + findBoundary) == 0 && line.indexOf("--" + findBoundary + "--") !== 0) {
insideBoundary = true;
if (boundary && boundary.lines) {
complete(boundary);
}
let match = /^\-\-([^\r\n]+)(\r?\n)?$/g.exec(line);
boundary = { boundary: match[1], lines: [] };
parent.body.push(boundary);
continue;
}
if (insideBoundary) {
if ((boundary == null ? void 0 : boundary.boundary) && lines[i2 - 1] == "" && line.indexOf("--" + findBoundary + "--") == 0) {
insideBoundary = false;
complete(boundary);
continue;
}
if ((boundary == null ? void 0 : boundary.boundary) && line.indexOf("--" + findBoundary + "--") == 0) {
continue;
}
boundary == null ? void 0 : boundary.lines.push(line);
}
} else {
parent.body = lines.splice(i2).join("\r\n");
break;
}
}
}
if (parent.body && parent.body.length && parent.body[parent.body.length - 1].lines) {
complete(parent.body[parent.body.length - 1]);
}
return parent;
};
const Dig2Dec = (s) => {
let retV = 0;
if (s.length == 4) {
for (let i = 0; i < 4; i++) {
retV += eval(s.charAt(i)) * Math.pow(2, 3 - i);
}
return retV;
}
return -1;
};
const Hex2Utf8 = (s) => {
let retS = "";
let tempS = "";
let ss = "";
if (s.length == 16) {
tempS = "1110" + s.substring(0, 4);
tempS += "10" + s.substring(4, 10);
tempS += "10" + s.substring(10, 16);
let sss = "0123456789ABCDEF";
for (let i = 0; i < 3; i++) {
retS += "%";
ss = tempS.substring(i * 8, (eval(i.toString()) + 1) * 8);
retS += sss.charAt(Dig2Dec(ss.substring(0, 4)));
retS += sss.charAt(Dig2Dec(ss.substring(4, 8)));
}
return retS;
}
return "";
};
const Dec2Dig = (n1) => {
let s2 = "";
let n2 = 0;
for (let i2 = 0; i2 < 4; i2++) {
n2 = Math.pow(2, 3 - i2);
if (n1 >= n2) {
s2 += "1";
n1 = n1 - n2;
} else {
s2 += "0";
}
}
return s2;
};
const Str2Hex = (s) => {
let c = "";
let n;
let ss = "0123456789ABCDEF";
let digS = "";
for (let i = 0; i < s.length; i++) {
c = s.charAt(i);
n = ss.indexOf(c);
digS += Dec2Dig(eval(n.toString()));
}
return digS;
};
const GB2312UTF8 = (s1) => {
let s2 = escape(s1);
let sa = s2.split("%");
let retV2 = "";
if (sa[0] != "") {
retV2 = sa[0];
}
for (let i2 = 1; i2 < sa.length; i2++) {
if (sa[i2].substring(0, 1) == "u") {
retV2 += Hex2Utf8(Str2Hex(sa[i2].substring(1, 5)));
if (sa[i2].length) {
retV2 += sa[i2].substring(5);
}
} else {
retV2 += unescape("%" + sa[i2]);
if (sa[i2].length) {
retV2 += sa[i2].substring(5);
}
}
}
return retV2;
};
const BASE64_RE = /^[A-Za-z0-9+/]+={0,2}$/;
function isValidBase64(value) {
if (!value) return false;
const sanitized = value.replace(/\s+/g, "");
return sanitized.length % 4 === 0 && BASE64_RE.test(sanitized);
}
function safeBase64Decode(value) {
if (!isValidBase64(value)) {
return value;
}
try {
return jsBase64.Base64.decode(value);
} catch (err) {
console.warn("Base64 decode failed", err);
return value;
}
}
const read = (eml, options) => {
function _append(headers, content, result2) {
const contentType = headers["Content-Type"] || headers["Content-type"];
const contentDisposition = headers["Content-Disposition"];
const charset = getCharsetName(getCharset(contentType) || DEFAULT_CHARSET);
let encoding = headers["Content-Transfer-Encoding"] || headers["Content-transfer-encoding"];
if (typeof encoding === "string") {
encoding = encoding.toLowerCase();
}
if (encoding === "base64") {
if (contentType && contentType.indexOf("gbk") >= 0) {
content = encode(GB2312UTF8(content.replace(/\r?\n/g, "")));
} else {
content = encode(content.replace(/\r?\n/g, ""));
}
} else if (encoding === "quoted-printable") {
content = unquotePrintable(content, charset);
} else if (encoding && charset !== "utf8" && encoding.search(/binary|8bit/) === 0) {
content = decode(content, charset);
}
if (!contentDisposition && contentType && contentType.indexOf("text/html") >= 0) {
if (typeof content !== "string") {
content = decode(content, charset);
}
let htmlContent = content.replace(/\r\n|(")/g, "");
try {
if (encoding === "base64") {
htmlContent = safeBase64Decode(htmlContent);
} else {
const decoded = safeBase64Decode(htmlContent);
if (decoded !== htmlContent && jsBase64.Base64.btoa(decoded) === htmlContent.replace(/\s+/g, "")) {
htmlContent = decoded;
}
}
} catch (error) {
console.error(error);
}
if (result2.html) {
result2.html += htmlContent;
} else {
result2.html = htmlContent;
}
result2.htmlheaders = {
"Content-Type": contentType,
"Content-Transfer-Encoding": encoding || ""
};
} else if (!contentDisposition && contentType && contentType.indexOf("text/plain") >= 0) {
if (typeof content !== "string") {
content = decode(content, charset);
}
content = safeBase64Decode(content);
if (result2.text) {
result2.text += content;
} else {
result2.text = content;
}
result2.textheaders = {
"Content-Type": contentType,
"Content-Transfer-Encoding": encoding || ""
};
} else {
if (!result2.attachments) {
result2.attachments = [];
}
const attachment = {};
const id = headers["Content-ID"] || headers["Content-Id"];
if (id) {
attachment.id = id;
}
const NameContainer = ["Content-Disposition", "Content-Type", "Content-type"];
let result_name;
for (const key of NameContainer) {
const name = headers[key];
if (name) {
result_name = name.replace(/(\s|'|utf-8|\*[0-9]\*)/g, "").split(";").map((v) => /name[\*]?="?(.+?)"?$/gi.exec(v)).reduce((a, b) => {
if (b && b[1]) {
a += b[1];
}
return a;
}, "");
if (result_name) {
break;
}
}
}
if (result_name) {
attachment.name = decodeURI(result_name);
}
const ct = headers["Content-Type"] || headers["Content-type"];
if (ct) {
attachment.contentType = ct;
}
const cd = headers["Content-Disposition"];
if (cd) {
attachment.inline = /^\s*inline/g.test(cd);
}
attachment.data = content;
attachment.data64 = decode(content, charset);
result2.attachments.push(attachment);
}
}
function _read(data) {
if (!data) {
throw new Error("No data provided");
}
try {
const result2 = {};
if (!data.headers) {
throw new Error("data does't has headers");
}
if (data.headers["Date"]) {
result2.date = new Date(data.headers["Date"]);
} else {
throw new Error("Required Date header is missing");
}
if (data.headers["Subject"]) {
result2.subject = unquoteString(data.headers["Subject"]);
}
if (data.headers["From"]) {
result2.from = getEmailAddress(data.headers["From"]);
}
if (data.headers["To"]) {
result2.to = getEmailAddress(data.headers["To"]);
}
if (data.headers["CC"]) {
result2.cc = getEmailAddress(data.headers["CC"]);
}
if (data.headers["Cc"]) {
result2.cc = getEmailAddress(data.headers["Cc"]);
}
result2.headers = data.headers;
let boundary = null;
const ct = data.headers["Content-Type"] || data.headers["Content-type"];
if (ct && /^multipart\//g.test(ct)) {
const b = getBoundary(ct);
if (b && b.length) {
boundary = b;
}
}
if (boundary && Array.isArray(data.body)) {
for (let i2 = 0; i2 < data.body.length; i2++) {
const boundaryBlock = data.body[i2];
if (!boundaryBlock) {
continue;
}
if (typeof boundaryBlock.part === "undefined") {
console.warn("Warning: undefined b.part");
} else if (typeof boundaryBlock.part === "string") {
result2.data = boundaryBlock.part;
} else {
if (typeof boundaryBlock.part.body === "undefined") {
console.warn("Warning: undefined b.part.body");
} else if (typeof boundaryBlock.part.body === "string") {
_append(boundaryBlock.part.headers, boundaryBlock.part.body, result2);
} else {
const currentHeaders = boundaryBlock.part.headers;
const currentHeadersContentType = currentHeaders["Content-Type"] || currentHeaders["Content-type"];
if (currentHeadersContentType && currentHeadersContentType.indexOf("multipart") >= 0 && !result2.multipartAlternative) {
result2.multipartAlternative = {
"Content-Type": currentHeadersContentType
};
}
for (let j = 0; j < boundaryBlock.part.body.length; j++) {
const selfBoundary = boundaryBlock.part.body[j];
if (typeof selfBoundary === "string") {
result2.data = selfBoundary;
continue;
}
const headers = selfBoundary.part.headers;
const content = selfBoundary.part.body;
if (Array.isArray(content)) {
content.forEach((bound) => {
_append(bound.part.headers, bound.part.body, result2);
});
} else {
_append(headers, content, result2);
}
}
}
}
}
} else if (typeof data.body === "string") {
_append(data.headers, data.body, result2);
}
return result2;
} catch (e) {
return e;
}
}
const readResult = _read(eml);
const result = readResult;
return result;
};
exports.buildEml = build;
exports.parseEml = parse;
exports.readEml = read;