mail/back/imap/storeMessage.js
2023-03-13 19:12:57 +01:00

268 lines
9.3 KiB
JavaScript

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,
};