From ed47855ef1a6c9d10d48080367ff8b280530e362 Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Wed, 20 Mar 2024 17:31:54 +0100 Subject: Share UniqueIdent between collections --- aero-collections/src/calendar/mod.rs | 6 +- aero-collections/src/calendar/namespace.rs | 47 ++++++++++++++ aero-collections/src/lib.rs | 1 + aero-collections/src/mail/incoming.rs | 2 +- aero-collections/src/mail/mailbox.rs | 2 +- aero-collections/src/mail/mod.rs | 1 - aero-collections/src/mail/namespace.rs | 2 +- aero-collections/src/mail/query.rs | 2 +- aero-collections/src/mail/snapshot.rs | 2 +- aero-collections/src/mail/uidindex.rs | 2 +- aero-collections/src/mail/unique_ident.rs | 101 ----------------------------- aero-collections/src/unique_ident.rs | 101 +++++++++++++++++++++++++++++ aero-collections/src/user.rs | 19 +++++- aero-proto/src/dav.rs | 13 +++- aero-proto/src/imap/index.rs | 2 +- aero-proto/src/imap/mailbox_view.rs | 2 +- 16 files changed, 189 insertions(+), 116 deletions(-) create mode 100644 aero-collections/src/calendar/namespace.rs delete mode 100644 aero-collections/src/mail/unique_ident.rs create mode 100644 aero-collections/src/unique_ident.rs diff --git a/aero-collections/src/calendar/mod.rs b/aero-collections/src/calendar/mod.rs index 19e3340..708e1f1 100644 --- a/aero-collections/src/calendar/mod.rs +++ b/aero-collections/src/calendar/mod.rs @@ -1 +1,5 @@ -//@FIXME Event Index +pub mod namespace; + +pub struct Calendar { + a: u64, +} diff --git a/aero-collections/src/calendar/namespace.rs b/aero-collections/src/calendar/namespace.rs new file mode 100644 index 0000000..cf8a159 --- /dev/null +++ b/aero-collections/src/calendar/namespace.rs @@ -0,0 +1,47 @@ +use anyhow::Result; +use std::collections::{HashMap, BTreeMap}; +use std::sync::{Weak, Arc}; + +use serde::{Deserialize, Serialize}; + +use aero_user::storage; + +use crate::unique_ident::UniqueIdent; +use crate::user::User; +use super::Calendar; + +pub(crate) const CAL_LIST_PK: &str = "calendars"; +pub(crate) const CAL_LIST_SK: &str = "list"; + +pub(crate) struct CalendarNs(std::sync::Mutex>>); +impl CalendarNs { + pub fn new() -> Self { + Self(std::sync::Mutex::new(HashMap::new())) + } + + pub fn list(&self) { + todo!(); + } +} + +#[derive(Serialize, Deserialize)] +pub(crate) struct CalendarList(BTreeMap); + +#[derive(Serialize, Deserialize, Clone, Copy, Debug)] +pub(crate) struct CalendarListEntry { + id_lww: (u64, Option), +} + +impl CalendarList { + pub(crate) async fn load(user: &Arc) -> Result<(Self, Option)> { + todo!(); + } + + pub(crate) async fn save(user: &Arc, ct: Option) -> Result<()> { + todo!(); + } + + pub(crate) fn new() -> Self { + Self(BTreeMap::new()) + } +} diff --git a/aero-collections/src/lib.rs b/aero-collections/src/lib.rs index adcfc93..269cd13 100644 --- a/aero-collections/src/lib.rs +++ b/aero-collections/src/lib.rs @@ -1,3 +1,4 @@ +pub mod unique_ident; pub mod user; pub mod mail; pub mod calendar; diff --git a/aero-collections/src/mail/incoming.rs b/aero-collections/src/mail/incoming.rs index 8220461..cd2f8fd 100644 --- a/aero-collections/src/mail/incoming.rs +++ b/aero-collections/src/mail/incoming.rs @@ -15,7 +15,7 @@ use aero_bayou::timestamp::now_msec; use crate::mail::mailbox::Mailbox; use crate::mail::uidindex::ImapUidvalidity; -use crate::mail::unique_ident::*; +use crate::unique_ident::*; use crate::user::User; use crate::mail::IMF; diff --git a/aero-collections/src/mail/mailbox.rs b/aero-collections/src/mail/mailbox.rs index a767678..25aacf5 100644 --- a/aero-collections/src/mail/mailbox.rs +++ b/aero-collections/src/mail/mailbox.rs @@ -9,7 +9,7 @@ use aero_bayou::Bayou; use aero_bayou::timestamp::now_msec; use crate::mail::uidindex::*; -use crate::mail::unique_ident::*; +use crate::unique_ident::*; use crate::mail::IMF; pub struct Mailbox { diff --git a/aero-collections/src/mail/mod.rs b/aero-collections/src/mail/mod.rs index 85361f3..ca9b08b 100644 --- a/aero-collections/src/mail/mod.rs +++ b/aero-collections/src/mail/mod.rs @@ -3,7 +3,6 @@ pub mod mailbox; pub mod query; pub mod snapshot; pub mod uidindex; -pub mod unique_ident; pub mod namespace; // Internet Message Format diff --git a/aero-collections/src/mail/namespace.rs b/aero-collections/src/mail/namespace.rs index 452ac68..b1f6a70 100644 --- a/aero-collections/src/mail/namespace.rs +++ b/aero-collections/src/mail/namespace.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; use aero_bayou::timestamp::now_msec; use crate::mail::uidindex::ImapUidvalidity; -use crate::mail::unique_ident::{gen_ident, UniqueIdent}; +use crate::unique_ident::{gen_ident, UniqueIdent}; pub const MAILBOX_HIERARCHY_DELIMITER: char = '.'; diff --git a/aero-collections/src/mail/query.rs b/aero-collections/src/mail/query.rs index 3e6fe99..7faba41 100644 --- a/aero-collections/src/mail/query.rs +++ b/aero-collections/src/mail/query.rs @@ -1,6 +1,6 @@ use super::mailbox::MailMeta; use super::snapshot::FrozenMailbox; -use super::unique_ident::UniqueIdent; +use crate::unique_ident::UniqueIdent; use anyhow::Result; use futures::future::FutureExt; use futures::stream::{BoxStream, Stream, StreamExt}; diff --git a/aero-collections/src/mail/snapshot.rs b/aero-collections/src/mail/snapshot.rs index ed756b5..9503d4d 100644 --- a/aero-collections/src/mail/snapshot.rs +++ b/aero-collections/src/mail/snapshot.rs @@ -2,10 +2,10 @@ use std::sync::Arc; use anyhow::Result; +use crate::unique_ident::UniqueIdent; use super::mailbox::Mailbox; use super::query::{Query, QueryScope}; use super::uidindex::UidIndex; -use super::unique_ident::UniqueIdent; /// A Frozen Mailbox has a snapshot of the current mailbox /// state that is desynchronized with the real mailbox state. diff --git a/aero-collections/src/mail/uidindex.rs b/aero-collections/src/mail/uidindex.rs index 637a1ac..ca975a3 100644 --- a/aero-collections/src/mail/uidindex.rs +++ b/aero-collections/src/mail/uidindex.rs @@ -4,7 +4,7 @@ use im::{HashMap, OrdMap, OrdSet}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use aero_bayou::*; -use crate::mail::unique_ident::UniqueIdent; +use crate::unique_ident::UniqueIdent; pub type ModSeq = NonZeroU64; pub type ImapUid = NonZeroU32; diff --git a/aero-collections/src/mail/unique_ident.rs b/aero-collections/src/mail/unique_ident.rs deleted file mode 100644 index 0987a2c..0000000 --- a/aero-collections/src/mail/unique_ident.rs +++ /dev/null @@ -1,101 +0,0 @@ -use std::str::FromStr; -use std::sync::atomic::{AtomicU64, Ordering}; - -use lazy_static::lazy_static; -use rand::prelude::*; -use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; - -use aero_bayou::timestamp::now_msec; - -/// An internal Mail Identifier is composed of two components: -/// - a process identifier, 128 bits, itself composed of: -/// - the timestamp of when the process started, 64 bits -/// - a 64-bit random number -/// - a sequence number, 64 bits -/// They are not part of the protocol but an internal representation -/// required by Aerogramme. -/// Their main property is to be unique without having to rely -/// on synchronization between IMAP processes. -#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] -pub struct UniqueIdent(pub [u8; 24]); - -struct IdentGenerator { - pid: u128, - sn: AtomicU64, -} - -impl IdentGenerator { - fn new() -> Self { - let time = now_msec() as u128; - let rand = thread_rng().gen::() as u128; - Self { - pid: (time << 64) | rand, - sn: AtomicU64::new(0), - } - } - - fn gen(&self) -> UniqueIdent { - let sn = self.sn.fetch_add(1, Ordering::Relaxed); - let mut res = [0u8; 24]; - res[0..16].copy_from_slice(&u128::to_be_bytes(self.pid)); - res[16..24].copy_from_slice(&u64::to_be_bytes(sn)); - UniqueIdent(res) - } -} - -lazy_static! { - static ref GENERATOR: IdentGenerator = IdentGenerator::new(); -} - -pub fn gen_ident() -> UniqueIdent { - GENERATOR.gen() -} - -// -- serde -- - -impl<'de> Deserialize<'de> for UniqueIdent { - fn deserialize(d: D) -> Result - where - D: Deserializer<'de>, - { - let v = String::deserialize(d)?; - UniqueIdent::from_str(&v).map_err(D::Error::custom) - } -} - -impl Serialize for UniqueIdent { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -impl std::fmt::Display for UniqueIdent { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", hex::encode(self.0)) - } -} - -impl std::fmt::Debug for UniqueIdent { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", hex::encode(self.0)) - } -} - -impl FromStr for UniqueIdent { - type Err = &'static str; - - fn from_str(s: &str) -> Result { - let bytes = hex::decode(s).map_err(|_| "invalid hex")?; - - if bytes.len() != 24 { - return Err("bad length"); - } - - let mut tmp = [0u8; 24]; - tmp[..].copy_from_slice(&bytes); - Ok(UniqueIdent(tmp)) - } -} diff --git a/aero-collections/src/unique_ident.rs b/aero-collections/src/unique_ident.rs new file mode 100644 index 0000000..e4eea7a --- /dev/null +++ b/aero-collections/src/unique_ident.rs @@ -0,0 +1,101 @@ +use std::str::FromStr; +use std::sync::atomic::{AtomicU64, Ordering}; + +use lazy_static::lazy_static; +use rand::prelude::*; +use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; + +use aero_bayou::timestamp::now_msec; + +/// An internal Aerogramme identifier is composed of two components: +/// - a process identifier, 128 bits, itself composed of: +/// - the timestamp of when the process started, 64 bits +/// - a 64-bit random number +/// - a sequence number, 64 bits +/// They are not part of the protocol but an internal representation +/// required by Aerogramme. +/// Their main property is to be unique without having to rely +/// on synchronization between (IMAP) processes. +#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] +pub struct UniqueIdent(pub [u8; 24]); + +struct IdentGenerator { + pid: u128, + sn: AtomicU64, +} + +impl IdentGenerator { + fn new() -> Self { + let time = now_msec() as u128; + let rand = thread_rng().gen::() as u128; + Self { + pid: (time << 64) | rand, + sn: AtomicU64::new(0), + } + } + + fn gen(&self) -> UniqueIdent { + let sn = self.sn.fetch_add(1, Ordering::Relaxed); + let mut res = [0u8; 24]; + res[0..16].copy_from_slice(&u128::to_be_bytes(self.pid)); + res[16..24].copy_from_slice(&u64::to_be_bytes(sn)); + UniqueIdent(res) + } +} + +lazy_static! { + static ref GENERATOR: IdentGenerator = IdentGenerator::new(); +} + +pub fn gen_ident() -> UniqueIdent { + GENERATOR.gen() +} + +// -- serde -- + +impl<'de> Deserialize<'de> for UniqueIdent { + fn deserialize(d: D) -> Result + where + D: Deserializer<'de>, + { + let v = String::deserialize(d)?; + UniqueIdent::from_str(&v).map_err(D::Error::custom) + } +} + +impl Serialize for UniqueIdent { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } +} + +impl std::fmt::Display for UniqueIdent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(self.0)) + } +} + +impl std::fmt::Debug for UniqueIdent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", hex::encode(self.0)) + } +} + +impl FromStr for UniqueIdent { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + let bytes = hex::decode(s).map_err(|_| "invalid hex")?; + + if bytes.len() != 24 { + return Err("bad length"); + } + + let mut tmp = [0u8; 24]; + tmp[..].copy_from_slice(&bytes); + Ok(UniqueIdent(tmp)) + } +} diff --git a/aero-collections/src/user.rs b/aero-collections/src/user.rs index 193ce90..0c6b931 100644 --- a/aero-collections/src/user.rs +++ b/aero-collections/src/user.rs @@ -12,19 +12,27 @@ use aero_user::storage; use crate::mail::incoming::incoming_mail_watch_process; use crate::mail::mailbox::Mailbox; use crate::mail::uidindex::ImapUidvalidity; -use crate::mail::unique_ident::UniqueIdent; +use crate::unique_ident::UniqueIdent; use crate::mail::namespace::{MAILBOX_HIERARCHY_DELIMITER, INBOX, DRAFTS, ARCHIVE, SENT, TRASH, MAILBOX_LIST_PK, MAILBOX_LIST_SK,MailboxList,CreatedMailbox}; +use crate::calendar::Calendar; //@FIXME User should be totally rewriten -//to extract the local mailbox list -//to the mail/namespace.rs file (and mailbox list should be reworded as mail namespace) +// to extract the local mailbox list +// to the mail/namespace.rs file (and mailbox list should be reworded as mail namespace) + +//@FIXME User should be run in a LocalSet +// to remove most - if not all - synchronizations types. +// Especially RwLock & co. pub struct User { pub username: String, pub creds: Credentials, pub storage: storage::Store, pub mailboxes: std::sync::Mutex>>, + pub calendars: std::sync::Mutex>>, + // Handle on worker processing received email + // (moving emails from the mailqueue to the user's INBOX) tx_inbox_id: watch::Sender>, } @@ -178,6 +186,7 @@ impl User { storage, tx_inbox_id, mailboxes: std::sync::Mutex::new(HashMap::new()), + calendars: std::sync::Mutex::new(HashMap::new()), }); // Ensure INBOX exists (done inside load_mailbox_list) @@ -204,6 +213,10 @@ impl User { } } + // The idea here is that: + // 1. Opening a mailbox that is not already opened takes a significant amount of time + // 2. We don't want to lock the whole HashMap that contain the mailboxes during this + // operation which is why we droppped the lock above but take it again below. let mb = Arc::new(Mailbox::open(&self.creds, id, min_uidvalidity).await?); let mut cache = self.mailboxes.lock().unwrap(); diff --git a/aero-proto/src/dav.rs b/aero-proto/src/dav.rs index 0ef615a..3420f86 100644 --- a/aero-proto/src/dav.rs +++ b/aero-proto/src/dav.rs @@ -27,6 +27,8 @@ use aero_dav::acltypes as acl; use aero_dav::realization::{All, self as all}; use aero_dav::xml as dxml; +type ArcUser = std::sync::Arc; + pub struct Server { bind_addr: SocketAddr, login_provider: ArcLoginProvider, @@ -359,7 +361,15 @@ async fn propfind(user: std::sync::Arc, req: Request, base_node: async fn report(user: std::sync::Arc, req: Request, node: Box) -> Result>> { let status = hyper::StatusCode::from_u16(207)?; - let report = deserialize::>(req).await?; + let report = match deserialize::>(req).await { + Ok(v) => v, + Err(e) => { + tracing::error!(err=?e, "unable to decode REPORT body"); + return Ok(Response::builder() + .status(400) + .body(text_body("Bad request"))?) + } + }; // Multiget is really like a propfind where Depth: 0|1|Infinity is replaced by an arbitrary // list of URLs @@ -492,7 +502,6 @@ async fn deserialize>(req: Request) -> Result { //--- -type ArcUser = std::sync::Arc; trait DavNode: Send { // ------- specialized logic diff --git a/aero-proto/src/imap/index.rs b/aero-proto/src/imap/index.rs index 3de46be..afe6991 100644 --- a/aero-proto/src/imap/index.rs +++ b/aero-proto/src/imap/index.rs @@ -4,7 +4,7 @@ use anyhow::{anyhow, Result}; use imap_codec::imap_types::sequence::{SeqOrUid, Sequence, SequenceSet}; use aero_collections::mail::uidindex::{ImapUid, ModSeq, UidIndex}; -use aero_collections::mail::unique_ident::UniqueIdent; +use aero_collections::unique_ident::UniqueIdent; pub struct Index<'a> { pub imap_index: Vec>, diff --git a/aero-proto/src/imap/mailbox_view.rs b/aero-proto/src/imap/mailbox_view.rs index 5154359..0ef33d6 100644 --- a/aero-proto/src/imap/mailbox_view.rs +++ b/aero-proto/src/imap/mailbox_view.rs @@ -17,7 +17,7 @@ use aero_collections::mail::mailbox::Mailbox; use aero_collections::mail::query::QueryScope; use aero_collections::mail::snapshot::FrozenMailbox; use aero_collections::mail::uidindex::{ImapUid, ImapUidvalidity, ModSeq}; -use aero_collections::mail::unique_ident::UniqueIdent; +use aero_collections::unique_ident::UniqueIdent; use crate::imap::attributes::AttributesProxy; use crate::imap::flags; -- cgit v1.2.3