Added Connection and connection::Builder; Fixed a lot of Clippy lints;

This commit is contained in:
Nathan Fisher 2023-05-29 11:33:36 -04:00
parent c9eb30f4e7
commit 477de69ca3
13 changed files with 89 additions and 41 deletions

35
src/connection/builder.rs Normal file
View file

@ -0,0 +1,35 @@
use super::verifier::Verifier;
use crate::prelude::CertificateStore;
use std::net::TcpStream;
#[derive(Debug)]
pub struct Builder<S: CertificateStore> {
pub stream: Option<TcpStream>,
pub verifier: Option<Verifier<S>>,
}
impl<S: CertificateStore> Default for Builder<S> {
fn default() -> Self {
Self { stream: None, verifier: None }
}
}
impl<S: CertificateStore> Builder<S> {
pub fn new() -> Self {
Self::default()
}
pub fn stream(mut self, stream: TcpStream) -> Self {
self.stream = Some(stream);
self
}
pub fn verifier(mut self, verifier: Verifier<S>) -> Self {
self.verifier = Some(verifier);
self
}
pub fn build(self) -> super::Connection {
todo!()
}
}

1
src/connection/error.rs Normal file
View file

@ -0,0 +1 @@

8
src/connection/mod.rs Normal file
View file

@ -0,0 +1,8 @@
pub mod builder;
pub mod error;
pub mod verifier;
#[derive(Debug)]
pub struct Connection {
pub inner: rustls::ServerConnection,
}

View file

@ -0,0 +1,7 @@
use crate::prelude::CertificateStore;
use std::sync::Mutex;
#[derive(Debug)]
pub struct Verifier<S: CertificateStore> {
pub store: Mutex<S>,
}

View file

@ -31,7 +31,7 @@ impl GetFingerprint for Certificate {
subject.iter_common_name().for_each(|n| { subject.iter_common_name().for_each(|n| {
let mut val = n.attr_value().data; let mut val = n.attr_value().data;
let mut name = String::new(); let mut name = String::new();
if let Ok(_) = val.read_to_string(&mut name) { if val.read_to_string(&mut name).is_ok() {
names.push(name); names.push(name);
} }
}); });

View file

@ -80,7 +80,7 @@ impl ToSocketAddrs for Host {
type Iter = std::vec::IntoIter<SocketAddr>; type Iter = std::vec::IntoIter<SocketAddr>;
fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> { fn to_socket_addrs(&self) -> std::io::Result<Self::Iter> {
format!("{}:1958", self).to_socket_addrs() format!("{self}:1958").to_socket_addrs()
} }
} }

View file

@ -1,5 +1,6 @@
#![warn(clippy::all, clippy::pedantic)] #![warn(clippy::all, clippy::pedantic)]
pub mod certificate; pub mod certificate;
pub mod connection;
pub mod fingerprint; pub mod fingerprint;
pub mod host; pub mod host;
pub mod mailbox; pub mod mailbox;

View file

