From 2afbede1543ef371390f77d84968be31879fd2d8 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Sat, 27 May 2023 23:38:41 -0400 Subject: [PATCH] Add `Account`, `Domain`, `MultiDomain` structs and `MailStore` trait --- src/fingerprint/mod.rs | 16 +++++++ src/lib.rs | 25 ++++++----- src/mailstore/mod.rs | 100 +++++++++++++++++++++++++++++++++++++++++ src/message/mod.rs | 1 + src/message/parser.rs | 0 5 files changed, 131 insertions(+), 11 deletions(-) create mode 100644 src/mailstore/mod.rs create mode 100644 src/message/parser.rs diff --git a/src/fingerprint/mod.rs b/src/fingerprint/mod.rs index 14bd46f..25926dd 100644 --- a/src/fingerprint/mod.rs +++ b/src/fingerprint/mod.rs @@ -52,3 +52,19 @@ impl GetFingerprint for Certificate { }) } } + +#[cfg(test)] +mod tests { + use super::*; + + const CERT: &[u8] = include_bytes!("../../test/certificates/gemini.example.org/cert.der"); + const FP: &'static str = + include_str!("../../test/certificates/gemini.example.org/fingerprint.txt"); + + #[test] + fn get_fingerprint() { + let cert = Certificate(CERT.into()); + let fp = cert.fingerprint().unwrap(); + assert_eq!(fp.fingerprint, FP); + } +} diff --git a/src/lib.rs b/src/lib.rs index 80da445..d7ba3c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,16 @@ #![warn(clippy::all, clippy::pedantic)] -mod certificate; -mod fingerprint; -mod host; -mod mailbox; -mod mailuser; -mod message; +pub mod certificate; +pub mod fingerprint; +pub mod host; +pub mod mailbox; +pub mod mailstore; +pub mod mailuser; +pub mod message; pub mod prelude; -mod receiver; -mod request; -mod response; -mod sender; -mod status; +pub mod receiver; +pub mod request; +pub mod response; +pub mod sender; +pub mod status; + +pub use sender::{Error as SenderError, Sender}; diff --git a/src/mailstore/mod.rs b/src/mailstore/mod.rs new file mode 100644 index 0000000..b7e32bd --- /dev/null +++ b/src/mailstore/mod.rs @@ -0,0 +1,100 @@ +use crate::prelude::{Host, Mailbox, Mailuser, Message, ParseMailboxError}; +use std::{ + collections::{BTreeMap, HashMap}, + str::FromStr, +}; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +pub trait MailStore { + /// Retreives a list of all valid mailusers on this server + fn users(&self) -> Vec; + /// Checks whether this server services that domain + fn serves_domain(&self, domain: &str) -> bool; + /// Checks whether this server has the given mailuser + fn has_mailuser(&self, mailuser: &str) -> bool; + /// Retreives all of the messages for this user + fn get_user_messages(&self, user: &str) -> Option>; + /// Checks whether this server has a message with the given title for this user and if so + /// returns the message + fn get_message(&self, user: &str, title: &str) -> Option; + /// Adds a new mailuser to the given domain + fn add_user(&mut self, user: &str) -> Result<(), ParseMailboxError>; + /// Removes the given user if the account exists + fn remove_user(&mut self, user: &str) -> bool; +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Domain { + pub host: Host, + pub users: HashMap, +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Account { + pub user: Mailbox, + pub messages: BTreeMap, +} + +#[derive(Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct MultiDomain { + pub domains: HashMap, +} + +impl MailStore for Domain { + fn users(&self) -> Vec { + self.users + .values() + .map(|act| act.user.clone().into()) + .collect() + } + + fn serves_domain(&self, domain: &str) -> bool { + self.host.to_string().as_str() == domain + } + + fn has_mailuser(&self, mailuser: &str) -> bool { + self.users() + .iter() + .find(|x| x.username == mailuser) + .is_some() + } + + fn get_user_messages(&self, user: &str) -> Option> { + self.users.get(user).cloned().map(|u| u.messages) + } + + fn get_message(&self, user: &str, title: &str) -> Option { + self.users + .get(user) + .map(|u| { + u.messages + .values() + .find(|m| m.title.as_ref().map(|x| x.as_str()) == Some(title)) + }) + .flatten() + .cloned() + } + + fn add_user(&mut self, user: &str) -> Result<(), ParseMailboxError> { + if self.users.contains_key(user) { + return Ok(()); + } + let mbox = Mailbox::from_str(&format!("{user}@{}", self.host.to_string()))?; + let messages = BTreeMap::new(); + let acct = Account { + user: mbox, + messages, + }; + self.users.insert(user.to_string(), acct); + Ok(()) + } + + fn remove_user(&mut self, user: &str) -> bool { + self.users.remove(user).is_some() + } +} diff --git a/src/message/mod.rs b/src/message/mod.rs index 7cc5c36..205d0b6 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -59,6 +59,7 @@ pub struct Message { pub senders: Vec, pub recipients: Vec, pub timstamp: Option, + pub title: Option, pub body: String, } diff --git a/src/message/parser.rs b/src/message/parser.rs new file mode 100644 index 0000000..e69de29