UNPKG

@vortiq/eml-parse-js

Version:
931 lines (930 loc) 29.1 kB
import { Base64 } from "js-base64"; import { TextDecoder, TextEncoder } from "@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 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 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(Base64.fromBase64(value.replace(/\r?\n/g, ""))), "utf8"); } else { return decode(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 = 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 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|(&quot;)/g, ""); try { if (encoding === "base64") { htmlContent = safeBase64Decode(htmlContent); } else { const decoded = safeBase64Decode(htmlContent); if (decoded !== htmlContent && 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; }; export { build as buildEml, parse as parseEml, read as readEml };