189 lines
5.3 KiB
Rust
189 lines
5.3 KiB
Rust
use super::*;
|
|
use std::{
|
|
fs::{self, File},
|
|
io::{self, Write},
|
|
os::unix::fs::DirBuilderExt,
|
|
path::PathBuf,
|
|
process::id,
|
|
};
|
|
|
|
pub trait MultiDomain: MailStore {
|
|
type Error;
|
|
fn domains(&self) -> Result<Vec<String>, <Self as filesystem::MultiDomain>::Error>;
|
|
fn add_domain(&mut self, domain: &str) -> Result<(), <Self as filesystem::MultiDomain>::Error>;
|
|
fn remove_domain(
|
|
&mut self,
|
|
domain: &str,
|
|
) -> Result<(), <Self as filesystem::MultiDomain>::Error>;
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
|
pub struct Filesystem {
|
|
pub path: PathBuf,
|
|
}
|
|
|
|
impl MailStore for Filesystem {
|
|
type Error = io::Error;
|
|
|
|
fn users(&self) -> Vec<Mailuser> {
|
|
todo!()
|
|
}
|
|
|
|
fn serves_domain(&self, domain: &str) -> bool {
|
|
let mut path = self.path.clone();
|
|
path.push(domain);
|
|
path.exists()
|
|
}
|
|
|
|
fn has_mailuser(&self, mailuser: &str) -> bool {
|
|
if let Some((user, host)) = mailuser.rsplit_once('@') {
|
|
let mut path = self.path.clone();
|
|
if host.contains('.') && !host.contains(|c: char| c.is_whitespace()) {
|
|
path.push(host);
|
|
path.push(user);
|
|
return path.exists();
|
|
}
|
|
}
|
|
false
|
|
}
|
|
|
|
fn get_folder(&self, user: &str, folder: &str) -> Option<Folder> {
|
|
let Some((user, host)) = user.rsplit_once('@') else {
|
|
return None;
|
|
};
|
|
let mut path = self.path.clone();
|
|
path.push(host);
|
|
path.push(user);
|
|
path.push(folder);
|
|
let Ok(dir) = fs::read_dir(path) else {
|
|
return None;
|
|
};
|
|
let mut folder = Folder {
|
|
name: folder.to_string(),
|
|
messages: HashMap::new(),
|
|
};
|
|
dir.filter(|x| x.is_ok()).map(|x| x.unwrap()).for_each(|e| {
|
|
if let Ok(contents) = fs::read_to_string(e.path()) {
|
|
if let Ok(message) = contents.parse::<Message>() {
|
|
folder.messages.insert(message.id.clone(), message);
|
|
}
|
|
}
|
|
});
|
|
Some(folder)
|
|
}
|
|
|
|
fn get_message(&self, user: &str, folder: &str, title: &str) -> Option<Message> {
|
|
self.get_folder(user, folder).and_then(|f| {
|
|
f.messages
|
|
.values()
|
|
.find(|m| m.title.as_ref().map(|x| x.as_str()) == Some(title))
|
|
.cloned()
|
|
})
|
|
}
|
|
|
|
fn add_message(
|
|
&mut self,
|
|
user: &str,
|
|
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 mut path = self.path.clone();
|
|
path.push(host);
|
|
path.push(user);
|
|
path.push(folder);
|
|
path.push(&message.id);
|
|
let mut fd = File::create(path)?;
|
|
write!(fd, "{message}")
|
|
}
|
|
|
|
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 mut path = self.path.clone();
|
|
path.push(host);
|
|
path.push(user);
|
|
path.push(folder);
|
|
path.push(id);
|
|
if path.exists() {
|
|
fs::remove_file(&path)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn move_message(
|
|
&mut self,
|
|
user: &str,
|
|
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"));
|
|
};
|
|
let mut infile = self.path.clone();
|
|
infile.push(host);
|
|
infile.push(user);
|
|
let mut outfile = infile.clone();
|
|
infile.push(folder1);
|
|
infile.push(id);
|
|
outfile.push(folder2);
|
|
outfile.push(id);
|
|
fs::copy(&infile, &outfile)?;
|
|
fs::remove_file(infile)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn add_user(&mut self, user: &str) -> Result<(), Self::Error> {
|
|
todo!()
|
|
}
|
|
|
|
fn remove_user(&mut self, user: &str) -> bool {
|
|
todo!()
|
|
}
|
|
}
|
|
|
|
impl MultiDomain for Filesystem {
|
|
type Error = io::Error;
|
|
|
|
fn domains(&self) -> Result<Vec<String>, io::Error> {
|
|
Ok(self
|
|
.path
|
|
.read_dir()?
|
|
.filter(|x| {
|
|
if let Ok(x) = x {
|
|
if let Ok(t) = x.file_type() {
|
|
return t.is_dir();
|
|
}
|
|
}
|
|
false
|
|
})
|
|
.map(|x| x.unwrap().file_name().to_string_lossy().to_string())
|
|
.collect::<Vec<String>>())
|
|
}
|
|
|
|
fn add_domain(&mut self, domain: &str) -> Result<(), io::Error> {
|
|
let mut path = self.path.clone();
|
|
path.push(domain);
|
|
if !path.exists() {
|
|
fs::DirBuilder::new()
|
|
.recursive(true)
|
|
.mode(700)
|
|
.create(path)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn remove_domain(&mut self, domain: &str) -> Result<(), io::Error> {
|
|
let mut path = self.path.clone();
|
|
path.push(domain);
|
|
if path.exists() {
|
|
fs::remove_dir_all(path)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|