mbox: add support for multiple mbox mailboxes in config

Concerns #9
memfd
Manos Pitsidianakis 2020-06-20 14:49:02 +03:00
parent 674073899d
commit 1db2c16f95
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 98 additions and 42 deletions

View File

@ -257,6 +257,17 @@ Valid values are:
.\" default value
.Pq Em auto
.El
to set multiple mailboxes, you have to explicitly state the mailboxes you want in the
.Ic mailboxes
field and set the
.Ar path
property to each of them.
Example:
.Bd -literal
[accounts.mbox]
format = "mbox"
mailboxes."Python mailing list" = { path = "~/.mail/python.mbox", subscribe = true, autoload = true }
.Ed
.Sh mailboxes
.Bl -tag -width 36n
.It Ic alias Ar String

View File

@ -84,6 +84,7 @@ struct MboxMailbox {
hash: MailboxHash,
name: String,
path: PathBuf,
fs_path: PathBuf,
content: Vec<u8>,
children: Vec<MailboxHash>,
parent: Option<MailboxHash>,
@ -92,6 +93,7 @@ struct MboxMailbox {
permissions: MailboxPermissions,
pub total: Arc<Mutex<usize>>,
pub unseen: Arc<Mutex<usize>>,
index: Arc<Mutex<HashMap<EnvelopeHash, (Offset, Length)>>>,
}
impl BackendMailbox for MboxMailbox {
@ -117,6 +119,7 @@ impl BackendMailbox for MboxMailbox {
hash: self.hash,
name: self.name.clone(),
path: self.path.clone(),
fs_path: self.fs_path.clone(),
content: self.content.clone(),
children: self.children.clone(),
usage: self.usage.clone(),
@ -125,6 +128,7 @@ impl BackendMailbox for MboxMailbox {
permissions: self.permissions,
unseen: self.unseen.clone(),
total: self.total.clone(),
index: self.index.clone(),
})
}
@ -659,7 +663,7 @@ pub fn mbox_parse(
pub struct MboxType {
account_name: String,
path: PathBuf,
index: Arc<Mutex<HashMap<EnvelopeHash, (Offset, Length)>>>,
mailbox_index: Arc<Mutex<HashMap<EnvelopeHash, MailboxHash>>>,
mailboxes: Arc<Mutex<HashMap<MailboxHash, MboxMailbox>>>,
prefer_mbox_type: Option<MboxReader>,
}
@ -673,14 +677,13 @@ impl MailBackend for MboxType {
let mut w = AsyncBuilder::new();
let handle = {
let tx = w.tx();
let index = self.index.clone();
let mailbox_path = mailbox.path().to_string();
let mailbox_hash = mailbox.hash();
let mailbox_index = self.mailbox_index.clone();
let mailboxes = self.mailboxes.clone();
let mailbox_path = mailboxes.lock().unwrap()[&mailbox_hash].fs_path.clone();
let prefer_mbox_type = self.prefer_mbox_type.clone();
let closure = move |_work_context| {
let tx = tx.clone();
let index = index.clone();
let file = match std::fs::OpenOptions::new()
.read(true)
.write(true)
@ -702,9 +705,18 @@ impl MailBackend for MboxType {
return;
};
let mailboxes_lck = mailboxes.lock().unwrap();
let mut mailbox_index_lck = mailbox_index.lock().unwrap();
let index = mailboxes_lck[&mailbox_hash].index.clone();
drop(mailboxes_lck);
let payload = mbox_parse(index, contents.as_slice(), 0, prefer_mbox_type)
.map_err(|e| MeliError::from(e))
.map(|(_, v)| v);
.map(|(_, v)| {
for v in v.iter() {
mailbox_index_lck.insert(v.hash(), mailbox_hash);
}
v
});
{
let mut mailbox_lock = mailboxes.lock().unwrap();
mailbox_lock
@ -731,17 +743,16 @@ impl MailBackend for MboxType {
.map_err(MeliError::new)?;
for f in self.mailboxes.lock().unwrap().values() {
watcher
.watch(&f.path, RecursiveMode::Recursive)
.watch(&f.fs_path, RecursiveMode::Recursive)
.map_err(|e| e.to_string())
.map_err(MeliError::new)?;
debug!("watching {:?}", f.path.as_path());
debug!("watching {:?}", f.fs_path.as_path());
}
let account_hash = {
let mut hasher = DefaultHasher::new();
hasher.write(self.account_name.as_bytes());
hasher.finish()
};
let index = self.index.clone();
let mailboxes = self.mailboxes.clone();
let prefer_mbox_type = self.prefer_mbox_type.clone();
let handle = std::thread::Builder::new()
@ -750,7 +761,6 @@ impl MailBackend for MboxType {
// Move `watcher` in the closure's scope so that it doesn't get dropped.
let _watcher = watcher;
let _work_context = work_context;
let index = index;
let mailboxes = mailboxes;
loop {
match rx.recv() {
@ -791,7 +801,7 @@ impl MailBackend for MboxType {
.starts_with(mailbox_lock[&mailbox_hash].content.as_slice())
{
if let Ok((_, envelopes)) = mbox_parse(
index.clone(),
mailbox_lock[&mailbox_hash].index.clone(),
&contents[mailbox_lock[&mailbox_hash].content.len()..],
mailbox_lock[&mailbox_hash].content.len(),
prefer_mbox_type,
@ -822,7 +832,7 @@ impl MailBackend for MboxType {
.lock()
.unwrap()
.values()
.any(|f| &f.path == &pathbuf)
.any(|f| &f.fs_path == &pathbuf)
{
let mailbox_hash = get_path_hash!(&pathbuf);
sender.send(RefreshEvent {
@ -837,7 +847,12 @@ impl MailBackend for MboxType {
}
}
DebouncedEvent::Rename(src, dest) => {
if mailboxes.lock().unwrap().values().any(|f| &f.path == &src) {
if mailboxes
.lock()
.unwrap()
.values()
.any(|f| &f.fs_path == &src)
{
let mailbox_hash = get_path_hash!(&src);
sender.send(RefreshEvent {
account_hash,
@ -879,12 +894,20 @@ impl MailBackend for MboxType {
.map(|(h, f)| (*h, f.clone() as Mailbox))
.collect())
}
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
fn operation(&self, env_hash: EnvelopeHash) -> Box<dyn BackendOp> {
let mailbox_hash = self.mailbox_index.lock().unwrap()[&env_hash];
let mailboxes_lck = self.mailboxes.lock().unwrap();
let (offset, length) = {
let index = self.index.lock().unwrap();
index[&hash]
let index = mailboxes_lck[&mailbox_hash].index.lock().unwrap();
index[&env_hash]
};
Box::new(MboxOp::new(hash, self.path.as_path(), offset, length))
let mailbox_path = mailboxes_lck[&mailbox_hash].fs_path.clone();
Box::new(MboxOp::new(
env_hash,
mailbox_path.as_path(),
offset,
length,
))
}
fn save(&self, _bytes: &[u8], _mailbox_hash: MailboxHash, _flags: Option<Flag>) -> Result<()> {
@ -974,7 +997,8 @@ impl MboxType {
hash,
MboxMailbox {
hash,
path: ret.path.clone(),
path: name.clone().into(),
fs_path: ret.path.clone(),
name,
content: Vec::new(),
children: Vec::new(),
@ -993,39 +1017,60 @@ impl MboxType {
},
unseen: Arc::new(Mutex::new(0)),
total: Arc::new(Mutex::new(0)),
index: Default::default(),
},
);
/*
/* Look for other mailboxes */
let parent_mailbox = Path::new(path).parent().unwrap();
let read_dir = std::fs::read_dir(parent_mailbox);
if read_dir.is_ok() {
for f in read_dir.unwrap() {
if f.is_err() {
continue;
for (k, f) in s.mailboxes.iter() {
if let Some(path_str) = f.extra.get("path") {
let hash = get_path_hash!(path_str);
let pathbuf: PathBuf = path_str.into();
if !pathbuf.exists() || pathbuf.is_dir() {
return Err(MeliError::new(format!(
"mbox mailbox configuration entry \"{}\" path value {} is not a file.",
k, path_str
)));
}
let f = f.unwrap().path();
if f.is_file() && f != path {
let name: String = f
.file_name()
.map(|f| f.to_string_lossy().into())
.unwrap_or(String::new());
let hash = get_path_hash!(f);
ret.mailboxes.lock().unwrap().insert(
let read_only = if let Ok(metadata) = std::fs::metadata(&pathbuf) {
metadata.permissions().readonly()
} else {
true
};
ret.mailboxes.lock().unwrap().insert(
hash,
MboxMailbox {
hash,
MboxMailbox {
hash,
path: f,
name,
content: Vec::new(),
children: Vec::new(),
parent: None,
name: k.to_string(),
fs_path: pathbuf,
path: k.into(),
content: Vec::new(),
children: Vec::new(),
parent: None,
usage: Arc::new(RwLock::new(f.usage.unwrap_or_default())),
is_subscribed: f.subscribe.is_true(),
permissions: MailboxPermissions {
create_messages: !read_only,
remove_messages: !read_only,
set_flags: !read_only,
create_child: !read_only,
rename_messages: !read_only,
delete_messages: !read_only,
delete_mailbox: !read_only,
change_permissions: false,
},
);
}
unseen: Arc::new(Mutex::new(0)),
total: Arc::new(Mutex::new(0)),
index: Default::default(),
},
);
} else {
return Err(MeliError::new(format!(
"mbox mailbox configuration entry \"{}\" should have a \"path\" value set pointing to an mbox file.",
k
)));
}
}
*/
Ok(Box::new(ret))
}