diff --git a/back/db/imap/imap-db.ts b/back/db/imap/imap-db.ts index 17ee942..c3c9a06 100644 --- a/back/db/imap/imap-db.ts +++ b/back/db/imap/imap-db.ts @@ -16,26 +16,38 @@ export async function getAllAccounts() { return await execQueryAsync(query, values); } -export async function getAllMailboxes(accountId) { - const query = 'SELECT * FROM mailbox WHERE mailbox.account_id = ?'; +export async function getAllMailboxes(accountId: number) { + const query = "SELECT * FROM mailbox WHERE mailbox.account_id = ?"; const values = [accountId]; - return await execQueryAsync(query, values) + return await execQueryAsync(query, values); } -export async function registerMailbox(accountId, mailboxName) { +export async function registerMailbox(accountId: number, mailboxName: string) { const query = `INSERT INTO mailbox (account_id, mailbox_name) VALUES (?, ?)`; const values = [accountId, mailboxName]; return await execQueryAsyncWithId(query, values); } -export async function getMailbox(mailboxId) { +export async function getMailbox(mailboxId: number) { const query = `SELECT * FROM mailbox WHERE mailbox_id = ?`; const values = [mailboxId]; return await execQueryAsync(query, values); } -export function updateMailbox(mailboxId, uidnext) { +export function updateMailbox(mailboxId: number, uidnext: number) { const query = `UPDATE mailbox SET uidnext = ? WHERE mailbox_id = ?`; const values = [uidnext, mailboxId]; execQuery(query, values); -} \ No newline at end of file +} + +export async function updateMailboxModseq(mailboxId: number, modseq: number) { + const query = `UPDATE mailbox SET nextmodseq = ? WHERE mailbox_id = ?`; + const values = [modseq, mailboxId]; + return await execQueryAsync(query, values); +} + +export async function getMailboxModseq(mailboxId: number): Promise<{ modseq: number }[]> { + const query = `SELECT nextmodseq AS modseq FROM mailbox WHERE mailbox_id = ?`; + const values = [mailboxId]; + return await execQueryAsync(query, values); +} diff --git a/back/mails/imap/Mailbox.ts b/back/mails/imap/Mailbox.ts index 2e42a3a..3f95f53 100644 --- a/back/mails/imap/Mailbox.ts +++ b/back/mails/imap/Mailbox.ts @@ -1,5 +1,5 @@ import Imap, { ImapMessageAttributes, Box } from "imap"; -import { getMailbox, updateMailbox } from "../../db/imap/imap-db"; +import { getMailbox, getMailboxModseq, updateMailbox, updateMailboxModseq } from "../../db/imap/imap-db"; import { Attrs, AttrsWithEnvelope } from "../../interfaces/mail/attrs.interface"; import logger from "../../system/Logger"; import RegisterMessageInApp from "../message/saveMessage"; @@ -33,29 +33,25 @@ export default class Mailbox { async init() { // get mailbox from the database this.box = (await getMailbox(this.id))[0]; - const isReadOnly = false; this.imap.openBox(this.boxName, isReadOnly, (err, box) => { if (err) logger.err(err); - // sync only if has new messages - if (this.box.uidnext < box.uidnext) { - this.sync(this.box.uidnext, box.uidnext); - } else { - logger.log("Already up to date"); - } + // sync messages and flags + this.initSync(box); // wait for new mails this.imap.on("mail", (numNewMsgs: number) => { if (!this.syncing) { // if not syncing restart a sync - this.sync(this.box.uidnext, this.box.uidnext + numNewMsgs); + this.syncMail(this.box.uidnext, this.box.uidnext + numNewMsgs); } else { // else save number of message to sync latter this.msgToSync += numNewMsgs; } }); + // wait for flags update this.imap.on("update", (seqno: number, info: ImapInfo) => { logger.log(`Update message ${info.uid} with ${info.flags}`); const updateMsg = new updateMessage(info.uid, info.flags); @@ -64,7 +60,43 @@ export default class Mailbox { }); } - async sync(savedUid: number, currentUid: number) { + async updateModseq(newModseq: number) { + updateMailboxModseq(this.id, newModseq).then(() => { + this.box.highestmodseq = newModseq; + }); + } + + async initSync(box: Box) { + // sync mail only if has new messages + if (this.box.uidnext < box.uidnext) { + this.syncMail(this.box.uidnext, box.uidnext); + } else { + logger.log("Mail already up to date"); + } + + // sync flags + const lastModseq = (await getMailboxModseq(this.id))[0]?.modseq ?? 0; + if (box.highestmodseq > lastModseq) { + const fetchStream = this.imap.fetch("1:*", { bodies: "", modifiers: { changedsince: lastModseq } }); + fetchStream.on("message", (message) => { + message.once("attributes", (attrs) => { + const updateMsg = new updateMessage(attrs.uid, attrs.flags); + updateMsg.updateFlags(); + }); + }); + fetchStream.once("error", function (err) { + logger.err("Fetch error when syncing flags: " + err); + }); + fetchStream.once("end", function () { + logger.log("Done fetching new flags"); + }); + } else { + logger.log("Flags already up to date") + } + this.updateModseq(box.highestmodseq); + } + + async syncMail(savedUid: number, currentUid: number) { this.syncing = true; const promises: Promise[] = []; const mails: Attrs[] = []; @@ -113,7 +145,7 @@ export default class Mailbox { this.box.uidnext += this.msgToSync; // reset value to allow to detect new incoming message while syncing this.msgToSync = 0; - this.sync(currentUid, this.box.uidnext); + this.syncMail(currentUid, this.box.uidnext); } }); } diff --git a/back/mails/message/updateMessage.ts b/back/mails/message/updateMessage.ts index 5d5c7d3..b6123b3 100644 --- a/back/mails/message/updateMessage.ts +++ b/back/mails/message/updateMessage.ts @@ -12,7 +12,8 @@ export default class updateMessage { } async updateFlags() { - const messageId = (await getMessageIdOnUid(this.uid))[0].message_id; + const messageId = (await getMessageIdOnUid(this.uid))[0]?.message_id; + if (!messageId) return; const currentFlags = await getFlags(this.uid); const flagsToAdd = this.flags.filter((flag) => !currentFlags.find((f) => flag == f.flag_name));