"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var postal_mime_exports = {}; __export(postal_mime_exports, { addressParser: () => import_address_parser.default, decodeWords: () => import_decode_strings.decodeWords, default: () => PostalMime }); module.exports = __toCommonJS(postal_mime_exports); var import_mime_node = __toESM(require("./mime-node.cjs"), 1); var import_text_format = require("./text-format.cjs"); var import_address_parser = __toESM(require("./address-parser.cjs"), 1); var import_decode_strings = require("./decode-strings.cjs"); var import_base64_encoder = require("./base64-encoder.cjs"); const MAX_NESTING_DEPTH = 256; const MAX_HEADERS_SIZE = 2 * 1024 * 1024; function toCamelCase(key) { return key.replace(/-(.)/g, (o, c) => c.toUpperCase()); } class PostalMime { static parse(buf, options) { const parser = new PostalMime(options); return parser.parse(buf); } constructor(options) { this.options = options || {}; this.mimeOptions = { maxNestingDepth: this.options.maxNestingDepth || MAX_NESTING_DEPTH, maxHeadersSize: this.options.maxHeadersSize || MAX_HEADERS_SIZE }; this.root = this.currentNode = new import_mime_node.default({ postalMime: this, ...this.mimeOptions }); this.boundaries = []; this.textContent = {}; this.attachments = []; this.attachmentEncoding = (this.options.attachmentEncoding || "").toString().replace(/[-_\s]/g, "").trim().toLowerCase() || "arraybuffer"; this.started = false; } async finalize() { await this.root.finalize(); } async processLine(line, isFinal) { let boundaries = this.boundaries; if (boundaries.length && line.length > 2 && line[0] === 45 && line[1] === 45) { for (let i = boundaries.length - 1; i >= 0; i--) { let boundary = boundaries[i]; if (line.length < boundary.value.length + 2) { continue; } let boundaryMatches = true; for (let j = 0; j < boundary.value.length; j++) { if (line[j + 2] !== boundary.value[j]) { boundaryMatches = false; break; } } if (!boundaryMatches) { continue; } let boundaryEnd = boundary.value.length + 2; let isTerminator = false; if (line.length >= boundary.value.length + 4 && line[boundary.value.length + 2] === 45 && line[boundary.value.length + 3] === 45) { isTerminator = true; boundaryEnd = boundary.value.length + 4; } let hasValidTrailing = true; for (let j = boundaryEnd; j < line.length; j++) { if (line[j] !== 32 && line[j] !== 9) { hasValidTrailing = false; break; } } if (!hasValidTrailing) { continue; } if (isTerminator) { await boundary.node.finalize(); this.currentNode = boundary.node.parentNode || this.root; } else { await boundary.node.finalizeChildNodes(); this.currentNode = new import_mime_node.default({ postalMime: this, parentNode: boundary.node, parentMultipartType: boundary.node.contentType.multipart, ...this.mimeOptions }); } if (isFinal) { return this.finalize(); } return; } } this.currentNode.feed(line); if (isFinal) { return this.finalize(); } } readLine() { let startPos = this.readPos; let endPos = this.readPos; while (this.readPos < this.av.length) { const c = this.av[this.readPos++]; if (c !== 13 && c !== 10) { endPos = this.readPos; } if (c === 10) { return { bytes: new Uint8Array(this.buf, startPos, endPos - startPos), done: this.readPos >= this.av.length }; } } return { bytes: new Uint8Array(this.buf, startPos, endPos - startPos), done: this.readPos >= this.av.length }; } async processNodeTree() { let textContent = {}; let textTypes = /* @__PURE__ */ new Set(); let textMap = this.textMap = /* @__PURE__ */ new Map(); let forceRfc822Attachments = this.forceRfc822Attachments(); let walk = async (node, alternative, related) => { var _a, _b, _c, _d, _e; alternative = alternative || false; related = related || false; if (!node.contentType.multipart) { if (this.isInlineMessageRfc822(node) && !forceRfc822Attachments) { const subParser = new PostalMime(); node.subMessage = await subParser.parse(node.content); if (!textMap.has(node)) { textMap.set(node, {}); } let textEntry = textMap.get(node); if (node.subMessage.text || !node.subMessage.html) { textEntry.plain = textEntry.plain || []; textEntry.plain.push({ type: "subMessage", value: node.subMessage }); textTypes.add("plain"); } if (node.subMessage.html) { textEntry.html = textEntry.html || []; textEntry.html.push({ type: "subMessage", value: node.subMessage }); textTypes.add("html"); } if (subParser.textMap) { subParser.textMap.forEach((subTextEntry, subTextNode) => { textMap.set(subTextNode, subTextEntry); }); } for (let attachment of node.subMessage.attachments || []) { this.attachments.push(attachment); } } else if (this.isInlineTextNode(node)) { let textType = node.contentType.parsed.value.substr(node.contentType.parsed.value.indexOf("/") + 1); let selectorNode = alternative || node; if (!textMap.has(selectorNode)) { textMap.set(selectorNode, {}); } let textEntry = textMap.get(selectorNode); textEntry[textType] = textEntry[textType] || []; textEntry[textType].push({ type: "text", value: node.getTextContent() }); textTypes.add(textType); } else if (node.content) { const filename = ((_c = (_b = (_a = node.contentDisposition) == null ? void 0 : _a.parsed) == null ? void 0 : _b.params) == null ? void 0 : _c.filename) || node.contentType.parsed.params.name || null; const attachment = { filename: filename ? (0, import_decode_strings.decodeWords)(filename) : null, mimeType: node.contentType.parsed.value, disposition: ((_e = (_d = node.contentDisposition) == null ? void 0 : _d.parsed) == null ? void 0 : _e.value) || null }; if (related && node.contentId) { attachment.related = true; } if (node.contentDescription) { attachment.description = node.contentDescription; } if (node.contentId) { attachment.contentId = node.contentId; } switch (node.contentType.parsed.value) { // Special handling for calendar events case "text/calendar": case "application/ics": { if (node.contentType.parsed.params.method) { attachment.method = node.contentType.parsed.params.method.toString().toUpperCase().trim(); } const decodedText = node.getTextContent().replace(/\r?\n/g, "\n").replace(/\n*$/, "\n"); attachment.content = import_decode_strings.textEncoder.encode(decodedText); break; } // Regular attachments default: attachment.content = node.content; } this.attachments.push(attachment); } } else if (node.contentType.multipart === "alternative") { alternative = node; } else if (node.contentType.multipart === "related") { related = node; } for (let childNode of node.childNodes) { await walk(childNode, alternative, related); } }; await walk(this.root, false, false); textMap.forEach((mapEntry) => { textTypes.forEach((textType) => { if (!textContent[textType]) { textContent[textType] = []; } if (mapEntry[textType]) { mapEntry[textType].forEach((textEntry) => { switch (textEntry.type) { case "text": textContent[textType].push(textEntry.value); break; case "subMessage": { switch (textType) { case "html": textContent[textType].push((0, import_text_format.formatHtmlHeader)(textEntry.value)); break; case "plain": textContent[textType].push((0, import_text_format.formatTextHeader)(textEntry.value)); break; } } break; } }); } else { let alternativeType; switch (textType) { case "html": alternativeType = "plain"; break; case "plain": alternativeType = "html"; break; } (mapEntry[alternativeType] || []).forEach((textEntry) => { switch (textEntry.type) { case "text": switch (textType) { case "html": textContent[textType].push((0, import_text_format.textToHtml)(textEntry.value)); break; case "plain": textContent[textType].push((0, import_text_format.htmlToText)(textEntry.value)); break; } break; case "subMessage": { switch (textType) { case "html": textContent[textType].push((0, import_text_format.formatHtmlHeader)(textEntry.value)); break; case "plain": textContent[textType].push((0, import_text_format.formatTextHeader)(textEntry.value)); break; } } break; } }); } }); }); Object.keys(textContent).forEach((textType) => { textContent[textType] = textContent[textType].join("\n"); }); this.textContent = textContent; } isInlineTextNode(node) { var _a, _b, _c; if (((_b = (_a = node.contentDisposition) == null ? void 0 : _a.parsed) == null ? void 0 : _b.value) === "attachment") { return false; } switch ((_c = node.contentType.parsed) == null ? void 0 : _c.value) { case "text/html": case "text/plain": return true; case "text/calendar": case "text/csv": default: return false; } } isInlineMessageRfc822(node) { var _a, _b, _c; if (((_a = node.contentType.parsed) == null ? void 0 : _a.value) !== "message/rfc822") { return false; } let disposition = ((_c = (_b = node.contentDisposition) == null ? void 0 : _b.parsed) == null ? void 0 : _c.value) || (this.options.rfc822Attachments ? "attachment" : "inline"); return disposition === "inline"; } // Check if this is a specially crafted report email where message/rfc822 content should not be inlined forceRfc822Attachments() { if (this.options.forceRfc822Attachments) { return true; } let forceRfc822Attachments = false; let walk = (node) => { if (!node.contentType.multipart) { if (node.contentType.parsed && ["message/delivery-status", "message/feedback-report"].includes(node.contentType.parsed.value)) { forceRfc822Attachments = true; } } for (let childNode of node.childNodes) { walk(childNode); } }; walk(this.root); return forceRfc822Attachments; } async resolveStream(stream) { let chunkLen = 0; let chunks = []; const reader = stream.getReader(); while (true) { const { done, value } = await reader.read(); if (done) { break; } chunks.push(value); chunkLen += value.length; } const result = new Uint8Array(chunkLen); let chunkPointer = 0; for (let chunk of chunks) { result.set(chunk, chunkPointer); chunkPointer += chunk.length; } return result; } async parse(buf) { var _a, _b; if (this.started) { throw new Error("Can not reuse parser, create a new PostalMime object"); } this.started = true; if (buf && typeof buf.getReader === "function") { buf = await this.resolveStream(buf); } buf = buf || new ArrayBuffer(0); if (typeof buf === "string") { buf = import_decode_strings.textEncoder.encode(buf); } if (buf instanceof Blob || Object.prototype.toString.call(buf) === "[object Blob]") { buf = await (0, import_decode_strings.blobToArrayBuffer)(buf); } if (buf.buffer instanceof ArrayBuffer) { buf = new Uint8Array(buf).buffer; } this.buf = buf; this.av = new Uint8Array(buf); this.readPos = 0; while (this.readPos < this.av.length) { const line = this.readLine(); await this.processLine(line.bytes, line.done); } await this.processNodeTree(); const message = { headers: this.root.headers.map((entry) => ({ key: entry.key, originalKey: entry.originalKey, value: entry.value })).reverse() }; for (const key of ["from", "sender"]) { const addressHeader = this.root.headers.find((line) => line.key === key); if (addressHeader && addressHeader.value) { const addresses = (0, import_address_parser.default)(addressHeader.value); if (addresses && addresses.length) { message[key] = addresses[0]; } } } for (const key of ["delivered-to", "return-path"]) { const addressHeader = this.root.headers.find((line) => line.key === key); if (addressHeader && addressHeader.value) { const addresses = (0, import_address_parser.default)(addressHeader.value); if (addresses && addresses.length && addresses[0].address) { const camelKey = toCamelCase(key); message[camelKey] = addresses[0].address; } } } for (const key of ["to", "cc", "bcc", "reply-to"]) { const addressHeaders = this.root.headers.filter((line) => line.key === key); let addresses = []; addressHeaders.filter((entry) => entry && entry.value).map((entry) => (0, import_address_parser.default)(entry.value)).forEach((parsed) => addresses = addresses.concat(parsed || [])); if (addresses && addresses.length) { const camelKey = toCamelCase(key); message[camelKey] = addresses; } } for (const key of ["subject", "message-id", "in-reply-to", "references"]) { const header = this.root.headers.find((line) => line.key === key); if (header && header.value) { const camelKey = toCamelCase(key); message[camelKey] = (0, import_decode_strings.decodeWords)(header.value); } } let dateHeader = this.root.headers.find((line) => line.key === "date"); if (dateHeader) { let date = new Date(dateHeader.value); if (date.toString() === "Invalid Date") { date = dateHeader.value; } else { date = date.toISOString(); } message.date = date; } if ((_a = this.textContent) == null ? void 0 : _a.html) { message.html = this.textContent.html; } if ((_b = this.textContent) == null ? void 0 : _b.plain) { message.text = this.textContent.plain; } message.attachments = this.attachments; message.headerLines = (this.root.rawHeaderLines || []).slice().reverse(); switch (this.attachmentEncoding) { case "arraybuffer": break; case "base64": for (let attachment of message.attachments || []) { if (attachment == null ? void 0 : attachment.content) { attachment.content = (0, import_base64_encoder.base64ArrayBuffer)(attachment.content); attachment.encoding = "base64"; } } break; case "utf8": let attachmentDecoder = new TextDecoder("utf8"); for (let attachment of message.attachments || []) { if (attachment == null ? void 0 : attachment.content) { attachment.content = attachmentDecoder.decode(attachment.content); attachment.encoding = "utf8"; } } break; default: throw new Error("Unknown attachment encoding"); } return message; } } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { addressParser, decodeWords }); // Make default export work naturally with require() if (module.exports.default) { var defaultExport = module.exports.default; var namedExports = {}; for (var key in module.exports) { if (key !== 'default' && key !== '__esModule') { namedExports[key] = module.exports[key]; } } module.exports = defaultExport; Object.assign(module.exports, namedExports); // Preserve __esModule and .default for bundler/transpiler interop Object.defineProperty(module.exports, '__esModule', { value: true }); module.exports.default = defaultExport; }