402 lines
12 KiB
Rust
402 lines
12 KiB
Rust
/*
|
|
* meli - mailbox module.
|
|
*
|
|
* Copyright 2017 Manos Pitsidianakis
|
|
*
|
|
* This file is part of meli.
|
|
*
|
|
* meli is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* meli is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*!
|
|
* https://wiki2.dovecot.org/MailboxFormat/demo
|
|
*/
|
|
|
|
use crate::backends::*;
|
|
use crate::conf::AccountSettings;
|
|
use crate::email::*;
|
|
use crate::error::{MeliError, Result};
|
|
use crate::get_path_hash;
|
|
use std::collections::{hash_map::HashMap, BTreeMap};
|
|
use std::path::PathBuf;
|
|
use std::sync::{Arc, Mutex, RwLock};
|
|
|
|
mod demo_corpus;
|
|
|
|
#[derive(Debug, Default)]
|
|
struct DemoMailbox {
|
|
hash: MailboxHash,
|
|
name: String,
|
|
path: PathBuf,
|
|
envs: Vec<EnvelopeHash>,
|
|
children: Vec<MailboxHash>,
|
|
parent: Option<MailboxHash>,
|
|
usage: Arc<RwLock<SpecialUsageMailbox>>,
|
|
is_subscribed: bool,
|
|
permissions: MailboxPermissions,
|
|
pub total: Arc<Mutex<usize>>,
|
|
pub unseen: Arc<Mutex<usize>>,
|
|
}
|
|
|
|
impl BackendMailbox for DemoMailbox {
|
|
fn hash(&self) -> MailboxHash {
|
|
self.hash
|
|
}
|
|
|
|
fn name(&self) -> &str {
|
|
self.name.as_str()
|
|
}
|
|
|
|
fn path(&self) -> &str {
|
|
self.path.to_str().unwrap()
|
|
}
|
|
|
|
fn change_name(&mut self, s: &str) {
|
|
self.name = s.to_string();
|
|
}
|
|
|
|
fn clone(&self) -> Mailbox {
|
|
Box::new(DemoMailbox {
|
|
hash: self.hash,
|
|
name: self.name.clone(),
|
|
path: self.path.clone(),
|
|
envs: self.envs.clone(),
|
|
children: self.children.clone(),
|
|
usage: self.usage.clone(),
|
|
is_subscribed: self.is_subscribed,
|
|
parent: self.parent,
|
|
permissions: self.permissions,
|
|
unseen: self.unseen.clone(),
|
|
total: self.total.clone(),
|
|
})
|
|
}
|
|
|
|
fn children(&self) -> &[MailboxHash] {
|
|
&self.children
|
|
}
|
|
|
|
fn parent(&self) -> Option<MailboxHash> {
|
|
self.parent
|
|
}
|
|
|
|
fn special_usage(&self) -> SpecialUsageMailbox {
|
|
*self.usage.read().unwrap()
|
|
}
|
|
|
|
fn permissions(&self) -> MailboxPermissions {
|
|
self.permissions
|
|
}
|
|
fn is_subscribed(&self) -> bool {
|
|
self.is_subscribed
|
|
}
|
|
fn set_is_subscribed(&mut self, new_val: bool) -> Result<()> {
|
|
self.is_subscribed = new_val;
|
|
Ok(())
|
|
}
|
|
fn set_special_usage(&mut self, new_val: SpecialUsageMailbox) -> Result<()> {
|
|
*self.usage.write()? = new_val;
|
|
Ok(())
|
|
}
|
|
|
|
fn count(&self) -> Result<(usize, usize)> {
|
|
Ok((*self.unseen.lock()?, *self.total.lock()?))
|
|
}
|
|
}
|
|
|
|
/// `BackendOp` implementor for Demo
|
|
#[derive(Debug, Default)]
|
|
pub struct DemoOp {
|
|
hash: EnvelopeHash,
|
|
index: Arc<HashMap<EnvelopeHash, &'static str>>,
|
|
flags: Arc<RwLock<HashMap<EnvelopeHash, (Flag, Vec<u64>)>>>,
|
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
|
}
|
|
|
|
impl DemoOp {
|
|
pub fn new(
|
|
hash: EnvelopeHash,
|
|
flags: Arc<RwLock<HashMap<EnvelopeHash, (Flag, Vec<u64>)>>>,
|
|
index: Arc<HashMap<EnvelopeHash, &'static str>>,
|
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
|
) -> Self {
|
|
DemoOp {
|
|
hash,
|
|
flags,
|
|
index,
|
|
tag_index,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl BackendOp for DemoOp {
|
|
fn as_bytes(&mut self) -> ResultFuture<Vec<u8>> {
|
|
let ret = self.index[&self.hash].as_bytes().to_vec();
|
|
Ok(Box::pin(async { Ok(ret) }))
|
|
}
|
|
|
|
fn fetch_flags(&self) -> ResultFuture<Flag> {
|
|
let ret = self.flags.read().unwrap()[&self.hash].0;
|
|
Ok(Box::pin(async move { Ok(ret) }))
|
|
}
|
|
}
|
|
|
|
/// Demo backend
|
|
#[derive(Debug)]
|
|
pub struct DemoType {
|
|
account_name: String,
|
|
account_hash: AccountHash,
|
|
root_mailbox: String,
|
|
mailboxes: Arc<Mutex<HashMap<MailboxHash, DemoMailbox>>>,
|
|
envelopes: HashMap<EnvelopeHash, Envelope>,
|
|
index: Arc<HashMap<EnvelopeHash, &'static str>>,
|
|
flags: Arc<RwLock<HashMap<EnvelopeHash, (Flag, Vec<u64>)>>>,
|
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
|
event_consumer: BackendEventConsumer,
|
|
}
|
|
|
|
impl MailBackend for DemoType {
|
|
fn capabilities(&self) -> MailBackendCapabilities {
|
|
MailBackendCapabilities {
|
|
is_async: false,
|
|
is_remote: false,
|
|
extensions: None,
|
|
supports_search: false,
|
|
supports_tags: true,
|
|
supports_submission: false,
|
|
}
|
|
}
|
|
|
|
fn is_online(&self) -> ResultFuture<()> {
|
|
Ok(Box::pin(async { Ok(()) }))
|
|
}
|
|
|
|
fn fetch(
|
|
&mut self,
|
|
mailbox_hash: MailboxHash,
|
|
) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
|
|
let mailboxes = self.mailboxes.clone();
|
|
let mut mailbox_lock = mailboxes.lock().unwrap();
|
|
let mailbox_entry = mailbox_lock.entry(mailbox_hash).or_default();
|
|
let mut payload = Vec::with_capacity(mailbox_entry.envs.len());
|
|
for env_hash in mailbox_entry.envs.iter() {
|
|
payload.push(self.envelopes[env_hash].clone());
|
|
}
|
|
|
|
Ok(Box::pin(async_stream::try_stream! {
|
|
yield payload;
|
|
}))
|
|
}
|
|
|
|
fn watch(&self) -> ResultFuture<()> {
|
|
Err(MeliError::new("Unimplemented."))
|
|
}
|
|
|
|
fn refresh(&mut self, _mailbox_hash: MailboxHash) -> ResultFuture<()> {
|
|
Ok(Box::pin(async { Ok(()) }))
|
|
}
|
|
|
|
fn mailboxes(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
|
|
debug!("entering mailboxes");
|
|
let ret = Ok(self
|
|
.mailboxes
|
|
.lock()
|
|
.unwrap()
|
|
.iter()
|
|
.map(|(h, f)| (*h, f.clone() as Mailbox))
|
|
.collect());
|
|
debug!("returning mailboxes");
|
|
Ok(Box::pin(async { ret }))
|
|
}
|
|
|
|
fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
|
Ok(Box::new(DemoOp::new(
|
|
hash,
|
|
self.flags.clone(),
|
|
self.index.clone(),
|
|
self.tag_index.clone(),
|
|
)))
|
|
}
|
|
|
|
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
|
Some(self.tag_index.clone())
|
|
}
|
|
|
|
fn save(
|
|
&self,
|
|
_bytes: Vec<u8>,
|
|
_mailbox_hash: MailboxHash,
|
|
_flags: Option<Flag>,
|
|
) -> ResultFuture<()> {
|
|
Err(MeliError::new("Unimplemented."))
|
|
}
|
|
|
|
fn copy_messages(
|
|
&mut self,
|
|
_env_hashes: EnvelopeHashBatch,
|
|
_source_mailbox_hash: MailboxHash,
|
|
_destination_mailbox_hash: MailboxHash,
|
|
_move_: bool,
|
|
) -> ResultFuture<()> {
|
|
Err(MeliError::new("Unimplemented."))
|
|
}
|
|
|
|
fn set_flags(
|
|
&mut self,
|
|
env_hashes: EnvelopeHashBatch,
|
|
mailbox_hash: MailboxHash,
|
|
flags: SmallVec<[(std::result::Result<Flag, String>, bool); 8]>,
|
|
) -> ResultFuture<()> {
|
|
let mut tag_lck = self.tag_index.write().unwrap();
|
|
let mut flags_lck = self.flags.write().unwrap();
|
|
for env_hash in env_hashes.iter() {
|
|
let mut env_flags = flags_lck.get_mut(&env_hash).unwrap();
|
|
for set in flags.iter() {
|
|
match set {
|
|
(Ok(flag), value) => {
|
|
let mut flags = env_flags.0;
|
|
flags.set(*flag, *value);
|
|
env_flags.0 = flags;
|
|
}
|
|
(Err(tag), value) => {
|
|
let hash = tag_hash!(tag);
|
|
if *value {
|
|
tag_lck.insert(hash, tag.into());
|
|
if !env_flags.1.contains(&hash) {
|
|
env_flags.1.push(hash);
|
|
}
|
|
} else {
|
|
if let Some(pos) = env_flags.1.iter().position(|h| *h == hash) {
|
|
env_flags.1.remove(pos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
(self.event_consumer)(
|
|
self.account_hash,
|
|
BackendEvent::Refresh(RefreshEvent {
|
|
mailbox_hash,
|
|
account_hash: self.account_hash,
|
|
kind: RefreshEventKind::NewFlags(
|
|
env_hash,
|
|
(
|
|
env_flags.0,
|
|
env_flags
|
|
.1
|
|
.iter()
|
|
.map(|tag_hash| tag_lck[tag_hash].to_string())
|
|
.collect::<Vec<String>>(),
|
|
),
|
|
),
|
|
}),
|
|
);
|
|
}
|
|
Ok(Box::pin(async { Ok(()) }))
|
|
}
|
|
|
|
fn as_any(&self) -> &dyn ::core::any::Any {
|
|
self
|
|
}
|
|
|
|
fn as_any_mut(&mut self) -> &mut dyn core::any::Any {
|
|
self
|
|
}
|
|
}
|
|
|
|
impl DemoType {
|
|
pub fn new(
|
|
s: &AccountSettings,
|
|
_is_subscribed: Box<dyn Fn(&str) -> bool>,
|
|
event_consumer: BackendEventConsumer,
|
|
) -> Result<Box<dyn MailBackend>> {
|
|
let account_hash = {
|
|
use std::collections::hash_map::DefaultHasher;
|
|
use std::hash::Hasher;
|
|
let mut hasher = DefaultHasher::new();
|
|
hasher.write(s.name().as_bytes());
|
|
hasher.finish()
|
|
};
|
|
let mut ret = DemoType {
|
|
account_name: s.name().to_string(),
|
|
account_hash,
|
|
root_mailbox: s.root_mailbox.to_string(),
|
|
event_consumer,
|
|
mailboxes: Default::default(),
|
|
envelopes: Default::default(),
|
|
index: Default::default(),
|
|
flags: Default::default(),
|
|
tag_index: Default::default(),
|
|
};
|
|
{
|
|
let mut mbox_lck = ret.mailboxes.lock().unwrap();
|
|
let mut flags_lck = ret.flags.write().unwrap();
|
|
let index_lck = Arc::get_mut(&mut ret.index).unwrap();
|
|
//let tag_lck = ret.tag_index.write().unwrap();
|
|
for (path, name, envelopes) in demo_corpus::DEMO_MAILBOXES {
|
|
let hash = get_path_hash!(name);
|
|
let mut mbox = DemoMailbox {
|
|
hash,
|
|
path: path.into(),
|
|
name: name.to_string(),
|
|
envs: vec![],
|
|
children: Vec::new(),
|
|
parent: None,
|
|
usage: Arc::new(RwLock::new(SpecialUsageMailbox::Normal)),
|
|
is_subscribed: true,
|
|
permissions: MailboxPermissions {
|
|
create_messages: false,
|
|
remove_messages: false,
|
|
set_flags: false,
|
|
create_child: false,
|
|
rename_messages: false,
|
|
delete_messages: false,
|
|
delete_mailbox: false,
|
|
change_permissions: false,
|
|
},
|
|
unseen: Arc::new(Mutex::new(0)),
|
|
total: Arc::new(Mutex::new(0)),
|
|
};
|
|
for bytes in *envelopes {
|
|
let env = Envelope::from_bytes(bytes.as_bytes(), None).unwrap();
|
|
index_lck.insert(env.hash(), bytes);
|
|
flags_lck.insert(env.hash(), (env.flags(), vec![]));
|
|
mbox.envs.push(env.hash());
|
|
ret.envelopes.insert(env.hash(), env);
|
|
}
|
|
mbox_lck.insert(hash, mbox);
|
|
}
|
|
}
|
|
Ok(Box::new(ret))
|
|
}
|
|
|
|
pub fn validate_config(_s: &AccountSettings) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
static _DEMO_MAILBOXES: &'static [(&'static str, &'static str, &'static [&'static str])] = &[
|
|
(
|
|
"INBOX",
|
|
"INBOX",
|
|
&[include_str!(
|
|
"../../../demo/ermis-f/cur/6813190.1075861340968.JavaMail.evans@thyme:2,S"
|
|
)],
|
|
),
|
|
("INBOX/All", "All", &[]),
|
|
("INBOX/Discussion Threads", "Discussion Threads", &[]),
|
|
("INBOX/Sent", "Sent", &[]),
|
|
("INBOX/Notes", "Notes", &[]),
|
|
("INBOX/Trash", "Trash", &[]),
|
|
];
|