const { getAddresseId } = require("../sql/mail"); const { DEBUG } = require("../utils/debug"); const { simpleParser } = require("mailparser"); const moment = require("moment"); const fs = require("fs"); const { registerMessage, registerMailbox_message, saveHeader_fields, saveAddress_fields, registerBodypart, saveBodypart, saveSource, } = require("../sql/saveMessage"); const { createRoom, registerMessageInRoom, createThread, registerMessageInThread, isRoomGroup, findSpacesFromMessage, hasSameMembersAsParent, } = require("../sql/saveMessageApp"); const { getFieldId, findRoomByOwner } = require("../sql/mail"); function saveMessage(attrs, mailboxId, imap) { 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; registerMessage(ts, rfc822size, messageID) .then((messageId) => { const isSeen = 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, isSeen, deleted ); 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 saveSource(messageId, buffer); // parse data simpleParser(buffer, async (err, parsed) => { saveFromParsedData(parsed, messageId); }); }); }); }); f.once("error", function (err) { console.log("Fetch error: " + err); }); f.once("end", function () { console.log("Done fetching all messages!"); }); }) .catch((err) => { DEBUG.log("Unable to register message: " + err); }); } function saveFromParsedData(parsed, messageId) { fs.writeFileSync("./test.txt", JSON.stringify(parsed)); Object.keys(parsed).forEach((key) => { if (["from", "to", "cc", "bcc", "reply-to"].includes(key)) { // save address field getFieldId(key).then((fieldId) => { parsed[key].value.forEach((addr, nb) => { getAddresseId(addr.address, addr.name).then((addressId) => { saveAddress_fields(messageId, fieldId, addressId, nb); }); }); }); } else if (["subject", "inReplyTo"].includes(key)) { // todo : "references" getFieldId(key).then((fieldId) => { saveHeader_fields( messageId, fieldId, undefined, undefined, parsed[key] ); }); } else if (["html", "text", "textAsHtml"].includes(key)) { const hash = "0"; const size = "0"; // saveBodypart(size, hash, parsed[key], "").then((bodypartId) => { // getFieldId(key).then((fieldId) => { // saveHeader_fields( // messageId, // fieldId, // bodypartId, // undefined, // todo ? // undefined // ); // }); // }); } else if (key == "attachments") { // todo } else if ( ["date", "messageId", "headers", "headerLines"].includes(key) ) { // messageId and date are already saved // other field are not improted and can be retrieved in source return; } else { DEBUG.log("doesn't know key: " + key); return; } }); // todo when transfered } function haveSameReceivers() { // take cc and to } /** * take object address and join mailbox and host to return mailbox@host */ function createAddress(elt) { return `${elt.mailbox}@${elt.host}`; } function registerMessageInApp(envelope, messageId, isSeen) { getAddresseId(createAddress(envelope.sender[0])).then((ownerId) => { if (envelope.inReplyTo) { registerReplyMessage(envelope, messageId, isSeen); } else { findRoomByOwner(ownerId).then((res) => { if (res.length == 0) { createRoom(envelope.subject, ownerId).then((roomId) => { registerMessageInRoom(messageId, roomId, isSeen); }); } else { registerMessageInRoom(messageId, res[0].room_id, isSeen); } }); } }); } function registerReplyMessage(envelope, messageId, isSeen) { const messageID = envelope.messageId; findSpacesFromMessage(messageId).then((spaces) => { // todo sub thread will not be in index 0 so look in all indexes if (spaces.length == 0) { // no space, so is a transfer // todo test if members of transferred message are included } else if (spaces[0].thread_id) { registerMessageInThread(messageId, spaces[0].thread_id, isSeen); // todo // if (hasSameMembersAsParent(messageID, envelope.inReplyTo)) { // // register new message in thread // // possibly convert to room only if parent is channel // } else { // // todo create sub thread // } } else if (spaces[0].room_id) { // message in room and not thread isRoomGroup(spaces[0].room_id).then((isGroup) => { if (isGroup) { if (hasSameMembersAsParent(messageID, envelope.inReplyTo)) { registerMessageInRoom( messageId, spaces[0].room_id, isSeen ); } else { // group and not the same member as the reply // some recipient has been removed create a thread const isDm = 0; // todo createThread( space[0].room_id, envelope.subject, isSeen, isDm ).then((threadId) => { registerMessageInThread( messageId, threadId, isSeen ); }); } } else { // reply from channel // todo // if (messageInRoom == 1) { // was new channel transform to group // // register new message in group // } else if (sender == owner) { // correction from the original sender // // leave in the same channel // } else { // user response to announcement // // create new thread // } } }); } }); // `SELECT app_room_messages.room, app_room_messages.thread FROM app_room_messages INNER JOIN messages WHERE messages.messageID = '${envelope.inReplyTo}' AND app_room_messages.message = messages.id`; } function keyNormalizer(key) { // todo return key; } module.exports = { saveMessage, };