diff --git a/src/mailstore/filesystem.rs b/src/mailstore/filesystem.rs index e857f23..7545e4a 100644 --- a/src/mailstore/filesystem.rs +++ b/src/mailstore/filesystem.rs @@ -1,27 +1,21 @@ -use std::{ - ffi::CString, - io::BufReader, - os::{fd::AsRawFd, unix::prelude::OpenOptionsExt}, -}; - -use crate::prelude::Certificate; - use { super::*, crate::{ message::Parser as MessageParser, - prelude::{ClientCertificateStore, ParseMailboxError}, + prelude::{ClientCertificateStore, Certificate, ParseMailboxError}, }, rustls_pemfile::{read_one, Item}, std::{ ffi::OsString, fs::{self, File}, - io::{self, BufWriter, Write}, + io::{self, BufReader, BufWriter, Write}, iter, - os::unix::fs::DirBuilderExt, + os::{fd::AsRawFd, unix::{fs::DirBuilderExt, prelude::OpenOptionsExt}}, path::{Path, PathBuf}, }, }; +mod error; +pub use error::Error; pub trait MultiDomain: MailStore { type Error; @@ -107,7 +101,7 @@ impl From for Filesystem { } impl MailStore for Filesystem { - type Error = io::Error; + type Error = Error; fn users(&self) -> Vec { let mut users = vec![]; @@ -190,11 +184,9 @@ impl MailStore for Filesystem { Some(folder) } - fn create_folder(&self, user: &str, folder: &str) -> Result<(), io::Error> { + fn create_folder(&self, user: &str, folder: &str) -> Result<(), Error> { if self.has_mailuser(user) { - let user: Mailbox = user.parse().map_err(|e: ParseMailboxError| { - io::Error::new(io::ErrorKind::Other, e.to_string()) - })?; + let user: Mailbox = user.parse()?; let user: Mailuser = user.into(); let mut path = self.path.clone(); path.push(&user.host.to_string()); @@ -216,26 +208,23 @@ impl MailStore for Filesystem { folder: &str, message: Message, ) -> Result<(), Self::Error> { - let Some((user, host)) = user.rsplit_once('@') else { - return Err(io::Error::new(io::ErrorKind::Other, "Invalid username")); - }; + let mb: Mailbox = user.parse()?; let mut path = self.path.clone(); - path.push(host); - path.push(user); + path.push(&mb.host.to_string()); + path.push(&mb.username); path.push(folder); path.push(&message.id); path.set_extension("gmi"); let mut fd = File::create(path)?; - write!(fd, "{message}") + write!(fd, "{message}")?; + Ok(()) } fn delete_message(&mut self, user: &str, folder: &str, id: &str) -> Result<(), Self::Error> { - let Some((user, host)) = user.rsplit_once('@') else { - return Err(io::Error::new(io::ErrorKind::Other, "Invalid username")); - }; + let mb: Mailbox = user.parse()?; let mut path = self.path.clone(); - path.push(host); - path.push(user); + path.push(&mb.host.to_string()); + path.push(&mb.username); path.push(folder); path.push(id); path.set_extension("gmi"); @@ -251,13 +240,11 @@ impl MailStore for Filesystem { id: &str, folder1: &str, folder2: &str, - ) -> Result<(), io::Error> { - let Some((user, host)) = user.rsplit_once('@') else { - return Err(io::Error::new(io::ErrorKind::Other, "Invalid username")); - }; + ) -> Result<(), Self::Error> { + let mb: Mailbox = user.parse()?; let mut infile = self.path.clone(); - infile.push(host); - infile.push(user); + infile.push(&mb.host.to_string()); + infile.push(&mb.username); let mut outfile = infile.clone(); infile.push(folder1); infile.push(id); @@ -271,33 +258,26 @@ impl MailStore for Filesystem { } fn add_user(&mut self, user: &str) -> Result<(), Self::Error> { - let mb: Mailbox = user - .parse() - .map_err(|e: ParseMailboxError| io::Error::new(io::ErrorKind::Other, e.to_string()))?; + let mb: Mailbox = user.parse()?; let mut path = self.path.clone(); path.push(&mb.host.to_string()); path.push(&mb.username); - path.push("Inbox"); fs::create_dir_all(&path)?; if let Some(ref blurb) = mb.blurb { - let _last = path.pop(); path.push("blurb"); let fd = File::options() .create(true) .write(true) .truncate(true) .mode(0o2770) - .open(path)?; - if let Some(pw) = pw::Passwd::getpw() - .map_err(|e: pw::Error| io::Error::new(io::ErrorKind::Other, e.to_string()))? + .open(&path)?; + if let Some(pw) = pw::Passwd::getpw()? { - let groups = pw - .groups() - .map_err(|e: pw::Error| io::Error::new(io::ErrorKind::Other, e.to_string()))?; + let groups = pw.groups()?; if let Some(gr) = groups.iter().find(|g| g.name == mb.username) { unsafe { if libc::fchown(fd.as_raw_fd(), pw.uid, gr.gid) != 0 { - return Err(io::Error::last_os_error()); + return Err(io::Error::last_os_error().into()); } } } @@ -305,16 +285,19 @@ impl MailStore for Filesystem { let mut writer = BufWriter::new(fd); writer.write_all(blurb.as_bytes())?; } + let _last = path.pop(); + path.push("Inbox"); + fs::create_dir_all(&path)?; Ok(()) } fn remove_user(&mut self, user: &str) -> bool { - let Ok(mu) = user.parse::() else { + let Ok(mb) = user.parse::() else { return false; }; let mut path = self.path.clone(); - path.push(&mu.host.to_string()); - path.push(&mu.username); + path.push(&mb.host.to_string()); + path.push(&mb.username); if path.exists() { if fs::remove_dir_all(path).is_ok() { return true; diff --git a/src/mailstore/filesystem/error.rs b/src/mailstore/filesystem/error.rs new file mode 100644 index 0000000..7d073ab --- /dev/null +++ b/src/mailstore/filesystem/error.rs @@ -0,0 +1,49 @@ +use { + crate::prelude::ParseMailboxError, + std::{fmt, io}, +}; + +#[derive(Debug)] +pub enum Error { + Io(io::Error), + MailBox(ParseMailboxError), + Permissions(pw::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Io(e) => write!(f, "Filesystem error: {e}"), + Self::MailBox(e) => write!(f, "Filesystem error: {e}"), + Self::Permissions(e) => write!(f, "Filesystem error: {e}"), + } + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Self::Io(e) => Some(e), + Self::MailBox(e) => Some(e), + Self::Permissions(e) => Some(e), + } + } +} + +impl From for Error { + fn from(value: io::Error) -> Self { + Self::Io(value) + } +} + +impl From for Error { + fn from(value: ParseMailboxError) -> Self { + Self::MailBox(value) + } +} + +impl From for Error { + fn from(value: pw::Error) -> Self { + Self::Permissions(value) + } +} diff --git a/src/mailstore/mod.rs b/src/mailstore/mod.rs index 7b2b4b4..70f0448 100644 --- a/src/mailstore/mod.rs +++ b/src/mailstore/mod.rs @@ -6,7 +6,8 @@ use std::{collections::HashMap, io, str::FromStr}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -pub mod filesystem; +mod filesystem; +pub use filesystem::{Error as FilesystemError, Filesystem, MultiDomain}; pub trait MailStore { type Error; @@ -68,12 +69,6 @@ pub struct Folder { pub messages: HashMap, } -#[derive(Debug)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct MultiDomain { - pub domains: HashMap, -} - impl MailStore for Domain { type Error = io::Error; diff --git a/src/prelude.rs b/src/prelude.rs index 98c50ad..9031a38 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -5,6 +5,7 @@ pub use super::{ host::{Error as ParseHostError, Host}, mailbox::{Error as ParseMailboxError, Mailbox}, mailuser::Mailuser, + mailstore::{Account, Domain, Folder, Filesystem, FilesystemError, MailStore}, message::{Error as ParseMessageError, Link, Message, Recipients}, //receiver, request::{Error as ParseRequestError, Request},