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 {
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<OsString> for Filesystem {
}
impl MailStore for Filesystem {
type Error = io::Error;
type Error = Error;
fn users(&self) -> Vec<Mailuser> {
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::<Mailbox>() else {
let Ok(mb) = user.parse::<Mailbox>() 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;

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")]
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<String, Message>,
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct MultiDomain {
pub domains: HashMap<String, Domain>,
}
impl MailStore for Domain {
type Error = io::Error;

View file

@ -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},