@ -33,25 +33,24 @@ impl FromStr for Mailbox {
if let Some((username, mut host)) = s.split_once('@') { if let Some((username, mut host)) = s.split_once('@') {
if username.is_empty() { if username.is_empty() {
Err(Error::EmptyUser) Err(Error::EmptyUser)
} else if username.contains(|c: char| c.is_whitespace()) { } else if username.contains(char::is_whitespace) {
Err(Error::IllegalWhitespace) Err(Error::IllegalWhitespace)
} else { } else {
let username = username.to_string(); let username = username.to_string();
if let Some((h, b)) = host.split_once(|c: char| c.is_whitespace()) { if let Some((h, b)) = host.split_once(char::is_whitespace) {
if h.is_empty() { if h.is_empty() {
return Err(Error::EmptyHost); return Err(Error::EmptyHost);
} else if h.contains(|c: char| c.is_whitespace()) { } else if h.contains(char::is_whitespace) {
return Err(Error::IllegalWhitespace); return Err(Error::IllegalWhitespace);
} else { }
host = h; host = h;
if !b.is_empty() { if !b.is_empty() {
if b.contains("\n") { if b.contains('\n') {
return Err(Error::IllegalWhitespace); return Err(Error::IllegalWhitespace);
} }
blurb = Some(b.to_string()); blurb = Some(b.to_string());
} }
} }
}
let host = host.parse()?; let host = host.parse()?;
Ok(Self { Ok(Self {
username, username,

View file

@ -4,7 +4,6 @@ use std::{
io::{self, Write}, io::{self, Write},
os::unix::fs::DirBuilderExt, os::unix::fs::DirBuilderExt,
path::PathBuf, path::PathBuf,
process::id,
}; };
pub trait MultiDomain: MailStore { pub trait MultiDomain: MailStore {
@ -39,7 +38,7 @@ impl MailStore for Filesystem {
fn has_mailuser(&self, mailuser: &str) -> bool { fn has_mailuser(&self, mailuser: &str) -> bool {
if let Some((user, host)) = mailuser.rsplit_once('@') { if let Some((user, host)) = mailuser.rsplit_once('@') {
let mut path = self.path.clone(); let mut path = self.path.clone();
if host.contains('.') && !host.contains(|c: char| c.is_whitespace()) { if host.contains('.') && !host.contains(char::is_whitespace) {
path.push(host); path.push(host);
path.push(user); path.push(user);
return path.exists(); return path.exists();
@ -63,7 +62,7 @@ impl MailStore for Filesystem {
name: folder.to_string(), name: folder.to_string(),
messages: HashMap::new(), messages: HashMap::new(),
}; };
dir.filter(|x| x.is_ok()).map(|x| x.unwrap()).for_each(|e| { dir.filter_map(Result::ok).for_each(|e| {
if let Ok(contents) = fs::read_to_string(e.path()) { if let Ok(contents) = fs::read_to_string(e.path()) {
if let Ok(message) = contents.parse::<Message>() { if let Ok(message) = contents.parse::<Message>() {
folder.messages.insert(message.id.clone(), message); folder.messages.insert(message.id.clone(), message);
@ -77,7 +76,7 @@ impl MailStore for Filesystem {
self.get_folder(user, folder).and_then(|f| { self.get_folder(user, folder).and_then(|f| {
f.messages f.messages
.values() .values()
.find(|m| m.title.as_ref().map(|x| x.as_str()) == Some(title)) .find(|m| m.title.as_ref().map(String::as_str) == Some(title))
.cloned() .cloned()
}) })
} }
@ -172,7 +171,7 @@ impl MultiDomain for Filesystem {
if !path.exists() { if !path.exists() {
fs::DirBuilder::new() fs::DirBuilder::new()
.recursive(true) .recursive(true)
.mode(700) .mode(0o700)
.create(path)?; .create(path)?;
} }
Ok(()) Ok(())

View file

@ -1,6 +1,6 @@
#![allow(unused_variables)] #![allow(unused_variables)]
use crate::prelude::{Host, Mailbox, Mailuser, Message, ParseMailboxError}; use crate::prelude::{Host, Mailbox, Mailuser, Message};
use std::{collections::HashMap, convert::Infallible, io, str::FromStr}; use std::{collections::HashMap, io, str::FromStr};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -88,8 +88,7 @@ impl MailStore for Domain {
fn has_mailuser(&self, mailuser: &str) -> bool { fn has_mailuser(&self, mailuser: &str) -> bool {
self.users() self.users()
.iter() .iter()
.find(|x| x.username == mailuser) .any(|x| x.username == mailuser)
.is_some()
} }
fn get_folder(&self, user: &str, folder: &str) -> Option<Folder> { fn get_folder(&self, user: &str, folder: &str) -> Option<Folder> {
@ -106,7 +105,7 @@ impl MailStore for Domain {
u.folders.get(folder).and_then(|f| { u.folders.get(folder).and_then(|f| {
f.messages f.messages
.values() .values()
.find(|m| m.title.as_ref().map(|x| x.as_str()) == Some(title)) .find(|m| m.title.as_ref().map(String::as_str) == Some(title))
}) })
}) })
.cloned() .cloned()
@ -126,7 +125,7 @@ impl MailStore for Domain {
if self.users.contains_key(user) { if self.users.contains_key(user) {
return Ok(()); return Ok(());
} }
let mbox = Mailbox::from_str(&format!("{user}@{}", self.host.to_string())) let mbox = Mailbox::from_str(&format!("{user}@{}", self.host))
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{e}")))?; .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("{e}")))?;
let folders = HashMap::new(); let folders = HashMap::new();
let acct = Account { let acct = Account {

View file

@ -68,12 +68,12 @@ impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if !self.senders.is_empty() { if !self.senders.is_empty() {
write!(f, "< ")?; write!(f, "< ")?;
self.senders.iter().try_for_each(|s| write!(f, "{s}\n"))?; self.senders.iter().try_for_each(|s| writeln!(f, "{s}"))?;
} }
if !self.recipients.is_empty() { if !self.recipients.is_empty() {
write!(f, ": ")?; write!(f, ": ")?;
self.recipients.iter().try_for_each(|r| write!(f, " {r}"))?; self.recipients.iter().try_for_each(|r| write!(f, " {r}"))?;
write!(f, "\n")?; writeln!(f)?;
} }
write!(f, "{}\r\n", self.body) write!(f, "{}\r\n", self.body)
} }

View file

@ -49,8 +49,8 @@ impl Request {
pub fn recipients(&self) -> Vec<Mailuser> { pub fn recipients(&self) -> Vec<Mailuser> {
let mut recipients = vec![]; let mut recipients = vec![];
self.message.lines().for_each(|l| { self.message.lines().for_each(|l| {
if l.starts_with(':') { if let Some(s) = l.strip_prefix(':') {
l[1..].trim().split_whitespace().for_each(|u| { s.split_whitespace().for_each(|u| {
if let Ok(user) = u.parse() { if let Ok(user) = u.parse() {
recipients.push(user); recipients.push(user);
} }
@ -63,8 +63,8 @@ impl Request {
pub fn senders(&self) -> Vec<Mailbox> { pub fn senders(&self) -> Vec<Mailbox> {
let mut senders = vec![]; let mut senders = vec![];
self.message.lines().for_each(|l| { self.message.lines().for_each(|l| {
if l.starts_with('<') { if let Some(s) = l.strip_prefix('<') {
if let Ok(mbox) = l[1..].trim().parse() { if let Ok(mbox) = s.trim().parse() {
senders.push(mbox); senders.push(mbox);
} }
} }
@ -73,13 +73,12 @@ impl Request {
} }
pub fn timestamp(&self) -> Option<String> { pub fn timestamp(&self) -> Option<String> {
let mut ts = None; for l in self.message.lines() {
self.message.lines().for_each(|l| { if let Some(s) = l.strip_prefix('@') {
if l.starts_with('@') { return Some(s.trim().to_string());
ts = Some(l[1..].trim().to_string());
} }
}); }
ts None
} }
} }

View file

@ -9,15 +9,15 @@ use {
#[derive(Debug)] #[derive(Debug)]
/// A verifier is used to verify certificates sent by the receiving server /// A verifier is used to verify certificates sent by the receiving server
/// during the tls handshake. For convenience the [CertificateStore] trait /// during the tls handshake. For convenience the [`CertificateStore`] trait
/// is implemented for [std::collections::HashMap<String, String>] and /// is implemented for [`std::collections::HashMap<String, String>`] and
/// [std::collections::BTreeMap<String, String>], so a Verifier can easily /// [`std::collections::BTreeMap<String, String>`], so a Verifier can easily
/// be constructed from those types. /// be constructed from those types.
/// ///
/// This type is passed to [rustls::client::DangerousClientConfig::set_certificate_verifier] /// This type is passed to [`rustls::client::DangerousClientConfig::set_certificate_verifier`]
/// in order to allow Tofu certificate validation. /// in order to allow Tofu certificate validation.
/// # Examples /// # Examples
/// Create a Verifier from a HashMap /// Create a Verifier from a `HashMap`
/// ``` /// ```
/// use std::collections::HashMap; /// use std::collections::HashMap;
/// use dory::prelude::{CertificateStore, Verifier}; /// use dory::prelude::{CertificateStore, Verifier};
@ -70,7 +70,7 @@ impl<S: CertificateStore> ServerCertVerifier for Verifier<S> {
.borrow_mut() .borrow_mut()
.insert_certificate(&name, &fp.fingerprint); .insert_certificate(&name, &fp.fingerprint);
} }
return Ok(ServerCertVerified::assertion()); Ok(ServerCertVerified::assertion())
} }
} }
} }