import { getAddressId, getFlagId } from "../../db/utils/mail"; import { EmailAddress, ParsedMail, simpleParser } from "mailparser"; import moment from "moment"; import Imap from "imap"; import { registerMessage, registerMailbox_message, saveHeader_fields, saveAddress_fields, registerBodypart, saveBodypart, saveSource, registerFlag, } from "../../db/message/storeMessage-db"; import { getFieldId } from "../../db/utils/mail"; import logger from "../../system/Logger"; import { AttrsWithEnvelope } from "../../interfaces/mail/attrs.interface"; export function saveMessage(attrs: AttrsWithEnvelope, mailboxId: number, imap: Imap): Promise { const envelope = attrs.envelope; const ts = moment(new Date(envelope.date).getTime()).format("YYYY-MM-DD HH:mm:ss"); const rfc822size = attrs.size; const messageID = envelope.messageId; return new Promise((resolve, reject) => { registerMessage(ts, rfc822size, messageID) .then((messageId) => { const isSeen: boolean = attrs.flags.includes("\\Seen"); const deleted: boolean = attrs.flags.includes("\\Deleted"); registerMailbox_message(mailboxId, attrs.uid, messageId, attrs?.modseq || 0, isSeen, deleted); registerFlags(messageId, attrs.flags); // fetch message to save everything const f = imap.fetch(attrs.uid, { bodies: "" }); let buffer = ""; f.on("message", function (msg, seqno) { msg.on("body", function (stream, info) { stream.on("data", function (chunk) { buffer += chunk.toString("utf8"); }); stream.once("end", () => { // save raw data todo // saveSource(messageId, buffer); // parse data simpleParser(buffer, async (err, parsed) => { saveFromParsedData(parsed, messageId) .then(() => { resolve(messageId); }) .catch((err) => { reject(err); }); }); }); }); }); f.once("error", function (err) { logger.warn("Fetch error: " + err); }); f.once("end", function () { // logger.log("Done fetching data of " + messageID); // todo }); }) .catch((err) => { logger.warn("Unable to register message: " + err); reject(err); }); }); } function registerFlags(messageId: number, flags: string[]) { flags.forEach((flag) => { getFlagId(flag).then((flagId) => { registerFlag(messageId, flagId); }).catch((err: Error) => { logger.err(err); }); }); } async function saveFromParsedData(parsed: ParsedMail, messageId: number) { const promises: Promise[] = []; Object.keys(parsed).forEach((key) => { if (["from", "to", "cc", "bcc", "replyTo"].includes(key)) { promises.push( // save address field getFieldId(key).then((fieldId) => { parsed[key].value.forEach((addr: EmailAddress, nb: number) => { getAddressId(addr.address, addr.name).then(async (addressId) => { await saveAddress_fields(messageId, fieldId, addressId, nb); }); }); }), ); } else if (["subject", "inReplyTo", "references"].includes(key)) { // todo : "references" (array) if (key == "references") return; promises.push( getFieldId(key).then(async (fieldId) => { await saveHeader_fields(messageId, fieldId, undefined, undefined, parsed[key]); }), ); } else if (["html", "text", "textAsHtml"].includes(key)) { const hash = "0"; const size = "0"; let partType = "text/plain"; if (key == "html") { partType = "text/html"; } else if (key == "textAsHtml") { partType = "text/TexAsHtml"; } saveBodypart(size, hash, parsed[key], "").then((bodypartId) => { getFieldId(key).then((fieldId) => { saveHeader_fields(messageId, fieldId, bodypartId, partType, undefined); }); }); } else if (key == "attachments") { // todo attachments } else if (["date", "messageId", "headers", "headerLines"].includes(key)) { // messageId and date are already saved // other field are not important and can be retrieved in source return; } else { logger.warn("doesn't know key: " + key); return; } }); return Promise.all(promises); // todo when transfered } if (process.env["NODE_DEV"] == "TEST") { module.exports = { saveFromParsedData, }; }