const { getAddresseId } = require("../sql/mail"); const { DEBUG } = require("../utils/debug"); const { simpleParser } = require("mailparser"); const { registerMessage, registerMailbox_message, saveHeader_fields, saveAddress_fields, registerBodypart, saveBodypart } = require("../sql/saveMessage"); const { getFieldId } = require("../sql/mail"); function saveMessage(attrs, mailboxId, imap) { const envelope = attrs.envelope; const timestamp = new Date(envelope.date).getTime(); const rfc822size = attrs.size; registerMessage(timestamp, rfc822size, envelope.messageId).then( (messageId) => { const seen = attrs.flags.includes("Seen") ? 1 : 0; // todo verify const deleted = attrs.flags.includes("Deleted") ? 1 : 0; // todo verify registerMailbox_message( mailboxId, attrs.uid, messageId, attrs.modseq, seen, deleted ); const len = attrs.struct.length; attrs.struct.forEach((part) => { if (len > 1) part = part[0]; // todo should be recursive to take eveyraçpghyrue // todo attachments if (part?.type == "text") { const registerType = `${part.type}/${part.subtype}` const hash = "2"; // todo const text = "1"; // todo saveBodypart(part.size, hash, text, '').then((bodypartId) => { registerBodypart(messageId, registerType, bodypartId, part.size, part.lines); }); } }); // const fetch = imap.fetch(uid, { bodies: ["HEADER", part.partID] }); // fetch.on("message", (msg) => { // msg.on("body", (stream, info) => { // simpleParser(stream, async (err, parsed) => { // console.log(part.partID, parsed?.subject); // }); // }); // msg.once("end", () => { // console.log("Finished fetching message"); // }); // }); // fetch.once("error", (err) => { // console.log(err); // }); // fetch.once("end", () => { // console.log("Done fetching all messages"); // }); // } // }); // todo when transfered //The part column records to which MIME part this header field belongs. It's empty for the main header (the one seen above) and nonempty when a multipart message has headers on each part. const part = ''; // todo ^ const position = 2; // todo // save envelope (header + from, to, subject, date, cc) Object.keys(envelope).forEach(key => { const newKey = keyNormalizer(key); if (isHeader(newKey)) { getFieldId(newKey).then((fieldId) => { saveHeader_fields(messageId, part, position, fieldId, envelope[key]); }); } else { getFieldId(newKey).then((fieldId) => { if (envelope[key]) { envelope[key].forEach((elt, index) => { getAddresseId(`${elt.mailbox}@${elt.host}`).then((addressId) => { saveAddress_fields(messageId, part, position, fieldId, index, addressId); }); }); } }); } }); // save more header // message.headerLines.forEach(elt => { // const newKey = keyNormalizer(elt.key); // getFieldId(newKey).then((fieldId) => { // saveHeader_fields(messageId, part, 2, fieldId, elt[line]); // }); // }); // // todo add date field } ); } function findTextPart(struct) { for (var i = 0, len = struct.length, r; i < len; ++i) { if (Array.isArray(struct[i])) { if (r = findTextPart(struct[i])) return r; } else if (struct[i].type === 'text' && (struct[i].subtype === 'plain' || struct[i].subtype === 'html')) return [struct[i].partID, struct[i].type + '/' + struct[i].subtype]; } } function isHeader(key) { switch (key) { case "date": case "subject": case "messageId": case "inReplyTo": // when transfer or reply to message return true; case "from": case "sender": case "replyTo": case "to": case "cc": case "bcc": return false; default: DEBUG.log("Unknown header key: " + key); return true; } } function keyNormalizer(key) { // todo return key; } module.exports = { saveMessage, };