diff --git a/src/mailstore/filesystem.rs b/src/mailstore/filesystem.rs index 238c623..ca12ccd 100644 --- a/src/mailstore/filesystem.rs +++ b/src/mailstore/filesystem.rs @@ -37,11 +37,19 @@ impl MailStore for Filesystem { false } - fn get_user_messages(&self, user: &str) -> Option> { + fn get_folder(&self, user: &str, folder: &str) -> Option { todo!() } - fn get_message(&self, user: &str, title: &str) -> Option { + fn get_message(&self, user: &str, folder: &str, title: &str) -> Option { + todo!() + } + + 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/mailstore/mod.rs b/src/mailstore/mod.rs index 8ce18b7..bd41096 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::{BTreeMap, HashMap}, - str::FromStr, -}; +use std::{collections::HashMap, str::FromStr}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -17,12 +14,16 @@ 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; /// Move a message to a new folder fn move_message(&mut self, user: &str, title: &str, folder: &str); + /// 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 @@ -40,7 +41,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)] @@ -68,19 +76,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() } @@ -93,10 +105,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(()) @@ -105,4 +117,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, 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 91c18b1..5ca9caa 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}, @@ -21,11 +21,13 @@ 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 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, } @@ -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,7 +48,7 @@ where } fn host_string(&self) -> String { - self.request.sender.host.to_string() + self.request.recipient.host.to_string() } pub fn send(self) -> Result { @@ -53,8 +57,8 @@ where .as_str() .try_into() .map_err(|_| Error::DnsError)?; - let mut it = self.request.sender.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!() + } +}