You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

401 lines
10KB

  1. /*
  2. * meli - backends module
  3. *
  4. * Copyright 2017 Manos Pitsidianakis
  5. *
  6. * This file is part of meli.
  7. *
  8. * meli is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * meli is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with meli. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #[cfg(feature = "imap_backend")]
  22. pub mod imap;
  23. #[cfg(feature = "maildir_backend")]
  24. pub mod maildir;
  25. #[cfg(feature = "mbox_backend")]
  26. pub mod mbox;
  27. #[cfg(feature = "imap_backend")]
  28. pub use self::imap::ImapType;
  29. use crate::async_workers::*;
  30. use crate::conf::AccountSettings;
  31. use crate::error::{MeliError, Result};
  32. #[cfg(feature = "maildir_backend")]
  33. use self::maildir::MaildirType;
  34. #[cfg(feature = "mbox_backend")]
  35. use self::mbox::MboxType;
  36. use super::email::{Envelope, EnvelopeHash, Flag};
  37. use std::any::Any;
  38. use std::fmt;
  39. use std::fmt::Debug;
  40. use std::ops::Deref;
  41. use fnv::FnvHashMap;
  42. use std;
  43. pub type BackendCreator =
  44. Box<dyn Fn(&AccountSettings, Box<dyn Fn(&str) -> bool + Send + Sync>) -> Box<dyn MailBackend>>;
  45. /// A hashmap containing all available mail backends.
  46. /// An abstraction over any available backends.
  47. pub struct Backends {
  48. map: FnvHashMap<std::string::String, Box<dyn Fn() -> BackendCreator>>,
  49. }
  50. impl Default for Backends {
  51. fn default() -> Self {
  52. Backends::new()
  53. }
  54. }
  55. impl Backends {
  56. pub fn new() -> Self {
  57. let mut b = Backends {
  58. map: FnvHashMap::with_capacity_and_hasher(1, Default::default()),
  59. };
  60. #[cfg(feature = "maildir_backend")]
  61. {
  62. b.register(
  63. "maildir".to_string(),
  64. Box::new(|| Box::new(|f, i| Box::new(MaildirType::new(f, i)))),
  65. );
  66. }
  67. #[cfg(feature = "mbox_backend")]
  68. {
  69. b.register(
  70. "mbox".to_string(),
  71. Box::new(|| Box::new(|f, i| Box::new(MboxType::new(f, i)))),
  72. );
  73. }
  74. #[cfg(feature = "imap_backend")]
  75. {
  76. b.register(
  77. "imap".to_string(),
  78. Box::new(|| Box::new(|f, i| Box::new(ImapType::new(f, i)))),
  79. );
  80. }
  81. b
  82. }
  83. pub fn get(&self, key: &str) -> BackendCreator {
  84. if !self.map.contains_key(key) {
  85. panic!("{} is not a valid mail backend", key);
  86. }
  87. self.map[key]()
  88. }
  89. pub fn register(&mut self, key: String, backend: Box<dyn Fn() -> BackendCreator>) {
  90. if self.map.contains_key(&key) {
  91. panic!("{} is an already registered backend", key);
  92. }
  93. self.map.insert(key, backend);
  94. }
  95. }
  96. #[derive(Debug)]
  97. pub enum RefreshEventKind {
  98. Update(EnvelopeHash, Box<Envelope>),
  99. /// Rename(old_hash, new_hash)
  100. Rename(EnvelopeHash, EnvelopeHash),
  101. Create(Box<Envelope>),
  102. Remove(EnvelopeHash),
  103. Rescan,
  104. Failure(MeliError),
  105. }
  106. #[derive(Debug)]
  107. pub struct RefreshEvent {
  108. hash: FolderHash,
  109. kind: RefreshEventKind,
  110. }
  111. impl RefreshEvent {
  112. pub fn hash(&self) -> FolderHash {
  113. self.hash
  114. }
  115. pub fn kind(self) -> RefreshEventKind {
  116. /* consumes self! */
  117. self.kind
  118. }
  119. }
  120. /// A `RefreshEventConsumer` is a boxed closure that must be used to consume a `RefreshEvent` and
  121. /// send it to a UI provided channel. We need this level of abstraction to provide an interface for
  122. /// all users of mailbox refresh events.
  123. pub struct RefreshEventConsumer(Box<dyn Fn(RefreshEvent) -> () + Send + Sync>);
  124. impl RefreshEventConsumer {
  125. pub fn new(b: Box<dyn Fn(RefreshEvent) -> () + Send + Sync>) -> Self {
  126. RefreshEventConsumer(b)
  127. }
  128. pub fn send(&self, r: RefreshEvent) {
  129. self.0(r);
  130. }
  131. }
  132. pub struct NotifyFn(Box<dyn Fn(FolderHash) -> () + Send + Sync>);
  133. impl fmt::Debug for NotifyFn {
  134. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  135. write!(f, "NotifyFn Box")
  136. }
  137. }
  138. impl From<Box<dyn Fn(FolderHash) -> () + Send + Sync>> for NotifyFn {
  139. fn from(kind: Box<dyn Fn(FolderHash) -> () + Send + Sync>) -> Self {
  140. NotifyFn(kind)
  141. }
  142. }
  143. impl NotifyFn {
  144. pub fn new(b: Box<dyn Fn(FolderHash) -> () + Send + Sync>) -> Self {
  145. NotifyFn(b)
  146. }
  147. pub fn notify(&self, f: FolderHash) {
  148. self.0(f);
  149. }
  150. }
  151. #[derive(Debug, PartialEq, Eq, Hash, Clone)]
  152. pub enum FolderOperation {
  153. Create,
  154. Delete,
  155. Subscribe,
  156. Unsubscribe,
  157. Rename(NewFolderName),
  158. SetPermissions(FolderPermissions),
  159. }
  160. type NewFolderName = String;
  161. pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
  162. fn is_online(&self) -> bool;
  163. fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
  164. fn watch(
  165. &self,
  166. sender: RefreshEventConsumer,
  167. work_context: WorkContext,
  168. ) -> Result<std::thread::ThreadId>;
  169. fn folders(&self) -> FnvHashMap<FolderHash, Folder>;
  170. fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp>;
  171. fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()>;
  172. fn folder_operation(&mut self, _path: &str, _op: FolderOperation) -> Result<()> {
  173. Ok(())
  174. }
  175. fn as_any(&self) -> &dyn Any;
  176. }
  177. /// A `BackendOp` manages common operations for the various mail backends. They only live for the
  178. /// duration of the operation. They are generated by the `operation` method of `Mailbackend` trait.
  179. ///
  180. /// # Motivation
  181. ///
  182. /// We need a way to do various operations on individual mails regardless of what backend they come
  183. /// from (eg local or imap).
  184. ///
  185. /// # Creation
  186. /// ```no_run
  187. /// /* Create operation from Backend */
  188. ///
  189. /// let op = backend.operation(message.hash(), mailbox.folder.hash());
  190. /// ```
  191. ///
  192. /// # Example
  193. /// ```
  194. /// use melib::mailbox::backends::{BackendOp};
  195. /// use melib::Result;
  196. /// use melib::{Envelope, Flag};
  197. ///
  198. /// #[derive(Debug)]
  199. /// struct FooOp {}
  200. ///
  201. /// impl BackendOp for FooOp {
  202. /// fn description(&self) -> String {
  203. /// "Foobar".to_string()
  204. /// }
  205. /// fn as_bytes(&mut self) -> Result<&[u8]> {
  206. /// unimplemented!()
  207. /// }
  208. /// fn fetch_headers(&mut self) -> Result<&[u8]> {
  209. /// unimplemented!()
  210. /// }
  211. /// fn fetch_body(&mut self) -> Result<&[u8]> {
  212. /// unimplemented!()
  213. /// }
  214. /// fn fetch_flags(&self) -> Flag {
  215. /// unimplemented!()
  216. /// }
  217. /// }
  218. ///
  219. /// let operation = Box::new(FooOp {});
  220. /// assert_eq!("Foobar", &operation.description());
  221. /// ```
  222. pub trait BackendOp: ::std::fmt::Debug + ::std::marker::Send {
  223. fn description(&self) -> String;
  224. fn as_bytes(&mut self) -> Result<&[u8]>;
  225. //fn delete(&self) -> ();
  226. //fn copy(&self
  227. fn fetch_headers(&mut self) -> Result<&[u8]>;
  228. fn fetch_body(&mut self) -> Result<&[u8]>;
  229. fn fetch_flags(&self) -> Flag;
  230. fn set_flag(&mut self, envelope: &mut Envelope, flag: Flag, value: bool) -> Result<()>;
  231. }
  232. /// Wrapper for BackendOps that are to be set read-only.
  233. ///
  234. /// Warning: Backend implementations may still cause side-effects (for example IMAP can set the
  235. /// Seen flag when fetching an envelope)
  236. #[derive(Debug)]
  237. pub struct ReadOnlyOp {
  238. op: Box<dyn BackendOp>,
  239. }
  240. impl ReadOnlyOp {
  241. pub fn new(op: Box<dyn BackendOp>) -> Box<dyn BackendOp> {
  242. Box::new(ReadOnlyOp { op })
  243. }
  244. }
  245. impl BackendOp for ReadOnlyOp {
  246. fn description(&self) -> String {
  247. format!("read-only: {}", self.op.description())
  248. }
  249. fn as_bytes(&mut self) -> Result<&[u8]> {
  250. self.op.as_bytes()
  251. }
  252. fn fetch_headers(&mut self) -> Result<&[u8]> {
  253. self.op.fetch_headers()
  254. }
  255. fn fetch_body(&mut self) -> Result<&[u8]> {
  256. self.op.fetch_body()
  257. }
  258. fn fetch_flags(&self) -> Flag {
  259. self.op.fetch_flags()
  260. }
  261. fn set_flag(&mut self, _envelope: &mut Envelope, _flag: Flag, _value: bool) -> Result<()> {
  262. Err(MeliError::new("read-only set."))
  263. }
  264. }
  265. #[derive(Debug, Copy, Hash, Eq, Clone, Serialize, Deserialize, PartialEq)]
  266. pub enum SpecialUseMailbox {
  267. Normal,
  268. Inbox,
  269. Archive,
  270. Drafts,
  271. Flagged,
  272. Junk,
  273. Sent,
  274. Trash,
  275. }
  276. pub trait BackendFolder: Debug {
  277. fn hash(&self) -> FolderHash;
  278. fn name(&self) -> &str;
  279. /// Path of folder within the mailbox hierarchy, with `/` as separator.
  280. fn path(&self) -> &str;
  281. fn change_name(&mut self, new_name: &str);
  282. fn clone(&self) -> Folder;
  283. fn children(&self) -> &[FolderHash];
  284. fn parent(&self) -> Option<FolderHash>;
  285. fn permissions(&self) -> FolderPermissions;
  286. }
  287. #[derive(Debug)]
  288. struct DummyFolder {
  289. v: Vec<FolderHash>,
  290. }
  291. impl BackendFolder for DummyFolder {
  292. fn hash(&self) -> FolderHash {
  293. 0
  294. }
  295. fn name(&self) -> &str {
  296. ""
  297. }
  298. fn path(&self) -> &str {
  299. ""
  300. }
  301. fn change_name(&mut self, _s: &str) {}
  302. fn clone(&self) -> Folder {
  303. folder_default()
  304. }
  305. fn children(&self) -> &[FolderHash] {
  306. &self.v
  307. }
  308. fn parent(&self) -> Option<FolderHash> {
  309. None
  310. }
  311. fn permissions(&self) -> FolderPermissions {
  312. FolderPermissions::default()
  313. }
  314. }
  315. pub fn folder_default() -> Folder {
  316. Box::new(DummyFolder {
  317. v: Vec::with_capacity(0),
  318. })
  319. }
  320. pub type FolderHash = u64;
  321. pub type Folder = Box<dyn BackendFolder + Send + Sync>;
  322. impl Clone for Folder {
  323. fn clone(&self) -> Self {
  324. BackendFolder::clone(self.deref())
  325. }
  326. }
  327. impl Default for Folder {
  328. fn default() -> Self {
  329. folder_default()
  330. }
  331. }
  332. #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
  333. pub struct FolderPermissions {
  334. pub create_messages: bool,
  335. pub remove_messages: bool,
  336. pub set_flags: bool,
  337. pub create_child: bool,
  338. pub rename_messages: bool,
  339. pub delete_messages: bool,
  340. pub delete_mailbox: bool,
  341. pub change_permissions: bool,
  342. }
  343. impl Default for FolderPermissions {
  344. fn default() -> Self {
  345. FolderPermissions {
  346. create_messages: false,
  347. remove_messages: false,
  348. set_flags: false,
  349. create_child: false,
  350. rename_messages: false,
  351. delete_messages: false,
  352. delete_mailbox: false,
  353. change_permissions: false,
  354. }
  355. }
  356. }