2017-09-07 20:00:08 +00:00
|
|
|
/*
|
|
|
|
* meli - backends 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/>.
|
|
|
|
*/
|
2019-09-21 18:23:06 +00:00
|
|
|
#[cfg(feature = "imap_backend")]
|
2018-07-27 18:37:56 +00:00
|
|
|
pub mod imap;
|
2019-09-21 18:23:06 +00:00
|
|
|
#[cfg(feature = "maildir_backend")]
|
2017-09-05 13:41:29 +00:00
|
|
|
pub mod maildir;
|
2019-09-21 18:23:06 +00:00
|
|
|
#[cfg(feature = "mbox_backend")]
|
2018-07-13 15:38:57 +00:00
|
|
|
pub mod mbox;
|
2017-09-05 13:41:29 +00:00
|
|
|
|
2019-09-21 18:23:06 +00:00
|
|
|
#[cfg(feature = "imap_backend")]
|
2019-06-28 16:34:40 +00:00
|
|
|
pub use self::imap::ImapType;
|
2019-04-04 11:21:52 +00:00
|
|
|
use crate::async_workers::*;
|
|
|
|
use crate::conf::AccountSettings;
|
2019-07-18 17:16:51 +00:00
|
|
|
use crate::error::{MeliError, Result};
|
2019-09-21 18:23:06 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "maildir_backend")]
|
2019-04-04 11:21:52 +00:00
|
|
|
use self::maildir::MaildirType;
|
2019-09-21 18:23:06 +00:00
|
|
|
#[cfg(feature = "mbox_backend")]
|
2019-07-11 08:44:27 +00:00
|
|
|
use self::mbox::MboxType;
|
2019-04-04 11:21:52 +00:00
|
|
|
use super::email::{Envelope, EnvelopeHash, Flag};
|
2019-11-06 12:53:12 +00:00
|
|
|
use std::any::Any;
|
2017-09-14 15:08:14 +00:00
|
|
|
use std::fmt;
|
2018-08-11 15:00:21 +00:00
|
|
|
use std::fmt::Debug;
|
2018-09-23 16:55:29 +00:00
|
|
|
use std::ops::Deref;
|
2017-09-14 15:08:14 +00:00
|
|
|
|
2019-04-04 11:21:52 +00:00
|
|
|
use fnv::FnvHashMap;
|
2017-09-30 17:02:22 +00:00
|
|
|
use std;
|
2017-09-30 17:53:14 +00:00
|
|
|
|
2019-09-09 08:54:47 +00:00
|
|
|
pub type BackendCreator =
|
2019-10-24 17:30:17 +00:00
|
|
|
Box<dyn Fn(&AccountSettings, Box<dyn Fn(&str) -> bool + Send + Sync>) -> Box<dyn MailBackend>>;
|
2018-08-11 15:00:21 +00:00
|
|
|
|
2017-09-30 17:53:14 +00:00
|
|
|
/// A hashmap containing all available mail backends.
|
2018-07-10 08:18:11 +00:00
|
|
|
/// An abstraction over any available backends.
|
2017-09-30 17:02:22 +00:00
|
|
|
pub struct Backends {
|
2019-09-09 08:54:47 +00:00
|
|
|
map: FnvHashMap<std::string::String, Box<dyn Fn() -> BackendCreator>>,
|
2017-09-30 17:02:22 +00:00
|
|
|
}
|
|
|
|
|
2018-08-23 12:36:52 +00:00
|
|
|
impl Default for Backends {
|
|
|
|
fn default() -> Self {
|
|
|
|
Backends::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-30 17:02:22 +00:00
|
|
|
impl Backends {
|
|
|
|
pub fn new() -> Self {
|
2017-09-30 17:53:14 +00:00
|
|
|
let mut b = Backends {
|
2018-07-27 18:37:56 +00:00
|
|
|
map: FnvHashMap::with_capacity_and_hasher(1, Default::default()),
|
2017-09-30 17:53:14 +00:00
|
|
|
};
|
2019-09-21 18:23:06 +00:00
|
|
|
#[cfg(feature = "maildir_backend")]
|
|
|
|
{
|
|
|
|
b.register(
|
|
|
|
"maildir".to_string(),
|
|
|
|
Box::new(|| Box::new(|f, i| Box::new(MaildirType::new(f, i)))),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
#[cfg(feature = "mbox_backend")]
|
|
|
|
{
|
|
|
|
b.register(
|
|
|
|
"mbox".to_string(),
|
|
|
|
Box::new(|| Box::new(|f, i| Box::new(MboxType::new(f, i)))),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
#[cfg(feature = "imap_backend")]
|
|
|
|
{
|
|
|
|
b.register(
|
|
|
|
"imap".to_string(),
|
|
|
|
Box::new(|| Box::new(|f, i| Box::new(ImapType::new(f, i)))),
|
|
|
|
);
|
|
|
|
}
|
2017-09-30 17:53:14 +00:00
|
|
|
b
|
2017-09-30 17:02:22 +00:00
|
|
|
}
|
|
|
|
|
2018-08-11 15:00:21 +00:00
|
|
|
pub fn get(&self, key: &str) -> BackendCreator {
|
2017-09-30 17:02:22 +00:00
|
|
|
if !self.map.contains_key(key) {
|
2017-09-30 17:53:14 +00:00
|
|
|
panic!("{} is not a valid mail backend", key);
|
2017-09-30 17:02:22 +00:00
|
|
|
}
|
2017-09-30 17:53:14 +00:00
|
|
|
self.map[key]()
|
2017-09-30 17:02:22 +00:00
|
|
|
}
|
2018-08-13 06:25:48 +00:00
|
|
|
|
2019-09-09 08:54:47 +00:00
|
|
|
pub fn register(&mut self, key: String, backend: Box<dyn Fn() -> BackendCreator>) {
|
2017-09-30 17:02:22 +00:00
|
|
|
if self.map.contains_key(&key) {
|
2017-09-30 17:53:14 +00:00
|
|
|
panic!("{} is an already registered backend", key);
|
2017-09-30 17:02:22 +00:00
|
|
|
}
|
|
|
|
self.map.insert(key, backend);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 13:08:11 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum RefreshEventKind {
|
2018-09-12 12:10:19 +00:00
|
|
|
Update(EnvelopeHash, Box<Envelope>),
|
2018-10-14 16:49:16 +00:00
|
|
|
/// Rename(old_hash, new_hash)
|
|
|
|
Rename(EnvelopeHash, EnvelopeHash),
|
2018-09-12 12:10:19 +00:00
|
|
|
Create(Box<Envelope>),
|
2019-10-24 17:30:17 +00:00
|
|
|
Remove(EnvelopeHash),
|
2018-09-05 13:08:11 +00:00
|
|
|
Rescan,
|
2019-08-14 19:59:46 +00:00
|
|
|
Failure(MeliError),
|
2018-09-05 13:08:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2017-09-28 15:06:35 +00:00
|
|
|
pub struct RefreshEvent {
|
2018-09-05 13:08:11 +00:00
|
|
|
hash: FolderHash,
|
|
|
|
kind: RefreshEventKind,
|
2018-08-24 23:23:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl RefreshEvent {
|
2018-09-05 13:08:11 +00:00
|
|
|
pub fn hash(&self) -> FolderHash {
|
2018-08-24 23:23:40 +00:00
|
|
|
self.hash
|
|
|
|
}
|
2018-09-05 13:08:11 +00:00
|
|
|
pub fn kind(self) -> RefreshEventKind {
|
|
|
|
/* consumes self! */
|
|
|
|
self.kind
|
2018-08-24 23:23:40 +00:00
|
|
|
}
|
2017-09-05 13:41:29 +00:00
|
|
|
}
|
2017-09-14 15:08:14 +00:00
|
|
|
|
2017-09-30 17:02:22 +00:00
|
|
|
/// A `RefreshEventConsumer` is a boxed closure that must be used to consume a `RefreshEvent` and
|
|
|
|
/// send it to a UI provided channel. We need this level of abstraction to provide an interface for
|
|
|
|
/// all users of mailbox refresh events.
|
2019-09-09 08:54:47 +00:00
|
|
|
pub struct RefreshEventConsumer(Box<dyn Fn(RefreshEvent) -> () + Send + Sync>);
|
2017-09-28 15:06:35 +00:00
|
|
|
impl RefreshEventConsumer {
|
2019-09-09 08:54:47 +00:00
|
|
|
pub fn new(b: Box<dyn Fn(RefreshEvent) -> () + Send + Sync>) -> Self {
|
2017-09-28 15:06:35 +00:00
|
|
|
RefreshEventConsumer(b)
|
|
|
|
}
|
2019-03-03 20:11:15 +00:00
|
|
|
pub fn send(&self, r: RefreshEvent) {
|
2017-09-28 15:06:35 +00:00
|
|
|
self.0(r);
|
|
|
|
}
|
|
|
|
}
|
2018-09-07 12:36:42 +00:00
|
|
|
|
2019-09-09 08:54:47 +00:00
|
|
|
pub struct NotifyFn(Box<dyn Fn(FolderHash) -> () + Send + Sync>);
|
2018-09-07 12:36:42 +00:00
|
|
|
|
|
|
|
impl fmt::Debug for NotifyFn {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "NotifyFn Box")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-09 08:54:47 +00:00
|
|
|
impl From<Box<dyn Fn(FolderHash) -> () + Send + Sync>> for NotifyFn {
|
|
|
|
fn from(kind: Box<dyn Fn(FolderHash) -> () + Send + Sync>) -> Self {
|
2018-09-07 12:36:42 +00:00
|
|
|
NotifyFn(kind)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl NotifyFn {
|
2019-09-09 08:54:47 +00:00
|
|
|
pub fn new(b: Box<dyn Fn(FolderHash) -> () + Send + Sync>) -> Self {
|
2018-09-07 12:36:42 +00:00
|
|
|
NotifyFn(b)
|
|
|
|
}
|
2019-03-30 11:41:32 +00:00
|
|
|
pub fn notify(&self, f: FolderHash) {
|
|
|
|
self.0(f);
|
2018-09-07 12:36:42 +00:00
|
|
|
}
|
|
|
|
}
|
2019-08-26 16:44:05 +00:00
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
|
|
|
pub enum FolderOperation {
|
|
|
|
Create,
|
|
|
|
Delete,
|
|
|
|
Subscribe,
|
|
|
|
Unsubscribe,
|
|
|
|
Rename(NewFolderName),
|
|
|
|
}
|
|
|
|
|
|
|
|
type NewFolderName = String;
|
|
|
|
|
2019-11-03 11:12:28 +00:00
|
|
|
pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
2019-10-24 17:30:17 +00:00
|
|
|
fn is_online(&self) -> bool;
|
2018-10-14 16:49:16 +00:00
|
|
|
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
|
2019-09-11 14:57:55 +00:00
|
|
|
fn watch(
|
|
|
|
&self,
|
|
|
|
sender: RefreshEventConsumer,
|
|
|
|
work_context: WorkContext,
|
|
|
|
) -> Result<std::thread::ThreadId>;
|
2019-04-14 14:24:01 +00:00
|
|
|
fn folders(&self) -> FnvHashMap<FolderHash, Folder>;
|
2019-11-03 11:12:28 +00:00
|
|
|
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp>;
|
2019-02-16 14:37:14 +00:00
|
|
|
|
2019-06-28 16:34:40 +00:00
|
|
|
fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()>;
|
2019-09-09 08:54:47 +00:00
|
|
|
fn folder_operation(&mut self, _path: &str, _op: FolderOperation) -> Result<()> {
|
2019-08-26 16:44:05 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2019-11-06 12:53:12 +00:00
|
|
|
|
|
|
|
fn as_any(&self) -> &dyn Any;
|
2017-09-28 15:06:35 +00:00
|
|
|
}
|
2017-09-14 15:08:14 +00:00
|
|
|
|
2017-09-16 11:14:08 +00:00
|
|
|
/// A `BackendOp` manages common operations for the various mail backends. They only live for the
|
2019-07-18 17:14:14 +00:00
|
|
|
/// duration of the operation. They are generated by the `operation` method of `Mailbackend` trait.
|
2017-09-16 10:32:56 +00:00
|
|
|
///
|
|
|
|
/// # Motivation
|
|
|
|
///
|
|
|
|
/// We need a way to do various operations on individual mails regardless of what backend they come
|
|
|
|
/// from (eg local or imap).
|
|
|
|
///
|
2019-03-26 13:27:02 +00:00
|
|
|
/// # Creation
|
|
|
|
/// ```no_run
|
|
|
|
/// /* Create operation from Backend */
|
|
|
|
///
|
|
|
|
/// let op = backend.operation(message.hash(), mailbox.folder.hash());
|
|
|
|
/// ```
|
|
|
|
///
|
2017-09-16 10:32:56 +00:00
|
|
|
/// # Example
|
|
|
|
/// ```
|
2019-07-18 17:14:14 +00:00
|
|
|
/// use melib::mailbox::backends::{BackendOp};
|
2017-10-01 14:31:20 +00:00
|
|
|
/// use melib::Result;
|
|
|
|
/// use melib::{Envelope, Flag};
|
2017-09-16 10:32:56 +00:00
|
|
|
///
|
|
|
|
/// #[derive(Debug)]
|
|
|
|
/// struct FooOp {}
|
|
|
|
///
|
|
|
|
/// impl BackendOp for FooOp {
|
|
|
|
/// fn description(&self) -> String {
|
|
|
|
/// "Foobar".to_string()
|
|
|
|
/// }
|
|
|
|
/// fn as_bytes(&mut self) -> Result<&[u8]> {
|
|
|
|
/// unimplemented!()
|
|
|
|
/// }
|
|
|
|
/// fn fetch_headers(&mut self) -> Result<&[u8]> {
|
|
|
|
/// unimplemented!()
|
|
|
|
/// }
|
|
|
|
/// fn fetch_body(&mut self) -> Result<&[u8]> {
|
|
|
|
/// unimplemented!()
|
|
|
|
/// }
|
2017-10-01 14:31:20 +00:00
|
|
|
/// fn fetch_flags(&self) -> Flag {
|
2017-09-16 16:15:51 +00:00
|
|
|
/// unimplemented!()
|
|
|
|
/// }
|
2017-09-16 10:32:56 +00:00
|
|
|
/// }
|
|
|
|
///
|
2019-07-18 17:14:14 +00:00
|
|
|
/// let operation = Box::new(FooOp {});
|
2017-09-16 10:32:56 +00:00
|
|
|
/// assert_eq!("Foobar", &operation.description());
|
|
|
|
/// ```
|
2017-09-14 15:08:14 +00:00
|
|
|
pub trait BackendOp: ::std::fmt::Debug + ::std::marker::Send {
|
|
|
|
fn description(&self) -> String;
|
|
|
|
fn as_bytes(&mut self) -> Result<&[u8]>;
|
|
|
|
//fn delete(&self) -> ();
|
|
|
|
//fn copy(&self
|
2017-09-14 15:29:23 +00:00
|
|
|
fn fetch_headers(&mut self) -> Result<&[u8]>;
|
|
|
|
fn fetch_body(&mut self) -> Result<&[u8]>;
|
2017-09-16 16:15:51 +00:00
|
|
|
fn fetch_flags(&self) -> Flag;
|
2019-04-04 11:21:52 +00:00
|
|
|
fn set_flag(&mut self, envelope: &mut Envelope, flag: Flag) -> Result<()>;
|
2017-09-14 15:08:14 +00:00
|
|
|
}
|
|
|
|
|
2019-07-18 17:16:51 +00:00
|
|
|
/// Wrapper for BackendOps that are to be set read-only.
|
|
|
|
///
|
|
|
|
/// Warning: Backend implementations may still cause side-effects (for example IMAP can set the
|
|
|
|
/// Seen flag when fetching an envelope)
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct ReadOnlyOp {
|
2019-09-09 08:54:47 +00:00
|
|
|
op: Box<dyn BackendOp>,
|
2019-07-18 17:16:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ReadOnlyOp {
|
2019-09-09 08:54:47 +00:00
|
|
|
pub fn new(op: Box<dyn BackendOp>) -> Box<dyn BackendOp> {
|
2019-07-18 17:16:51 +00:00
|
|
|
Box::new(ReadOnlyOp { op })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BackendOp for ReadOnlyOp {
|
|
|
|
fn description(&self) -> String {
|
|
|
|
format!("read-only: {}", self.op.description())
|
|
|
|
}
|
|
|
|
fn as_bytes(&mut self) -> Result<&[u8]> {
|
|
|
|
self.op.as_bytes()
|
|
|
|
}
|
|
|
|
fn fetch_headers(&mut self) -> Result<&[u8]> {
|
|
|
|
self.op.fetch_headers()
|
|
|
|
}
|
|
|
|
fn fetch_body(&mut self) -> Result<&[u8]> {
|
|
|
|
self.op.fetch_body()
|
|
|
|
}
|
|
|
|
fn fetch_flags(&self) -> Flag {
|
|
|
|
self.op.fetch_flags()
|
|
|
|
}
|
|
|
|
fn set_flag(&mut self, _envelope: &mut Envelope, _flag: Flag) -> Result<()> {
|
|
|
|
Err(MeliError::new("read-only set."))
|
2017-09-14 15:08:14 +00:00
|
|
|
}
|
|
|
|
}
|
2018-08-11 15:00:21 +00:00
|
|
|
|
2019-08-25 18:44:19 +00:00
|
|
|
#[derive(Debug, Copy, Hash, Eq, Clone, Serialize, Deserialize, PartialEq)]
|
2019-08-23 18:58:41 +00:00
|
|
|
pub enum SpecialUseMailbox {
|
|
|
|
Normal,
|
|
|
|
Inbox,
|
|
|
|
Archive,
|
|
|
|
Drafts,
|
|
|
|
Flagged,
|
|
|
|
Junk,
|
|
|
|
Sent,
|
|
|
|
Trash,
|
|
|
|
}
|
|
|
|
|
2018-08-11 15:00:21 +00:00
|
|
|
pub trait BackendFolder: Debug {
|
2018-09-05 13:08:11 +00:00
|
|
|
fn hash(&self) -> FolderHash;
|
2018-08-11 15:00:21 +00:00
|
|
|
fn name(&self) -> &str;
|
2019-08-23 18:32:32 +00:00
|
|
|
/// Path of folder within the mailbox hierarchy, with `/` as separator.
|
|
|
|
fn path(&self) -> &str;
|
2019-04-04 11:21:52 +00:00
|
|
|
fn change_name(&mut self, new_name: &str);
|
2018-08-11 15:00:21 +00:00
|
|
|
fn clone(&self) -> Folder;
|
2019-04-26 08:04:30 +00:00
|
|
|
fn children(&self) -> &Vec<FolderHash>;
|
|
|
|
fn parent(&self) -> Option<FolderHash>;
|
2018-08-11 15:00:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct DummyFolder {
|
2019-04-26 08:04:30 +00:00
|
|
|
v: Vec<FolderHash>,
|
2018-08-11 15:00:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BackendFolder for DummyFolder {
|
2018-09-05 13:08:11 +00:00
|
|
|
fn hash(&self) -> FolderHash {
|
2018-08-11 15:00:21 +00:00
|
|
|
0
|
|
|
|
}
|
2018-09-17 04:53:16 +00:00
|
|
|
|
2018-08-11 15:00:21 +00:00
|
|
|
fn name(&self) -> &str {
|
|
|
|
""
|
|
|
|
}
|
2018-09-17 04:53:16 +00:00
|
|
|
|
2019-08-23 18:32:32 +00:00
|
|
|
fn path(&self) -> &str {
|
|
|
|
""
|
|
|
|
}
|
|
|
|
|
2018-08-19 11:54:32 +00:00
|
|
|
fn change_name(&mut self, _s: &str) {}
|
2018-09-17 04:53:16 +00:00
|
|
|
|
2018-08-11 15:00:21 +00:00
|
|
|
fn clone(&self) -> Folder {
|
|
|
|
folder_default()
|
|
|
|
}
|
2018-09-17 04:53:16 +00:00
|
|
|
|
2019-04-26 08:04:30 +00:00
|
|
|
fn children(&self) -> &Vec<FolderHash> {
|
2018-08-11 15:00:21 +00:00
|
|
|
&self.v
|
|
|
|
}
|
2019-04-26 08:04:30 +00:00
|
|
|
|
|
|
|
fn parent(&self) -> Option<FolderHash> {
|
|
|
|
None
|
|
|
|
}
|
2018-08-11 15:00:21 +00:00
|
|
|
}
|
2018-09-17 04:53:16 +00:00
|
|
|
|
2018-08-11 15:00:21 +00:00
|
|
|
pub fn folder_default() -> Folder {
|
|
|
|
Box::new(DummyFolder {
|
|
|
|
v: Vec::with_capacity(0),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-09-05 13:08:11 +00:00
|
|
|
pub type FolderHash = u64;
|
2019-08-14 19:54:43 +00:00
|
|
|
pub type Folder = Box<dyn BackendFolder + Send + Sync>;
|
2018-09-23 16:55:29 +00:00
|
|
|
|
|
|
|
impl Clone for Folder {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
BackendFolder::clone(self.deref())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Folder {
|
|
|
|
fn default() -> Self {
|
|
|
|
folder_default()
|
|
|
|
}
|
|
|
|
}
|