142 lines
5.3 KiB
TypeScript
142 lines
5.3 KiB
TypeScript
import { getAddresseId, 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<number> {
|
|
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<any>[] = [];
|
|
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) => {
|
|
getAddresseId(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
|
|
} 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,
|
|
};
|
|
}
|