Create error type for Filesystem instead of using io::Error; Fix

creation of user account folder so that the entire directory has
the correct permissions, bot just the Inbox folder
This commit is contained in:
Nathan Fisher 2023-06-23 12:21:59 -04:00
parent f17bda9349
commit c6675cb024
4 changed files with 83 additions and 55 deletions

View file

@ -1,27 +1,21 @@
use std::{
ffi::CString,
io::BufReader,
os::{fd::AsRawFd, unix::prelude::OpenOptionsExt},
};
use crate::prelude::Certificate;
use { use {
super::*, super::*,
crate::{ crate::{
message::Parser as MessageParser, message::Parser as MessageParser,
prelude::{ClientCertificateStore, ParseMailboxError}, prelude::{ClientCertificateStore, Certificate, ParseMailboxError},
}, },
rustls_pemfile::{read_one, Item}, rustls_pemfile::{read_one, Item},
std::{ std::{
ffi::OsString, ffi::OsString,
fs::{self, File}, fs::{self, File},
io::{self, BufWriter, Write}, io::{self, BufReader, BufWriter, Write},
iter, iter,
os::unix::fs::DirBuilderExt, os::{fd::AsRawFd, unix::{fs::DirBuilderExt, prelude::OpenOptionsExt}},
path::{Path, PathBuf}, path::{Path, PathBuf},
}, },
}; };
mod error;
pub use error::Error;
pub trait MultiDomain: MailStore { pub trait MultiDomain: MailStore {
type Error; type Error;
@ -107,7 +101,7 @@ impl From<OsString> for Filesystem {
} }
impl MailStore for Filesystem { impl MailStore for Filesystem {
type Error = io::Error; type Error = Error;
fn users(&self) -> Vec<Mailuser> { fn users(&self) -> Vec<Mailuser> {
let mut users = vec![]; let mut users = vec![];
@ -190,11 +184,9 @@ impl MailStore for Filesystem {
Some(folder) 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) { if self.has_mailuser(user) {
let user: Mailbox = user.parse().map_err(|e: ParseMailboxError| { let user: Mailbox = user.parse()?;
io::Error::new(io::ErrorKind::Other, e.to_string())
})?;
let user: Mailuser = user.into(); let user: Mailuser = user.into();
let mut path = self.path.clone(); let mut path = self.path.clone();
path.push(&user.host.to_string()); path.push(&user.host.to_string());
@ -216,26 +208,23 @@ impl MailStore for Filesystem {
folder: &str, folder: &str,
message: Message, message: Message,
) -> Result<(), Self::Error> { ) -> Result<(), Self::Error> {
let Some((user, host)) = user.rsplit_once('@') else { let mb: Mailbox = user.parse()?;
return Err(io::Error::new(io::ErrorKind::Other, "Invalid username"));
};
let mut path = self.path.clone(); let mut path = self.path.clone();
path.push(host); path.push(&mb.host.to_string());
path.push(user); path.push(&mb.username);
path.push(folder); path.push(folder);
path.push(&message.id); path.push(&message.id);
path.set_extension("gmi"); path.set_extension("gmi");
let mut fd = File::create(path)?; 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> { fn delete_message(&mut self, user: &str, folder: &str, id: &str) -> Result<(), Self::Error> {
let Some((user, host)) = user.rsplit_once('@') else { let mb: Mailbox = user.parse()?;
return Err(io::Error::new(io::ErrorKind::Other, "Invalid username"));
};
let mut path = self.path.clone(); let mut path = self.path.clone();
path.push(host); path.push(&mb.host.to_string());
path.push(user); path.push(&mb.username);
path.push(folder); path.push(folder);
path.push(id); path.push(id);
path.set_extension("gmi"); path.set_extension("gmi");
@ -251,13 +240,11 @@ impl MailStore for Filesystem {
id: &str, id: &str,
folder1: &str, folder1: &str,
folder2: &str, folder2: &str,
) -> Result<(), io::Error> { ) -> Result<(), Self::Error> {
let Some((user, host)) = user.rsplit_once('@') else { let mb: Mailbox = user.parse()?;
return Err(io::Error::new(io::ErrorKind::Other, "Invalid username"));
};
let mut infile = self.path.clone(); let mut infile = self.path.clone();
infile.push(host); infile.push(&mb.host.to_string());
infile.push(user); infile.push(&mb.username);
let mut outfile = infile.clone(); let mut outfile = infile.clone();
infile.push(folder1); infile.push(folder1);
infile.push(id); infile.push(id);
@ -271,33 +258,26 @@ impl MailStore for Filesystem {
} }
fn add_user(&mut self, user: &str) -> Result<(), Self::Error> { fn add_user(&mut self, user: &str) -> Result<(), Self::Error> {
let mb: Mailbox = user let mb: Mailbox = user.parse()?;
.parse()
.map_err(|e: ParseMailboxError| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
let mut path = self.path.clone(); let mut path = self.path.clone();
path.push(&mb.host.to_string()); path.push(&mb.host.to_string());
path.push(&mb.username); path.push(&mb.username);
path.push("Inbox");
fs::create_dir_all(&path)?; fs::create_dir_all(&path)?;
if let Some(ref blurb) = mb.blurb { if let Some(ref blurb) = mb.blurb {
let _last = path.pop();
path.push("blurb"); path.push("blurb");
let fd = File::options() let fd = File::options()
.create(true) .create(true)
.write(true) .write(true)
.truncate(true) .truncate(true)
.mode(0o2770) .mode(0o2770)
.open(path)?; .open(&path)?;
if let Some(pw) = pw::Passwd::getpw() if let Some(pw) = pw::Passwd::getpw()?
.map_err(|e: pw::Error| io::Error::new(io::ErrorKind::Other, e.to_string()))?
{ {
let groups = pw let groups = pw.groups()?;
.groups()
.map_err(|e: pw::Error| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
if let Some(gr) = groups.iter().find(|g| g.name == mb.username) { if let Some(gr) = groups.iter().find(|g| g.name == mb.username) {
unsafe { unsafe {
if libc::fchown(fd.as_raw_fd(), pw.uid, gr.gid) != 0 { 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); let mut writer = BufWriter::new(fd);
writer.write_all(blurb.as_bytes())?; writer.write_all(blurb.as_bytes())?;
} }
let _last = path.pop();
path.push("Inbox");
fs::create_dir_all(&path)?;
Ok(()) Ok(())
} }
fn remove_user(&mut self, user: &str) -> bool { fn remove_user(&mut self, user: &str) -> bool {
let Ok(mu) = user.parse::<Mailbox>() else { let Ok(mb) = user.parse::<Mailbox>() else {
return false; return false;
}; };
let mut path = self.path.clone(); let mut path = self.path.clone();
path.push(&mu.host.to_string()); path.push(&mb.host.to_string());
path.push(&mu.username); path.push(&mb.username);
if path.exists() { if path.exists() {
if fs::remove_dir_all(path).is_ok() { if fs::remove_dir_all(path).is_ok() {
return true; return true;

View file

@ -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<io::Error> for Error {
fn from(value: io::Error) -> Self {
Self::Io(value)
}
}
impl From<ParseMailboxError> for Error {
fn from(value: ParseMailboxError) -> Self {
Self::MailBox(value)
}
}
impl From<pw::Error> for Error {
fn from(value: pw::Error) -> Self {
Self::Permissions(value)
}
}

View file

@ -6,7 +6,8 @@ use std::{collections::HashMap, io, str::FromStr};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
pub mod filesystem; mod filesystem;
pub use filesystem::{Error as FilesystemError, Filesystem, MultiDomain};
pub trait MailStore { pub trait MailStore {
type Error; type Error;
@ -68,12 +69,6 @@ pub struct Folder {
pub messages: HashMap<String, Message>, pub messages: HashMap<String, Message>,
} }
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MultiDomain {
pub domains: HashMap<String, Domain>,
}
impl MailStore for Domain { impl MailStore for Domain {
type Error = io::Error; type Error = io::Error;

View file

@ -5,6 +5,7 @@ pub use super::{
host::{Error as ParseHostError, Host}, host::{Error as ParseHostError, Host},
mailbox::{Error as ParseMailboxError, Mailbox}, mailbox::{Error as ParseMailboxError, Mailbox},
mailuser::Mailuser, mailuser::Mailuser,
mailstore::{Account, Domain, Folder, Filesystem, FilesystemError, MailStore},
message::{Error as ParseMessageError, Link, Message, Recipients}, message::{Error as ParseMessageError, Link, Message, Recipients},
//receiver, //receiver,
request::{Error as ParseRequestError, Request}, request::{Error as ParseRequestError, Request},