From 50644d2d689bac0b7a9b58780c70b9e7023202d0 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Sun, 28 May 2023 00:18:15 -0400 Subject: [PATCH 1/3] Some tweaks to `MailStore` trait --- src/mailstore/mod.rs | 55 +++++++++++++++++++++++++++++++------------- src/message/mod.rs | 1 + 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/src/mailstore/mod.rs b/src/mailstore/mod.rs index b7e32bd..acbb3d3 100644 --- a/src/mailstore/mod.rs +++ b/src/mailstore/mod.rs @@ -1,6 +1,6 @@ use crate::prelude::{Host, Mailbox, Mailuser, Message, ParseMailboxError}; use std::{ - collections::{BTreeMap, HashMap}, + collections::HashMap, str::FromStr, }; @@ -15,10 +15,14 @@ pub trait MailStore { /// 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>; + fn get_folder(&self, user: &str, folder: &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; + fn get_message(&self, user: &str, folder: &str, title: &str) -> Option; + /// Deletes a message it it exists + fn delete_message(&mut self, user: &str, folder: &str, key: &str) -> bool; + /// Adds a message + fn add_message(&mut self, user: &str, folder: &str, message: Message); /// 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 @@ -36,7 +40,14 @@ pub struct Domain { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Account { pub user: Mailbox, - pub messages: BTreeMap, + pub folders: HashMap, +} + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct Folder { + pub name: String, + pub messages: HashMap, } #[derive(Debug)] @@ -64,19 +75,23 @@ impl MailStore for Domain { .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 { + fn get_folder(&self, user: &str, folder: &str) -> Option { self.users .get(user) - .map(|u| { - u.messages - .values() - .find(|m| m.title.as_ref().map(|x| x.as_str()) == Some(title)) + .cloned() + .and_then(|u| u.folders.get(folder).cloned()) + } + + fn get_message(&self, user: &str, folder: &str, title: &str) -> Option { + self.users + .get(user) + .and_then(|u| { + u.folders.get(folder).and_then(|f| { + f.messages + .values() + .find(|m| m.title.as_ref().map(|x| x.as_str()) == Some(title)) + }) }) - .flatten() .cloned() } @@ -85,10 +100,10 @@ impl MailStore for Domain { return Ok(()); } let mbox = Mailbox::from_str(&format!("{user}@{}", self.host.to_string()))?; - let messages = BTreeMap::new(); + let folders = HashMap::new(); let acct = Account { user: mbox, - messages, + folders, }; self.users.insert(user.to_string(), acct); Ok(()) @@ -97,4 +112,12 @@ impl MailStore for Domain { fn remove_user(&mut self, user: &str) -> bool { self.users.remove(user).is_some() } + + fn add_message(&mut self, user: &str, folder: &str, message: Message) { + todo!() + } + + fn delete_message(&mut self, user: &str, folder: &str, key: &str) -> bool { + todo!() + } } diff --git a/src/message/mod.rs b/src/message/mod.rs index 205d0b6..f4ded5d 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -56,6 +56,7 @@ impl fmt::Display for Lines { #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct Message { + pub id: String, pub senders: Vec, pub recipients: Vec, pub timstamp: Option, From b7378dba78a97243d67271ec94312e5abf87c22c Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Sun, 28 May 2023 11:02:54 -0400 Subject: [PATCH 2/3] Fix some logic errors in Sender::send, as before it was sending to the sender rather than the recipient --- src/mailstore/mod.rs | 5 +---- src/sender/mod.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/mailstore/mod.rs b/src/mailstore/mod.rs index acbb3d3..5186ad1 100644 --- a/src/mailstore/mod.rs +++ b/src/mailstore/mod.rs @@ -1,8 +1,5 @@ use crate::prelude::{Host, Mailbox, Mailuser, Message, ParseMailboxError}; -use std::{ - collections::HashMap, - str::FromStr, -}; +use std::{collections::HashMap, str::FromStr}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; diff --git a/src/sender/mod.rs b/src/sender/mod.rs index 91c18b1..bb49891 100644 --- a/src/sender/mod.rs +++ b/src/sender/mod.rs @@ -1,5 +1,5 @@ use { - crate::prelude::{CertificateStore, ClientCertificateStore, Request, Response}, + crate::prelude::{CertificateStore, ClientCertificateStore, Mailuser, Request, Response}, rustls::{internal::msgs::codec::Codec, ClientConfig, ClientConnection, StreamOwned}, std::{ io::{self, Read, Write}, @@ -25,7 +25,7 @@ where pub request: Request, /// A [CertificateStore] for servers known to us pub store: S, - /// A [CertificateStore] for mailboxes which exist on this system + /// A [ClientCertificateStore] for mailboxes which exist on this system pub client_store: C, } @@ -47,13 +47,13 @@ where self.request.sender.host.to_string() } - pub fn send(self) -> Result { - let dnsname = self - .host_string() + pub fn send(self, recipient: &Mailuser) -> Result { + let dnsname = recipient + .to_string() .as_str() .try_into() .map_err(|_| Error::DnsError)?; - let mut it = self.request.sender.host.to_socket_addrs()?; + let mut it = recipient.host.to_socket_addrs()?; let client_cert = self.client_store.get_certificate(&self.request.sender); let verifier = Arc::new(Verifier::new(self.store)); let Some(socket_addrs) = it.next() else { From e3b9db45722293803c057eedb476fe3a8e11763e Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Sun, 28 May 2023 12:23:05 -0400 Subject: [PATCH 3/3] Fix some issues with Sender, add Multisender --- src/request/mod.rs | 10 ++++----- src/sender/error.rs | 21 +++++++++++++----- src/sender/mod.rs | 54 +++++++++++++++++++++++++++++++++++++++------ 3 files changed, 67 insertions(+), 18 deletions(-) diff --git a/src/request/mod.rs b/src/request/mod.rs index 60ee5fe..febf1ec 100644 --- a/src/request/mod.rs +++ b/src/request/mod.rs @@ -12,14 +12,14 @@ pub use error::Error; /// The full request as sent by the `Sender` and received by the `Receiver` pub struct Request { /// The sender of the message - pub sender: Mailuser, + pub recipient: Mailuser, /// The message body pub message: String, } impl fmt::Display for Request { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "misfin://{} {}\r\n", self.sender, self.message) + write!(f, "misfin://{} {}\r\n", self.recipient, self.message) } } @@ -37,8 +37,8 @@ impl FromStr for Request { let Some(user) = user.strip_prefix("misfin://") else { return Err(Error::Malformed); }; - let sender = user.parse()?; - Ok(Request { sender, message }) + let recipient = user.parse()?; + Ok(Request { recipient, message }) } else { Err(Error::MissingSeparator) } @@ -98,7 +98,7 @@ mod tests { fn req() -> Request { Request { - sender: Mailuser { + recipient: Mailuser { username: "john".to_string(), host: Host { subdomain: Some("misfin".into()), diff --git a/src/sender/error.rs b/src/sender/error.rs index d618383..bcc55a9 100644 --- a/src/sender/error.rs +++ b/src/sender/error.rs @@ -1,5 +1,5 @@ use { - crate::prelude::{ParseRequestError, ParseResponseError}, + crate::prelude::{ParseMailboxError, ParseRequestError, ParseResponseError}, rustls::InvalidMessage, std::{fmt, io}, }; @@ -9,10 +9,11 @@ use { pub enum Error { CertificateError(InvalidMessage), DnsError, - TlsError(rustls::Error), + IoError(io::Error), + MailboxError(ParseMailboxError), RequestError(ParseRequestError), ResponseError(ParseResponseError), - IoError(io::Error), + TlsError(rustls::Error), } impl fmt::Display for Error { @@ -20,10 +21,11 @@ impl fmt::Display for Error { match self { Self::CertificateError(e) => write!(f, "{e:?}"), Self::DnsError => write!(f, "Dns Error"), - Self::TlsError(e) => write!(f, "{e}"), + Self::IoError(e) => write!(f, "{e}"), + Self::MailboxError(e) => write!(f, "{e}"), Self::RequestError(e) => write!(f, "{e}"), Self::ResponseError(e) => write!(f, "{e}"), - Self::IoError(e) => write!(f, "{e}"), + Self::TlsError(e) => write!(f, "{e}"), } } } @@ -31,10 +33,11 @@ impl fmt::Display for Error { impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { + Self::IoError(e) => Some(e), + Self::MailboxError(e) => Some(e), Self::RequestError(e) => Some(e), Self::ResponseError(e) => Some(e), Self::TlsError(e) => Some(e), - Self::IoError(e) => Some(e), _ => None, } } @@ -52,6 +55,12 @@ impl From for Error { } } +impl From for Error { + fn from(value: ParseMailboxError) -> Self { + Self::MailboxError(value) + } +} + impl From for Error { fn from(value: ParseRequestError) -> Self { Self::RequestError(value) diff --git a/src/sender/mod.rs b/src/sender/mod.rs index bb49891..5ca9caa 100644 --- a/src/sender/mod.rs +++ b/src/sender/mod.rs @@ -21,6 +21,8 @@ where S: CertificateStore, C: ClientCertificateStore, { + /// The address of the sender + pub sender: Mailuser, /// The full message text to be sent pub request: Request, /// A [CertificateStore] for servers known to us @@ -34,9 +36,11 @@ where S: CertificateStore + 'static, C: ClientCertificateStore, { - pub fn new(request_str: &str, store: S, client_store: C) -> Result { + pub fn new(sender: &str, request_str: &str, store: S, client_store: C) -> Result { + let sender = sender.parse()?; let request: Request = request_str.parse()?; Ok(Self { + sender, request, store, client_store, @@ -44,17 +48,17 @@ where } fn host_string(&self) -> String { - self.request.sender.host.to_string() + self.request.recipient.host.to_string() } - pub fn send(self, recipient: &Mailuser) -> Result { - let dnsname = recipient - .to_string() + pub fn send(self) -> Result { + let dnsname = self + .host_string() .as_str() .try_into() .map_err(|_| Error::DnsError)?; - let mut it = recipient.host.to_socket_addrs()?; - let client_cert = self.client_store.get_certificate(&self.request.sender); + let mut it = self.request.recipient.host.to_socket_addrs()?; + let client_cert = self.client_store.get_certificate(&self.sender); let verifier = Arc::new(Verifier::new(self.store)); let Some(socket_addrs) = it.next() else { return Err(io::Error::new(io::ErrorKind::Other, "no data retrieved").into()); @@ -83,3 +87,39 @@ where Ok(res) } } + +#[derive(Debug)] +pub struct MultiSender +where + S: CertificateStore, + C: ClientCertificateStore, +{ + pub sender: Mailuser, + pub recipients: Vec, + pub store: S, + pub client_store: C, +} + +impl MultiSender +where + S: CertificateStore + 'static + Clone, + C: ClientCertificateStore + Clone, +{ + pub fn new(sender: &str, recipients: &[&str], store: S, client_store: C) -> Result { + let mut rec = vec![]; + for r in recipients { + let r = r.parse()?; + rec.push(r); + } + Ok(Self { + sender: sender.parse()?, + recipients: rec, + store, + client_store + }) + } + + pub fn send(&self, f: &dyn Fn(Result)) -> Result<(), Error> { + todo!() + } +}