import dataUriToBlob from "data-uri-to-blob"; import libmime from "libmime"; import union from "lodash/union"; import { fixNewLines, parseMIME, getHTML, getPlain, getAttachments, getAttachment, getGlobalHeaderValue } from "../helpers/mailparser"; import { getCertificateChain } from "./signingUtilities"; const SIGNATURE_CONTENT_TYPE = "application/pkcs7-signature"; export const DEFAULT_ATTACHMENT_NAME = "attachment"; const splitParticipants = participantsList => { if (!participantsList) { return []; } const participants = participantsList.map(participants => participants.split(",").map(p => p.trim()) ); return union.apply(null, participants); }; export const parseSMIME = smimeString => { return new Promise(resolve => { setTimeout(async () => { const emailString = fixNewLines(smimeString); const parts = parseMIME(emailString); const html = getHTML(emailString, parts); const plain = getPlain(emailString, parts); const rawAttachments = getAttachments(emailString, parts); const attachments = []; let signatureBase64; for (const rawAttachment of rawAttachments) { const { contentType, base64 } = getAttachment( emailString, rawAttachment ); if (contentType.indexOf(SIGNATURE_CONTENT_TYPE) !== -1) { signatureBase64 = base64; } const dataURI = "data:" + contentType + ";base64, " + base64; const blob = dataUriToBlob(dataURI); const filename = getFilenameFromHeaders(rawAttachment.headers); attachments.push({ blob, filename }); } const certificateChain = getCertificateChain(signatureBase64); const from = splitParticipants(getGlobalHeaderValue("from", parts)); const to = splitParticipants(getGlobalHeaderValue("to", parts)); const cc = splitParticipants(getGlobalHeaderValue("cc", parts)); const message = { from, to, cc, subject: getGlobalHeaderValue("subject", parts).join(" "), html: extractHtmlBodyFromString(html), plain, attachments, certificateChain }; resolve(message); }, 50); }); }; /** * Function extracts file name from content type header * @param headers * @returns {string} ('file.txt') */ export const getFilenameFromHeaders = headers => { const headersToSearch = ["content-type", "content-disposition"]; const filename = headers && Object.keys(headers) .filter(key => headersToSearch.includes(key)) .reduce((result, key) => { const headerValue = libmime.parseHeaderValue(headers[key][0]); return result || headerValue.params.name || headerValue.params.filename; }, ""); return filename || DEFAULT_ATTACHMENT_NAME; }; /** * Function extracts all tags within <body></body> from provided string * and removes whitespaces between tags and HTML comments. * @param string * @returns {*} */ export const extractHtmlBodyFromString = string => { const extractBodyRegex = /<body.*?>([\s\S]+)<\/body>/gm; const bodyMatch = extractBodyRegex.exec(string); let body = string; if (bodyMatch && bodyMatch[1]) { body = bodyMatch[1]; } return body .replace(/>\s+</gm, "><") .replace(/<!--[\s\S]*?-->/gm, "") .trim(); };