const { getAddresseId } = require("../sql/mail"); const { DEBUG } = require("../utils/debug"); const { simpleParser } = require("mailparser"); const moment = require("moment"); const { registerMessage, registerMailbox_message, saveHeader_fields, saveAddress_fields, registerBodypart, saveBodypart, } = 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 timestamp = moment(new Date(envelope.date).getTime()).format( "YYYY-MM-DD HH:mm:ss" ); const rfc822size = attrs.size; const messageID = envelope.messageId; registerMessage(timestamp, 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 len = attrs.struct.length; attrs.struct.forEach((part) => { if (len > 1) part = part[0]; // todo should be recursive to take eveyraçpghyrue // todo attachments console.log("parr", part) if (part?.type == "text") { console.log(attrs.uid, part.partID) const fetch = imap.fetch(attrs.uid, { bodies: ['', 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"); }); 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 ); }); } }); // 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 ^ // save envelope (header + from, to, subject, date, cc) // Object.keys(envelope).forEach((key, position) => { // 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]) { // return; // } // envelope[key].forEach((elt, index) => { // getAddresseId(createAddress(elt)).then((addressId) => { // saveAddress_fields( // messageId, // part, // position, // fieldId, // index, // addressId // ); // }); // }); // }); // } // }); // todo check for different provider name of inreplyto // registerMessageInApp(envelope, messageId, isSeen); }).catch((err) => { DEBUG.log("Unable to register message: "+err); }); } 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) { console.log(res, ownerId) 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 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, };