Merge branch 'odin' of git.hitchhiker-linux.org:jeang3nie/dory into odin

This commit is contained in:
Nathan Fisher 2023-05-28 19:27:37 -04:00
commit 0a30c49d49
6 changed files with 116 additions and 38 deletions

View file

@ -37,11 +37,19 @@ impl MailStore for Filesystem {
false false
} }
fn get_user_messages(&self, user: &str) -> Option<BTreeMap<String, Message>> { fn get_folder(&self, user: &str, folder: &str) -> Option<Folder> {
todo!() todo!()
} }
fn get_message(&self, user: &str, title: &str) -> Option<Message> { fn get_message(&self, user: &str, folder: &str, title: &str) -> Option<Message> {
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!() todo!()
} }

View file

@ -1,8 +1,5 @@
use crate::prelude::{Host, Mailbox, Mailuser, Message, ParseMailboxError}; use crate::prelude::{Host, Mailbox, Mailuser, Message, ParseMailboxError};
use std::{ use std::{collections::HashMap, str::FromStr};
collections::{BTreeMap, HashMap},
str::FromStr,
};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -17,12 +14,16 @@ pub trait MailStore {
/// Checks whether this server has the given mailuser /// Checks whether this server has the given mailuser
fn has_mailuser(&self, mailuser: &str) -> bool; fn has_mailuser(&self, mailuser: &str) -> bool;
/// Retreives all of the messages for this user /// Retreives all of the messages for this user
fn get_user_messages(&self, user: &str) -> Option<BTreeMap<String, Message>>; fn get_folder(&self, user: &str, folder: &str) -> Option<Folder>;
/// Checks whether this server has a message with the given title for this user and if so /// Checks whether this server has a message with the given title for this user and if so
/// returns the message /// returns the message
fn get_message(&self, user: &str, title: &str) -> Option<Message>; fn get_message(&self, user: &str, folder: &str, title: &str) -> Option<Message>;
/// Move a message to a new folder /// Move a message to a new folder
fn move_message(&mut self, user: &str, title: &str, folder: &str); 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 /// Adds a new mailuser to the given domain
fn add_user(&mut self, user: &str) -> Result<(), ParseMailboxError>; fn add_user(&mut self, user: &str) -> Result<(), ParseMailboxError>;
/// Removes the given user if the account exists /// Removes the given user if the account exists
@ -40,7 +41,14 @@ pub struct Domain {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Account { pub struct Account {
pub user: Mailbox, pub user: Mailbox,
pub messages: BTreeMap<String, Message>, pub folders: HashMap<String, Folder>,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Folder {
pub name: String,
pub messages: HashMap<String, Message>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -68,19 +76,23 @@ impl MailStore for Domain {
.is_some() .is_some()
} }
fn get_user_messages(&self, user: &str) -> Option<BTreeMap<String, Message>> { fn get_folder(&self, user: &str, folder: &str) -> Option<Folder> {
self.users.get(user).cloned().map(|u| u.messages)
}
fn get_message(&self, user: &str, title: &str) -> Option<Message> {
self.users self.users
.get(user) .get(user)
.map(|u| { .cloned()
u.messages .and_then(|u| u.folders.get(folder).cloned())
}
fn get_message(&self, user: &str, folder: &str, title: &str) -> Option<Message> {
self.users
.get(user)
.and_then(|u| {
u.folders.get(folder).and_then(|f| {
f.messages
.values() .values()
.find(|m| m.title.as_ref().map(|x| x.as_str()) == Some(title)) .find(|m| m.title.as_ref().map(|x| x.as_str()) == Some(title))
}) })
.flatten() })
.cloned() .cloned()
} }
@ -93,10 +105,10 @@ impl MailStore for Domain {
return Ok(()); return Ok(());
} }
let mbox = Mailbox::from_str(&format!("{user}@{}", self.host.to_string()))?; let mbox = Mailbox::from_str(&format!("{user}@{}", self.host.to_string()))?;
let messages = BTreeMap::new(); let folders = HashMap::new();
let acct = Account { let acct = Account {
user: mbox, user: mbox,
messages, folders,
}; };
self.users.insert(user.to_string(), acct); self.users.insert(user.to_string(), acct);
Ok(()) Ok(())
@ -105,4 +117,12 @@ impl MailStore for Domain {
fn remove_user(&mut self, user: &str) -> bool { fn remove_user(&mut self, user: &str) -> bool {
self.users.remove(user).is_some() 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!()
}
} }

View file

@ -56,6 +56,7 @@ impl fmt::Display for Lines {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Message { pub struct Message {
pub id: String,
pub senders: Vec<Mailbox>, pub senders: Vec<Mailbox>,
pub recipients: Vec<Mailbox>, pub recipients: Vec<Mailbox>,
pub timstamp: Option<String>, pub timstamp: Option<String>,

View file

@ -12,14 +12,14 @@ pub use error::Error;
/// The full request as sent by the `Sender` and received by the `Receiver` /// The full request as sent by the `Sender` and received by the `Receiver`
pub struct Request { pub struct Request {
/// The sender of the message /// The sender of the message
pub sender: Mailuser, pub recipient: Mailuser,
/// The message body /// The message body
pub message: String, pub message: String,
} }
impl fmt::Display for Request { impl fmt::Display for Request {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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 { let Some(user) = user.strip_prefix("misfin://") else {
return Err(Error::Malformed); return Err(Error::Malformed);
}; };
let sender = user.parse()?; let recipient = user.parse()?;
Ok(Request { sender, message }) Ok(Request { recipient, message })
} else { } else {
Err(Error::MissingSeparator) Err(Error::MissingSeparator)
} }
@ -98,7 +98,7 @@ mod tests {
fn req() -> Request { fn req() -> Request {
Request { Request {
sender: Mailuser { recipient: Mailuser {
username: "john".to_string(), username: "john".to_string(),
host: Host { host: Host {
subdomain: Some("misfin".into()), subdomain: Some("misfin".into()),

View file

@ -1,5 +1,5 @@
use { use {
crate::prelude::{ParseRequestError, ParseResponseError}, crate::prelude::{ParseMailboxError, ParseRequestError, ParseResponseError},
rustls::InvalidMessage, rustls::InvalidMessage,
std::{fmt, io}, std::{fmt, io},
}; };
@ -9,10 +9,11 @@ use {
pub enum Error { pub enum Error {
CertificateError(InvalidMessage), CertificateError(InvalidMessage),
DnsError, DnsError,
TlsError(rustls::Error), IoError(io::Error),
MailboxError(ParseMailboxError),
RequestError(ParseRequestError), RequestError(ParseRequestError),
ResponseError(ParseResponseError), ResponseError(ParseResponseError),
IoError(io::Error), TlsError(rustls::Error),
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -20,10 +21,11 @@ impl fmt::Display for Error {
match self { match self {
Self::CertificateError(e) => write!(f, "{e:?}"), Self::CertificateError(e) => write!(f, "{e:?}"),
Self::DnsError => write!(f, "Dns Error"), 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::RequestError(e) => write!(f, "{e}"),
Self::ResponseError(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 { impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self { match self {
Self::IoError(e) => Some(e),
Self::MailboxError(e) => Some(e),
Self::RequestError(e) => Some(e), Self::RequestError(e) => Some(e),
Self::ResponseError(e) => Some(e), Self::ResponseError(e) => Some(e),
Self::TlsError(e) => Some(e), Self::TlsError(e) => Some(e),
Self::IoError(e) => Some(e),
_ => None, _ => None,
} }
} }
@ -52,6 +55,12 @@ impl From<rustls::Error> for Error {
} }
} }
impl From<ParseMailboxError> for Error {
fn from(value: ParseMailboxError) -> Self {
Self::MailboxError(value)
}
}
impl From<ParseRequestError> for Error { impl From<ParseRequestError> for Error {
fn from(value: ParseRequestError) -> Self { fn from(value: ParseRequestError) -> Self {
Self::RequestError(value) Self::RequestError(value)

View file

@ -1,5 +1,5 @@
use { use {
crate::prelude::{CertificateStore, ClientCertificateStore, Request, Response}, crate::prelude::{CertificateStore, ClientCertificateStore, Mailuser, Request, Response},
rustls::{internal::msgs::codec::Codec, ClientConfig, ClientConnection, StreamOwned}, rustls::{internal::msgs::codec::Codec, ClientConfig, ClientConnection, StreamOwned},
std::{ std::{
io::{self, Read, Write}, io::{self, Read, Write},
@ -21,11 +21,13 @@ where
S: CertificateStore, S: CertificateStore,
C: ClientCertificateStore, C: ClientCertificateStore,
{ {
/// The address of the sender
pub sender: Mailuser,
/// The full message text to be sent /// The full message text to be sent
pub request: Request, pub request: Request,
/// A [CertificateStore] for servers known to us /// A [CertificateStore] for servers known to us
pub store: S, 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, pub client_store: C,
} }
@ -34,9 +36,11 @@ where
S: CertificateStore + 'static, S: CertificateStore + 'static,
C: ClientCertificateStore, C: ClientCertificateStore,
{ {
pub fn new(request_str: &str, store: S, client_store: C) -> Result<Self, Error> { pub fn new(sender: &str, request_str: &str, store: S, client_store: C) -> Result<Self, Error> {
let sender = sender.parse()?;
let request: Request = request_str.parse()?; let request: Request = request_str.parse()?;
Ok(Self { Ok(Self {
sender,
request, request,
store, store,
client_store, client_store,
@ -44,7 +48,7 @@ where
} }
fn host_string(&self) -> String { fn host_string(&self) -> String {
self.request.sender.host.to_string() self.request.recipient.host.to_string()
} }
pub fn send(self) -> Result<Response, Error> { pub fn send(self) -> Result<Response, Error> {
@ -53,8 +57,8 @@ where
.as_str() .as_str()
.try_into() .try_into()
.map_err(|_| Error::DnsError)?; .map_err(|_| Error::DnsError)?;
let mut it = self.request.sender.host.to_socket_addrs()?; let mut it = self.request.recipient.host.to_socket_addrs()?;
let client_cert = self.client_store.get_certificate(&self.request.sender); let client_cert = self.client_store.get_certificate(&self.sender);
let verifier = Arc::new(Verifier::new(self.store)); let verifier = Arc::new(Verifier::new(self.store));
let Some(socket_addrs) = it.next() else { let Some(socket_addrs) = it.next() else {
return Err(io::Error::new(io::ErrorKind::Other, "no data retrieved").into()); return Err(io::Error::new(io::ErrorKind::Other, "no data retrieved").into());
@ -83,3 +87,39 @@ where
Ok(res) Ok(res)
} }
} }
#[derive(Debug)]
pub struct MultiSender<S, C>
where
S: CertificateStore,
C: ClientCertificateStore,
{
pub sender: Mailuser,
pub recipients: Vec<Mailuser>,
pub store: S,
pub client_store: C,
}
impl<S, C> MultiSender<S, C>
where
S: CertificateStore + 'static + Clone,
C: ClientCertificateStore + Clone,
{
pub fn new(sender: &str, recipients: &[&str], store: S, client_store: C) -> Result<Self, Error> {
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<Response, Error>)) -> Result<(), Error> {
todo!()
}
}