From 3a21d049234b4570175f033143c417b3993847d9 Mon Sep 17 00:00:00 2001 From: MTRNord Date: Sat, 29 Jul 2023 02:08:09 +0200 Subject: [PATCH] Prepare db for modsequence RFC9051 --- .../20230728235740_modsequences.down.sql | 2 + .../20230728235740_modsequences.up.sql | 2 + .../src/backend/storage/maildir.rs | 72 +++++++++---------- .../erooster_core/src/backend/storage/mod.rs | 4 +- crates/erooster_imap/src/commands/select.rs | 3 +- 5 files changed, 40 insertions(+), 43 deletions(-) create mode 100644 crates/erooster_core/migrations/20230728235740_modsequences.down.sql create mode 100644 crates/erooster_core/migrations/20230728235740_modsequences.up.sql diff --git a/crates/erooster_core/migrations/20230728235740_modsequences.down.sql b/crates/erooster_core/migrations/20230728235740_modsequences.down.sql new file mode 100644 index 00000000..e86f0a8c --- /dev/null +++ b/crates/erooster_core/migrations/20230728235740_modsequences.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE mails +DROP COLUMN modseq; \ No newline at end of file diff --git a/crates/erooster_core/migrations/20230728235740_modsequences.up.sql b/crates/erooster_core/migrations/20230728235740_modsequences.up.sql new file mode 100644 index 00000000..d19a7220 --- /dev/null +++ b/crates/erooster_core/migrations/20230728235740_modsequences.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE mails +ADD COLUMN modseq BIGSERIAL constraint NOT NULL; \ No newline at end of file diff --git a/crates/erooster_core/src/backend/storage/maildir.rs b/crates/erooster_core/src/backend/storage/maildir.rs index b7d42afa..a4863c98 100644 --- a/crates/erooster_core/src/backend/storage/maildir.rs +++ b/crates/erooster_core/src/backend/storage/maildir.rs @@ -51,10 +51,10 @@ impl MailStorage for MaildirStorage { .collect() .await; maildir.find(id).map(|entry| { - let uid = mail_rows - .iter() - .find(|x| x.maildir_id == id) - .map_or(0, |x| x.id); + let db_item = mail_rows.iter().find(|y: &&DbMails| y.maildir_id == id); + let uid: i32 = db_item.map_or(0, |y| y.id); + let modseq = db_item.map_or(0, |y| y.modseq); + let mail_state = if entry.is_seen() { MailState::Read } else { @@ -62,10 +62,10 @@ impl MailStorage for MaildirStorage { }; MaildirMailEntry { uid: uid.try_into().expect("Invalid UID"), + modseq: modseq.try_into().expect("Invalid UID"), entry, mail_state, sequence_number: None, - date: None, } }) } @@ -150,8 +150,9 @@ impl MailStorage for MaildirStorage { .collect::>() .join(""); let maildir_id = maildir.store_cur_with_flags(data, &maildir_flags)?; - sqlx::query("INSERT INTO mails (maildir_id) VALUES ($1)") + sqlx::query("INSERT INTO mails (maildir_id, modseq) VALUES ($1, $2)") .bind(maildir_id.clone()) + .bind(1i64) .execute(self.db.get_pool()) .await?; Ok(maildir_id) @@ -161,8 +162,9 @@ impl MailStorage for MaildirStorage { async fn store_new(&self, path: &Path, data: &[u8]) -> color_eyre::eyre::Result { let maildir = Maildir::from(path.to_path_buf()); let maildir_id = maildir.store_new(data)?; - sqlx::query("INSERT INTO mails (maildir_id) VALUES ($1)") + sqlx::query("INSERT INTO mails (maildir_id, modseq) VALUES ($1, $2)") .bind(maildir_id.clone()) + .bind(1i64) .execute(self.db.get_pool()) .await?; Ok(maildir_id) @@ -205,16 +207,17 @@ impl MailStorage for MaildirStorage { .filter_map(|x| match x { Ok(x) => { let maildir_id = x.id(); - let uid = mail_rows - .iter() - .find(|y| y.maildir_id == maildir_id) - .map_or(0, |y| y.id); + + let db_item = mail_rows.iter().find(|y| y.maildir_id == maildir_id); + let uid: i32 = db_item.map_or(0, |y| y.id); + let modseq = db_item.map_or(0, |y| y.modseq); + Some(MaildirMailEntry { uid: uid.try_into().expect("Invalid UID"), + modseq: modseq.try_into().expect("Invalid UID"), entry: x, mail_state: MailState::Read, sequence_number: None, - date: None, }) } Err(_) => None, @@ -236,16 +239,17 @@ impl MailStorage for MaildirStorage { .filter_map(|x| match x { Ok(x) => { let maildir_id = x.id(); - let uid = mail_rows - .iter() - .find(|y| y.maildir_id == maildir_id) - .map_or(0, |y| y.id); + + let db_item = mail_rows.iter().find(|y| y.maildir_id == maildir_id); + let uid: i32 = db_item.map_or(0, |y| y.id); + let modseq = db_item.map_or(0, |y| y.modseq); + Some(MaildirMailEntry { uid: uid.try_into().expect("Invalid UID"), + modseq: modseq.try_into().expect("Invalid UID"), entry: x, mail_state: MailState::New, sequence_number: None, - date: None, }) } Err(_) => None, @@ -269,10 +273,9 @@ impl MailStorage for MaildirStorage { .filter_map(|x| match x { Ok(x) => { let maildir_id = x.id(); - let uid = mail_rows - .iter() - .find(|y| y.maildir_id == maildir_id) - .map_or(0, |y| y.id); + let db_item = mail_rows.iter().find(|y| y.maildir_id == maildir_id); + let uid: i32 = db_item.map_or(0, |y| y.id); + let modseq = db_item.map_or(0, |y| y.modseq); let state = if x.is_seen() { MailState::Read } else { @@ -280,10 +283,10 @@ impl MailStorage for MaildirStorage { }; Some(MaildirMailEntry { uid: uid.try_into().expect("Invalid UID"), + modseq: modseq.try_into().expect("Invalid Modseq"), entry: x, mail_state: state, sequence_number: None, - date: None, }) } Err(_) => None, @@ -440,31 +443,20 @@ impl MailStorage for MaildirStorage { #[derive(sqlx::FromRow)] struct DbMails { id: i32, - #[allow(dead_code)] maildir_id: String, + modseq: i64, } /// Wrapper for the mailentries from the Maildir crate pub struct MaildirMailEntry { entry: maildir::MailEntry, uid: u32, + modseq: u64, /// The sequence number. It is None until used pub sequence_number: Option, - date: Option, mail_state: MailState, } -impl MaildirMailEntry { - /// Loads async data in memory for non mut usage - /// FIXME: This should probably return the error somewhere - pub fn load(&mut self) { - self.date = match self.entry.date() { - Ok(date) => Some(date), - Err(_) => None, - }; - } -} - #[async_trait::async_trait] impl MailEntry for MaildirMailEntry { #[instrument(skip(self))] @@ -472,6 +464,11 @@ impl MailEntry for MaildirMailEntry { self.uid } + #[instrument(skip(self))] + fn modseq(&self) -> u64 { + self.modseq + } + #[instrument(skip(self))] fn mail_state(&self) -> MailState { self.mail_state @@ -502,11 +499,6 @@ impl MailEntry for MaildirMailEntry { self.entry.received().map_err(Into::into) } - #[instrument(skip(self))] - fn date(&self) -> Option { - self.date - } - #[instrument(skip(self))] fn flags(&self) -> &str { self.entry.flags() diff --git a/crates/erooster_core/src/backend/storage/mod.rs b/crates/erooster_core/src/backend/storage/mod.rs index ba974a26..60924a91 100644 --- a/crates/erooster_core/src/backend/storage/mod.rs +++ b/crates/erooster_core/src/backend/storage/mod.rs @@ -36,6 +36,8 @@ pub enum MailState { pub trait MailEntry { /// The uid of the mail entry fn uid(&self) -> u32; + /// The metadata modification sequence of the mail entry + fn modseq(&self) -> u64; /// The state (new or read) of the mail entry fn mail_state(&self) -> MailState; /// The sequence number of the mail entry @@ -48,8 +50,6 @@ pub trait MailEntry { fn headers(&mut self) -> color_eyre::eyre::Result>; /// The received time of the email fn received(&mut self) -> color_eyre::eyre::Result; - /// The date of the email - fn date(&self) -> Option; /// The flags of the email fn flags(&self) -> &str; /// Whether the email is a draft diff --git a/crates/erooster_imap/src/commands/select.rs b/crates/erooster_imap/src/commands/select.rs index c10855f2..59111c1b 100644 --- a/crates/erooster_imap/src/commands/select.rs +++ b/crates/erooster_imap/src/commands/select.rs @@ -76,6 +76,7 @@ where { let count = storage.count_cur(&mailbox_path) + storage.count_new(&mailbox_path); lines.feed(format!("* {count} EXISTS")).await?; + // TODO: Also send UNSEEN // FIXME: This is fundamentaly invalid and instead should refer to the timestamp a mailbox was created let current_time = SystemTime::now(); let unix_timestamp = current_time.duration_since(UNIX_EPOCH)?; @@ -101,7 +102,7 @@ where "* OK [PERMANENTFLAGS (\\Deleted \\Seen \\*)] Limited", )) .await?; - // TODO generate proper list command + // TODO: generate proper list command lines.feed(format!("* LIST () \".\" \"{folder}\"")).await?; let sub_folders = storage.list_subdirs(&mailbox_path)?; for sub_folder in sub_folders {