Added Connection and connection::Builder; Fixed a lot of Clippy lints;
This commit is contained in:
parent
c9eb30f4e7
commit
477de69ca3
13 changed files with 89 additions and 41 deletions
35
src/connection/builder.rs
Normal file
35
src/connection/builder.rs
Normal 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
1
src/connection/error.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
8
src/connection/mod.rs
Normal file
8
src/connection/mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
pub mod builder;
|
||||||
|
pub mod error;
|
||||||
|
pub mod verifier;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Connection {
|
||||||
|
pub inner: rustls::ServerConnection,
|
||||||
|
}
|
7
src/connection/verifier.rs
Normal file
7
src/connection/verifier.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
use crate::prelude::CertificateStore;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Verifier<S: CertificateStore> {
|
||||||
|
pub store: Mutex<S>,
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -33,23 +33,22 @@ 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()?;
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue